bump winit, vulkano, ...

ecs
q3k 2020-03-17 00:00:50 +01:00
parent 14ae060a30
commit 2ae07a4679
1751 changed files with 178073 additions and 142552 deletions

View File

@ -4,11 +4,12 @@ use cgmath as cgm;
use winit::{
dpi::LogicalSize,
Window,
WindowEvent,
WindowBuilder,
EventsLoop,
Event,
window::Window,
window::WindowBuilder,
event_loop::EventLoop,
event::Event,
event::WindowEvent,
platform::desktop::EventLoopExtDesktop,
};
use vulkano_win::VkSurfaceBuild;
use vulkano::instance as vi;
@ -21,8 +22,8 @@ const WIDTH: u32 = 800;
const HEIGHT: u32 = 600;
pub struct Renderer {
instance: vulkan::Instance<winit::Window>,
events_loop: EventsLoop,
instance: vulkan::Instance<Window>,
events_loop: EventLoop<()>,
}
impl Renderer {
@ -37,11 +38,11 @@ impl Renderer {
}
}
fn init_window(instance: Arc<vi::Instance>) -> (EventsLoop, Arc<vs::Surface<Window>>) {
let events_loop = EventsLoop::new();
fn init_window(instance: Arc<vi::Instance>) -> (EventLoop<()>, Arc<vs::Surface<Window>>) {
let events_loop = EventLoop::new();
let surface = WindowBuilder::new()
.with_title("abrasion")
.with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT)))
.with_inner_size(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT)))
.build_vk_surface(&events_loop, instance.clone())
.expect("could not create surface");
(events_loop, surface)
@ -53,10 +54,12 @@ impl Renderer {
pub fn poll_close(&mut self) -> bool {
let mut close = false;
self.events_loop.poll_events(|ev| {
// TODO(q3k): migrate to EventLoop::run
self.events_loop.run_return(|ev, _, control_flow| {
if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev {
close = true;
}
*control_flow = winit::event_loop::ControlFlow::Exit;
});
return close;
}

View File

@ -1,6 +1,6 @@
use cgmath as cgm;
#[derive(Copy, Clone)]
#[derive(Default, Copy, Clone)]
pub struct Vertex {
pos: [f32; 3],
color: [f32; 3],
@ -15,6 +15,7 @@ impl Vertex {
}
vulkano::impl_vertex!(Vertex, pos, color);
#[derive(Default, Copy, Clone)]
pub struct Instance {
model: [f32; 16],
}

View File

@ -25,7 +25,7 @@ const VERSION: vi::Version = vi::Version { major: 1, minor: 0, patch: 0};
fn required_instance_extensions() -> vi::InstanceExtensions {
let mut exts = vulkano_win::required_extensions();
exts.ext_debug_report = true;
exts.ext_debug_utils = true;
exts
}
@ -242,7 +242,8 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
}
let chain = self.swapchain_binding().chain.clone();
let (image_index, acquire_future) = match vs::acquire_next_image(chain.clone(), None) {
// TODO(q3k): check the 'suboptimal' (second) bool
let (image_index, _, acquire_future) = match vs::acquire_next_image(chain.clone(), None) {
Ok(r) => r,
Err(vs::AcquireError::OutOfDate) => {
self.armed = false;
@ -286,14 +287,12 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
}
fn init_debug_callback(instance: &Arc<vi::Instance>) -> vi::debug::DebugCallback {
let mt = vi::debug::MessageTypes {
error: true,
warning: true,
performance_warning: true,
information: true,
debug: true,
let mt = vi::debug::MessageType {
general: true,
validation: true,
performance: true,
};
vi::debug::DebugCallback::new(&instance, mt, |msg| {
vi::debug::DebugCallback::new(&instance, vi::debug::MessageSeverity::errors_and_warnings(), mt, |msg| {
log::debug!("validation layer: {:?}", msg.description);
}).expect("could not create debug callback")
}

View File

@ -25,7 +25,7 @@ pub trait Pipeline {
pub struct Forward {
pipeline: Arc<VulkanoPipeline>,
descriptor_set_pool: vdd::FixedSizeDescriptorSetsPool<Arc<VulkanoPipeline>>,
descriptor_set_pool: vdd::FixedSizeDescriptorSetsPool,
}
impl Forward {
@ -122,7 +122,8 @@ impl Forward {
.unwrap())
as Arc<VulkanoPipeline>;
let descriptor_set_pool = vdd::FixedSizeDescriptorSetsPool::new(pipeline.clone(), 0);
let layout = pipeline.descriptor_set_layout(0).unwrap();
let descriptor_set_pool = vdd::FixedSizeDescriptorSetsPool::new(layout.clone());
Forward {
pipeline,

View File

@ -49,21 +49,41 @@ impl<WT: 'static + Send + Sync> SwapchainBinding<WT> {
Some(p) => Some(p.chain.clone()),
};
let (chain, images) = vs::Swapchain::new(
surface_binding.device.clone(),
surface_binding.surface.clone(),
image_count,
surface_format.0,
extent,
1,
image_usage,
sharing,
capabilities.current_transform,
vs::CompositeAlpha::Opaque,
present_mode,
true,
prev.as_ref(),
).expect("could not create swap chain");
let (chain, images) = match prev.as_ref() {
None => vs::Swapchain::new(
surface_binding.device.clone(),
surface_binding.surface.clone(),
image_count,
surface_format.0,
extent,
1,
image_usage,
sharing,
capabilities.current_transform,
vs::CompositeAlpha::Opaque,
present_mode,
vs::FullscreenExclusive::Default,
true,
vs::ColorSpace::SrgbNonLinear,
),
Some(p) => vs::Swapchain::with_old_swapchain(
surface_binding.device.clone(),
surface_binding.surface.clone(),
image_count,
surface_format.0,
extent,
1,
image_usage,
sharing,
capabilities.current_transform,
vs::CompositeAlpha::Opaque,
present_mode,
vs::FullscreenExclusive::Default,
true,
vs::ColorSpace::SrgbNonLinear,
p.clone(),
),
}.expect("could not create swap chain");
log::info!("Swap chain: present mode {:?}, {} images", present_mode, images.len());

View File

@ -26,13 +26,13 @@ alias(
)
alias(
name = "vulkano",
actual = "//third_party/cargo/vendor/vulkano-0.11.1:vulkano",
actual = "//third_party/cargo/vendor/vulkano-0.18.0:vulkano",
)
alias(
name = "vulkano_win",
actual = "//third_party/cargo/vendor/vulkano-win-0.11.1:vulkano_win",
actual = "//third_party/cargo/vendor/vulkano-win-0.18.0:vulkano_win",
)
alias(
name = "winit",
actual = "//third_party/cargo/vendor/winit-0.18.1:winit",
actual = "//third_party/cargo/vendor/winit-0.22.0:winit",
)

571
third_party/cargo/Cargo.lock generated vendored
View File

@ -38,15 +38,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "arrayvec"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
dependencies = [
"nodrop",
]
[[package]]
name = "arrayvec"
version = "0.5.1"
@ -61,7 +52,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
"winapi 0.3.8",
]
[[package]]
@ -76,28 +67,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "backtrace"
version = "0.3.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8"
dependencies = [
"backtrace-sys",
"cfg-if",
"libc",
"rustc-demangle",
]
[[package]]
name = "backtrace-sys"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca797db0057bae1a7aa2eef3283a874695455cecf08a43bfb8507ee0ebc1ed69"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "bitflags"
version = "1.2.1"
@ -116,6 +85,17 @@ version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "calloop"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7aa2097be53a00de9e8fc349fea6d76221f398f5c4fa550d420669906962d160"
dependencies = [
"mio",
"mio-extras",
"nix",
]
[[package]]
name = "cc"
version = "1.0.50"
@ -136,7 +116,7 @@ checksum = "283944cdecc44bf0b8dd010ec9af888d3b4f142844fdbe026c20ef68148d6fe7"
dependencies = [
"approx",
"num-traits",
"rand 0.6.5",
"rand",
]
[[package]]
@ -159,14 +139,29 @@ dependencies = [
[[package]]
name = "cocoa"
version = "0.18.5"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1706996401131526e36b3b49f0c4d912639ce110996f3ca144d78946727bce54"
checksum = "f29f7768b2d1be17b96158e3285951d366b40211320fb30826a76cb7a0da6400"
dependencies = [
"bitflags",
"block",
"core-foundation",
"core-graphics",
"core-foundation 0.6.4",
"core-graphics 0.17.3",
"foreign-types",
"libc",
"objc",
]
[[package]]
name = "cocoa"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a4736c86d51bd878b474400d9ec888156f4037015f5d09794fab9f26eab1ad4"
dependencies = [
"bitflags",
"block",
"core-foundation 0.7.0",
"core-graphics 0.19.0",
"foreign-types",
"libc",
"objc",
@ -191,7 +186,17 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
dependencies = [
"core-foundation-sys",
"core-foundation-sys 0.6.2",
"libc",
]
[[package]]
name = "core-foundation"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
dependencies = [
"core-foundation-sys 0.7.0",
"libc",
]
@ -201,6 +206,12 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
[[package]]
name = "core-foundation-sys"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
[[package]]
name = "core-graphics"
version = "0.17.3"
@ -208,84 +219,113 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9"
dependencies = [
"bitflags",
"core-foundation",
"core-foundation 0.6.4",
"foreign-types",
"libc",
]
[[package]]
name = "crossbeam"
version = "0.5.0"
name = "core-graphics"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1c92ff2d7a202d592f5a412d75cf421495c913817781c1cb383bf12a77e185f"
checksum = "59e78b2e0aaf43f08e7ae0d6bc96895ef72ff0921c7d4ff4762201b2dba376dd"
dependencies = [
"bitflags",
"core-foundation 0.7.0",
"foreign-types",
"libc",
]
[[package]]
name = "core-video-sys"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dc065219542086f72d1e9f7aadbbab0989e980263695d129d502082d063a9d0"
dependencies = [
"cfg-if",
"core-foundation-sys 0.6.2",
"core-graphics 0.17.3",
"libc",
"objc",
]
[[package]]
name = "crossbeam"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
dependencies = [
"cfg-if",
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch 0.6.1",
"crossbeam-epoch",
"crossbeam-queue",
"crossbeam-utils",
"lazy_static",
"num_cpus",
"parking_lot 0.6.4",
]
[[package]]
name = "crossbeam-channel"
version = "0.3.9"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa"
checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061"
dependencies = [
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-deque"
version = "0.6.3"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13"
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
dependencies = [
"crossbeam-epoch 0.7.2",
"crossbeam-epoch",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-epoch"
version = "0.6.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2449aaa4ec7ef96e5fb24db16024b935df718e9ae1cec0a1e68feeca2efca7b8"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"arrayvec 0.4.12",
"autocfg 1.0.0",
"cfg-if",
"crossbeam-utils",
"lazy_static",
"memoffset 0.2.1",
"scopeguard 0.3.3",
"maybe-uninit",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-epoch"
version = "0.7.2"
name = "crossbeam-queue"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9"
checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
dependencies = [
"arrayvec 0.4.12",
"cfg-if",
"crossbeam-utils",
"lazy_static",
"memoffset 0.5.4",
"scopeguard 1.1.0",
]
[[package]]
name = "crossbeam-utils"
version = "0.6.6"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg 1.0.0",
"cfg-if",
"lazy_static",
]
[[package]]
name = "dispatch"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
name = "dlib"
version = "0.4.1"
@ -341,6 +381,22 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
dependencies = [
"bitflags",
"fuchsia-zircon-sys",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "half"
version = "1.5.0"
@ -365,12 +421,43 @@ dependencies = [
"quick-error",
]
[[package]]
name = "instant"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c346c299e3fe8ef94dc10c2c0253d858a69aac1245157a3bf4125915d528caf"
[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
dependencies = [
"libc",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
[[package]]
name = "libc"
version = "0.2.67"
@ -384,7 +471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753"
dependencies = [
"cc",
"winapi",
"winapi 0.3.8",
]
[[package]]
@ -398,12 +485,11 @@ dependencies = [
[[package]]
name = "lock_api"
version = "0.1.5"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b"
dependencies = [
"owning_ref",
"scopeguard 0.3.3",
"scopeguard",
]
[[package]]
@ -443,15 +529,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
dependencies = [
"libc",
"winapi",
"winapi 0.3.8",
]
[[package]]
name = "memoffset"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
[[package]]
name = "memoffset"
version = "0.5.4"
@ -463,20 +543,71 @@ dependencies = [
[[package]]
name = "metal"
version = "0.13.1"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7de9c2b83c946ab01c9942928388f911d93486b97636d9927541345905fea65d"
checksum = "f83c7dcc2038e12f68493fa3de44235df27b2497178e257185b4b5b5d028a1e4"
dependencies = [
"bitflags",
"block",
"cocoa",
"core-graphics",
"cocoa 0.19.1",
"core-graphics 0.17.3",
"foreign-types",
"libc",
"log",
"objc",
"objc-foundation",
"objc_id",
]
[[package]]
name = "mio"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f"
dependencies = [
"cfg-if",
"fuchsia-zircon",
"fuchsia-zircon-sys",
"iovec",
"kernel32-sys",
"libc",
"log",
"miow",
"net2",
"slab",
"winapi 0.2.8",
]
[[package]]
name = "mio-extras"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
dependencies = [
"lazycell",
"log",
"mio",
"slab",
]
[[package]]
name = "miow"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
dependencies = [
"kernel32-sys",
"net2",
"winapi 0.2.8",
"ws2_32-sys",
]
[[package]]
name = "net2"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
dependencies = [
"cfg-if",
"libc",
"winapi 0.3.8",
]
[[package]]
@ -492,12 +623,6 @@ dependencies = [
"void",
]
[[package]]
name = "nodrop"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "num-traits"
version = "0.2.11"
@ -507,16 +632,6 @@ dependencies = [
"autocfg 1.0.0",
]
[[package]]
name = "num_cpus"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "objc"
version = "0.2.7"
@ -527,17 +642,6 @@ dependencies = [
"objc_exception",
]
[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
"block",
"objc",
"objc_id",
]
[[package]]
name = "objc_exception"
version = "0.1.2"
@ -547,15 +651,6 @@ dependencies = [
"cc",
]
[[package]]
name = "objc_id"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
dependencies = [
"objc",
]
[[package]]
name = "openvr"
version = "0.6.0"
@ -584,66 +679,35 @@ dependencies = [
"num-traits",
]
[[package]]
name = "owning_ref"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce"
dependencies = [
"stable_deref_trait",
]
[[package]]
name = "parking_lot"
version = "0.6.4"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5"
checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc"
dependencies = [
"lock_api",
"parking_lot_core 0.3.1",
]
[[package]]
name = "parking_lot"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337"
dependencies = [
"lock_api",
"parking_lot_core 0.4.0",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.3.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c"
checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1"
dependencies = [
"cfg-if",
"cloudabi",
"libc",
"rand 0.5.6",
"rustc_version",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "parking_lot_core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
dependencies = [
"libc",
"rand 0.6.5",
"rustc_version",
"smallvec",
"winapi",
"winapi 0.3.8",
]
[[package]]
name = "percent-encoding"
version = "1.0.1"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pkg-config"
@ -675,19 +739,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
dependencies = [
"cloudabi",
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"winapi",
]
[[package]]
name = "rand"
version = "0.6.5"
@ -704,7 +755,7 @@ dependencies = [
"rand_os",
"rand_pcg",
"rand_xorshift",
"winapi",
"winapi 0.3.8",
]
[[package]]
@ -758,7 +809,7 @@ checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
dependencies = [
"libc",
"rand_core 0.4.2",
"winapi",
"winapi 0.3.8",
]
[[package]]
@ -772,7 +823,7 @@ dependencies = [
"libc",
"rand_core 0.4.2",
"rdrand",
"winapi",
"winapi 0.3.8",
]
[[package]]
@ -794,6 +845,15 @@ dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "raw-window-handle"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211"
dependencies = [
"libc",
]
[[package]]
name = "rdrand"
version = "0.4.0"
@ -803,6 +863,12 @@ dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "redox_syscall"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
[[package]]
name = "regex"
version = "1.3.5"
@ -821,21 +887,6 @@ version = "0.6.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "rusttype"
version = "0.7.9"
@ -852,7 +903,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14a911032fb5791ccbeec9f28fdcb9bf0983b81f227bafdfd227c658d0731c8a"
dependencies = [
"approx",
"arrayvec 0.5.1",
"arrayvec",
"ordered-float",
"stb_truetype",
]
@ -866,33 +917,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "shared_library"
version = "0.1.9"
@ -904,19 +934,22 @@ dependencies = [
]
[[package]]
name = "smallvec"
version = "0.6.13"
name = "slab"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
dependencies = [
"maybe-uninit",
]
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "smallvec"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
[[package]]
name = "smithay-client-toolkit"
version = "0.4.6"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa"
checksum = "421c8dc7acf5cb205b88160f8b4cc2c5cfabe210e43b2f80f009f4c1ef910f1d"
dependencies = [
"andrew",
"bitflags",
@ -925,16 +958,9 @@ dependencies = [
"memmap",
"nix",
"wayland-client",
"wayland-commons",
"wayland-protocols",
]
[[package]]
name = "stable_deref_trait"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
[[package]]
name = "stb_truetype"
version = "0.3.1"
@ -970,9 +996,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
[[package]]
name = "vk-sys"
version = "0.4.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36f5fd4a7d6d5d19808610583131c0aed271556527cad4cb71c436831a28e059"
checksum = "c24c107c0402856ad434d2dc0f3dbdc68c9e170e8bab0f27aa82a282d234d57c"
[[package]]
name = "void"
@ -982,9 +1008,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "vulkano"
version = "0.11.1"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715b3528d50119f2b7088479a79ff9d95416a6613d18c853d87ad98185ca43d1"
checksum = "1eb8a191577df4f71d70c31e5b69c56be2f3e88a87cbd6ad6e40ae06b38dc542"
dependencies = [
"crossbeam",
"fnv",
@ -997,11 +1023,11 @@ dependencies = [
[[package]]
name = "vulkano-win"
version = "0.11.1"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07fffe247f37c8b051e8210ecfb6fe9a13bbb69e05ea61aceb1def5f709320ae"
checksum = "bc7e4c74bbf33d0c6b9f7e98f47068c62dc65d6a0721c0cb4b05fe4776c9af80"
dependencies = [
"cocoa",
"cocoa 0.20.0",
"metal",
"objc",
"vulkano",
@ -1015,19 +1041,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
dependencies = [
"same-file",
"winapi",
"winapi 0.3.8",
"winapi-util",
]
[[package]]
name = "wayland-client"
version = "0.21.13"
version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49963e5f9eeaf637bfcd1b9f0701c99fd5cd05225eb51035550d4272806f2713"
checksum = "af1080ebe0efabcf12aef2132152f616038f2d7dcbbccf7b2d8c5270fe14bcda"
dependencies = [
"bitflags",
"calloop",
"downcast-rs",
"libc",
"mio",
"nix",
"wayland-commons",
"wayland-scanner",
@ -1036,9 +1064,9 @@ dependencies = [
[[package]]
name = "wayland-commons"
version = "0.21.13"
version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c08896768b667e1df195d88a62a53a2d1351a1ed96188be79c196b35bb32ec"
checksum = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb"
dependencies = [
"nix",
"wayland-sys",
@ -1046,22 +1074,21 @@ dependencies = [
[[package]]
name = "wayland-protocols"
version = "0.21.13"
version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4afde2ea2a428eee6d7d2c8584fdbe8b82eee8b6c353e129a434cd6e07f42145"
checksum = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9"
dependencies = [
"bitflags",
"wayland-client",
"wayland-commons",
"wayland-scanner",
"wayland-sys",
]
[[package]]
name = "wayland-scanner"
version = "0.21.13"
version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3828c568714507315ee425a9529edc4a4aa9901409e373e9e0027e7622b79e"
checksum = "93b02247366f395b9258054f964fe293ddd019c3237afba9be2ccbe9e1651c3d"
dependencies = [
"proc-macro2",
"quote",
@ -1070,14 +1097,20 @@ dependencies = [
[[package]]
name = "wayland-sys"
version = "0.21.13"
version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520ab0fd578017a0ee2206623ba9ef4afe5e8f23ca7b42f6acfba2f4e66b1628"
checksum = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4"
dependencies = [
"dlib",
"lazy_static",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.8"
@ -1088,6 +1121,12 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
@ -1100,7 +1139,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80"
dependencies = [
"winapi",
"winapi 0.3.8",
]
[[package]]
@ -1111,27 +1150,43 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "winit"
version = "0.18.1"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c57c15bd4c0ef18dff33e263e452abe32d00e2e05771cacaa410a14cc1c0776"
checksum = "02e9092b71b48ad6a0d98835a786308d10760cc09369d02e4a166608327f1f26"
dependencies = [
"android_glue",
"backtrace",
"cocoa",
"core-foundation",
"core-graphics",
"bitflags",
"cocoa 0.19.1",
"core-foundation 0.6.4",
"core-graphics 0.17.3",
"core-video-sys",
"dispatch",
"instant",
"lazy_static",
"libc",
"log",
"mio",
"mio-extras",
"objc",
"parking_lot 0.7.1",
"parking_lot",
"percent-encoding",
"raw-window-handle",
"smithay-client-toolkit",
"wayland-client",
"winapi",
"winapi 0.3.8",
"x11-dl",
]
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "x11-dl"
version = "2.18.5"

View File

@ -9,9 +9,9 @@ path = "fake_lib.rs"
[dependencies]
env_logger = "0.6.1"
log = "0.4.6"
vulkano = "0.11.1"
vulkano-win = "0.11.1"
winit = "0.18.0"
vulkano = "0.18.0"
vulkano-win = "0.18.0"
winit = "0.22.0"
cgmath = "0.17.0"
openvr = "0.6.0"
@ -23,13 +23,13 @@ target = "x86_64-unknown-linux-gnu"
skipped_deps = ['rusttype-0.7.9']
additional_deps = ['//third_party/cargo/vendor/rusttype-0.8.2:rusttype']
[raze.crates.x11-dl.'2.18.4']
[raze.crates.x11-dl.'2.18.5']
gen_buildrs = true
[raze.crates.wayland-client.'0.21.13']
[raze.crates.wayland-client.'0.23.6']
gen_buildrs = true
[raze.crates.wayland-protocols.'0.21.13']
[raze.crates.wayland-protocols.'0.23.6']
gen_buildrs = true
[raze.crates.log.'0.4.8']

View File

@ -1 +0,0 @@
{"files":{"Cargo.toml":"adb58f911fb48a1a3828bda514c2da5ce5650e8e98eb7217f014b1720fe11d38","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0245ee104228a100ce5fceecf43e25faae450494d9173f43fd94c27d69fdac13","README.rst":"a3cff166a7b622233cd9ccfbe472f910267629f4b41e387ad8573fc06eac37d6","benches/arraystring.rs":"f12b890977117ebde4ca42bcd6b91f2a6a087f2b235aaca6d15e30d125ae9f67","benches/extend.rs":"8c8f78df7e90b62c7e160cf5ea6c61b90bc4035a9704b6a179a1e01d8fafe2e9","build.rs":"fc29930f06cb4dde58f43d2f30b28c366ca3bafcd7e44b41a1c250d60fa900fb","custom.css":"e6f2cd299392337b4e2959c52f422e5b7be11920ea98d10db44d10ddef5ed47c","src/array.rs":"67fb063ee515bfd4968ede219dff81091a5935ef93529ebd1bb2a716ea3ed3d3","src/array_string.rs":"8a1a4cfc1699e2373815e57dc676a87a30629f91a9e861c866ccc6cb1381eadf","src/char.rs":"64a08f6a743b67bf2c96483f91c2fdaea79f6e91df5cd752f770b16a6b1d5b1e","src/errors.rs":"dde99bffaddfd45396aab7e07642cc018ef5435fe60c4f26a2c05a36555be18c","src/lib.rs":"34167f35d9a5b887e6fb424500bb64764d68d029d0e374827886b05ad4d26bca","src/maybe_uninit.rs":"7cca39ffe0f122716baaa174b433ff5fe9c93560f8e54fc077a0083500eaa1dd","src/maybe_uninit_nodrop.rs":"7fb2e24bf815dd6e1d104056fa9be4a11de7e0f0e5474742af186c580a6b47cc","src/maybe_uninit_stable.rs":"3f7daba622cf5df86992b451b46636a491c9611292f59969eb6890a10a00476d","src/range.rs":"65744ab7def208a1ab155ea2448fe9ea7fc14f33211361b1041f540125b32efd","tests/serde.rs":"ef3986a82656b09f3fbb14358e767051ffabe09592c61e69ea695cb88760e8ba","tests/tests.rs":"8066a4aca7b40356525ed87f7658773e610ef4fce3522b0cc0f301384d880f00"},"package":"cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"}

View File

@ -1,62 +0,0 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "arrayvec"
version = "0.4.12"
authors = ["bluss"]
description = "A vector with fixed capacity, backed by an array (it can be stored on the stack too). Implements fixed capacity ArrayVec and ArrayString."
documentation = "https://docs.rs/arrayvec/"
keywords = ["stack", "vector", "array", "data-structure", "no_std"]
categories = ["data-structures", "no-std"]
license = "MIT/Apache-2.0"
repository = "https://github.com/bluss/arrayvec"
[package.metadata.docs.rs]
features = ["serde-1"]
[package.metadata.release]
no-dev-version = true
tag-name = "{{version}}"
[[bench]]
name = "extend"
harness = false
[[bench]]
name = "arraystring"
harness = false
[dependencies.nodrop]
version = "0.1.12"
default-features = false
[dependencies.serde]
version = "1.0"
optional = true
default-features = false
[dev-dependencies.bencher]
version = "0.1.4"
[dev-dependencies.matches]
version = "0.1"
[dev-dependencies.serde_test]
version = "1.0"
[build-dependencies]
[features]
array-sizes-129-255 = []
array-sizes-33-128 = []
default = ["std"]
serde-1 = ["serde"]
std = []
use_union = []

View File

@ -1,224 +0,0 @@
arrayvec
========
A vector with fixed capacity.
Please read the `API documentation here`__
__ https://docs.rs/arrayvec
|build_status|_ |crates|_ |crates2|_
.. |build_status| image:: https://travis-ci.org/bluss/arrayvec.svg
.. _build_status: https://travis-ci.org/bluss/arrayvec
.. |crates| image:: http://meritbadge.herokuapp.com/arrayvec
.. _crates: https://crates.io/crates/arrayvec
.. |crates2| image:: http://meritbadge.herokuapp.com/nodrop
.. _crates2: https://crates.io/crates/nodrop
Recent Changes (arrayvec)
-------------------------
- 0.4.12
- Use raw pointers instead of ``get_unchecked_mut`` where the target may be
uninitialized a everywhere relevant in the ArrayVec implementation.
- 0.4.11
- In Rust 1.36 or later, use newly stable MaybeUninit. This extends the
soundness work introduced in 0.4.9, we are finally able to use this in
stable. We use feature detection (build script) to enable this at build
time.
- 0.4.10
- Use ``repr(C)`` in the ``union`` version that was introduced in 0.4.9, to
allay some soundness concerns.
- 0.4.9
- Use ``union`` in the implementation on when this is detected to be supported
(nightly only for now). This is a better solution for treating uninitialized
regions correctly, and we'll use it in stable Rust as soon as we are able.
When this is enabled, the ``ArrayVec`` has no space overhead in its memory
layout, although the size of the vec should not be relied upon. (See `#114`_)
- ``ArrayString`` updated to not use uninitialized memory, it instead zeros its
backing array. This will be refined in the next version, since we
need to make changes to the user visible API.
- The ``use_union`` feature now does nothing (like its documentation foretold).
.. _`#114`: https://github.com/bluss/arrayvec/pull/114
- 0.4.8
- Implement Clone and Debug for ``IntoIter`` by @clarcharr
- Add more array sizes under crate features. These cover all in the range
up to 128 and 129 to 255 respectively (we have a few of those by default):
- ``array-size-33-128``
- ``array-size-129-255``
- 0.4.7
- Fix future compat warning about raw pointer casts
- Use ``drop_in_place`` when dropping the arrayvec by-value iterator
- Decrease mininum Rust version (see docs) by @jeehoonkang
- 0.3.25
- Fix future compat warning about raw pointer casts
- 0.4.6
- Fix compilation on 16-bit targets. This means, the 65536 array size is not
included on these targets.
- 0.3.24
- Fix compilation on 16-bit targets. This means, the 65536 array size is not
included on these targets.
- Fix license files so that they are both included (was fixed in 0.4 before)
- 0.4.5
- Add methods to ``ArrayString`` by @DenialAdams:
- ``.pop() -> Option<char>``
- ``.truncate(new_len)``
- ``.remove(index) -> char``
- Remove dependency on crate odds
- Document debug assertions in unsafe methods better
- 0.4.4
- Add method ``ArrayVec::truncate()`` by @niklasf
- 0.4.3
- Improve performance for ``ArrayVec::extend`` with a lower level
implementation (#74)
- Small cleanup in dependencies (use no std for crates where we don't need more)
- 0.4.2
- Add constructor method ``new`` to ``CapacityError``.
- 0.4.1
- Add ``Default`` impl to ``ArrayString`` by @tbu-
- 0.4.0
- Reformed signatures and error handling by @bluss and @tbu-:
- ``ArrayVec``'s ``push, insert, remove, swap_remove`` now match ``Vec``'s
corresponding signature and panic on capacity errors where applicable.
- Add fallible methods ``try_push, insert`` and checked methods
``pop_at, swap_pop``.
- Similar changes to ``ArrayString``'s push methods.
- Use a local version of the ``RangeArgument`` trait
- Add array sizes 50, 150, 200 by @daboross
- Support serde 1.0 by @daboross
- New method ``.push_unchecked()`` by @niklasf
- ``ArrayString`` implements ``PartialOrd, Ord`` by @tbu-
- Require Rust 1.14
- crate feature ``use_generic_array`` was dropped.
- 0.3.23
- Implement ``PartialOrd, Ord`` as well as ``PartialOrd<str>`` for
``ArrayString``.
- 0.3.22
- Implement ``Array`` for the 65536 size
- 0.3.21
- Use ``encode_utf8`` from crate odds
- Add constructor ``ArrayString::from_byte_string``
- 0.3.20
- Simplify and speed up ``ArrayString``s ``.push(char)``-
- 0.3.19
- Add new crate feature ``use_generic_array`` which allows using their
``GenericArray`` just like a regular fixed size array for the storage
of an ``ArrayVec``.
- 0.3.18
- Fix bounds check in ``ArrayVec::insert``!
It would be buggy if ``self.len() < index < self.capacity()``. Take note of
the push out behavior specified in the docs.
- 0.3.17
- Added crate feature ``use_union`` which forwards to the nodrop crate feature
- Added methods ``.is_full()`` to ``ArrayVec`` and ``ArrayString``.
- 0.3.16
- Added method ``.retain()`` to ``ArrayVec``.
- Added methods ``.as_slice(), .as_mut_slice()`` to ``ArrayVec`` and ``.as_str()``
to ``ArrayString``.
- 0.3.15
- Add feature std, which you can opt out of to use ``no_std`` (requires Rust 1.6
to opt out).
- Implement ``Clone::clone_from`` for ArrayVec and ArrayString
- 0.3.14
- Add ``ArrayString::from(&str)``
- 0.3.13
- Added ``DerefMut`` impl for ``ArrayString``.
- Added method ``.simplify()`` to drop the element for ``CapacityError``.
- Added method ``.dispose()`` to ``ArrayVec``
- 0.3.12
- Added ArrayString, a fixed capacity analogy of String
- 0.3.11
- Added trait impls Default, PartialOrd, Ord, Write for ArrayVec
- 0.3.10
- Go back to using external NoDrop, fixing a panic safety bug (issue #3)
- 0.3.8
- Inline the non-dropping logic to remove one drop flag in the
ArrayVec representation.
- 0.3.7
- Added method .into_inner()
- Added unsafe method .set_len()
License
=======
Dual-licensed to be compatible with the Rust project.
Licensed under the Apache License, Version 2.0
http://www.apache.org/licenses/LICENSE-2.0 or the MIT license
http://opensource.org/licenses/MIT, at your
option. This file may not be copied, modified, or distributed
except according to those terms.

View File

@ -1,90 +0,0 @@
extern crate arrayvec;
#[macro_use] extern crate bencher;
use arrayvec::ArrayString;
use bencher::Bencher;
fn try_push_c(b: &mut Bencher) {
let mut v = ArrayString::<[u8; 512]>::new();
b.iter(|| {
v.clear();
while v.try_push('c').is_ok() {
}
v.len()
});
b.bytes = v.capacity() as u64;
}
fn try_push_alpha(b: &mut Bencher) {
let mut v = ArrayString::<[u8; 512]>::new();
b.iter(|| {
v.clear();
while v.try_push('α').is_ok() {
}
v.len()
});
b.bytes = v.capacity() as u64;
}
// Yes, pushing a string char-by-char is slow. Use .push_str.
fn try_push_string(b: &mut Bencher) {
let mut v = ArrayString::<[u8; 512]>::new();
let input = "abcαβγ“”";
b.iter(|| {
v.clear();
for ch in input.chars().cycle() {
if !v.try_push(ch).is_ok() {
break;
}
}
v.len()
});
b.bytes = v.capacity() as u64;
}
fn push_c(b: &mut Bencher) {
let mut v = ArrayString::<[u8; 512]>::new();
b.iter(|| {
v.clear();
while !v.is_full() {
v.push('c');
}
v.len()
});
b.bytes = v.capacity() as u64;
}
fn push_alpha(b: &mut Bencher) {
let mut v = ArrayString::<[u8; 512]>::new();
b.iter(|| {
v.clear();
while !v.is_full() {
v.push('α');
}
v.len()
});
b.bytes = v.capacity() as u64;
}
fn push_string(b: &mut Bencher) {
let mut v = ArrayString::<[u8; 512]>::new();
let input = "abcαβγ“”";
b.iter(|| {
v.clear();
for ch in input.chars().cycle() {
if !v.is_full() {
v.push(ch);
} else {
break;
}
}
v.len()
});
b.bytes = v.capacity() as u64;
}
benchmark_group!(benches, try_push_c, try_push_alpha, try_push_string, push_c,
push_alpha, push_string);
benchmark_main!(benches);

View File

@ -1,43 +0,0 @@
extern crate arrayvec;
#[macro_use] extern crate bencher;
use arrayvec::ArrayVec;
use bencher::Bencher;
fn extend_with_constant(b: &mut Bencher) {
let mut v = ArrayVec::<[u8; 512]>::new();
let cap = v.capacity();
b.iter(|| {
v.clear();
v.extend((0..cap).map(|_| 1));
v[0]
});
b.bytes = v.capacity() as u64;
}
fn extend_with_range(b: &mut Bencher) {
let mut v = ArrayVec::<[u8; 512]>::new();
let cap = v.capacity();
b.iter(|| {
v.clear();
v.extend((0..cap).map(|x| x as _));
v[0]
});
b.bytes = v.capacity() as u64;
}
fn extend_with_slice(b: &mut Bencher) {
let mut v = ArrayVec::<[u8; 512]>::new();
let data = [1; 512];
b.iter(|| {
v.clear();
v.extend(data.iter().cloned());
v[0]
});
b.bytes = v.capacity() as u64;
}
benchmark_group!(benches, extend_with_constant, extend_with_range, extend_with_slice);
benchmark_main!(benches);

View File

@ -1,90 +0,0 @@
use std::env;
use std::io::Write;
use std::process::{Command, Stdio};
fn main() {
// we need to output *some* file to opt out of the default
println!("cargo:rerun-if-changed=build.rs");
detect_maybe_uninit();
}
fn detect_maybe_uninit() {
let has_stable_maybe_uninit = probe(&stable_maybe_uninit());
if has_stable_maybe_uninit {
println!("cargo:rustc-cfg=has_stable_maybe_uninit");
return;
}
let has_unstable_union_with_md = probe(&maybe_uninit_code(true));
if has_unstable_union_with_md {
println!("cargo:rustc-cfg=has_manually_drop_in_union");
println!("cargo:rustc-cfg=has_union_feature");
}
}
// To guard against changes in this currently unstable feature, use
// a detection tests instead of a Rustc version and/or date test.
fn stable_maybe_uninit() -> String {
let code = "
#![allow(warnings)]
use std::mem::MaybeUninit;
fn main() { }
";
code.to_string()
}
// To guard against changes in this currently unstable feature, use
// a detection tests instead of a Rustc version and/or date test.
fn maybe_uninit_code(use_feature: bool) -> String {
let feature = if use_feature { "#![feature(untagged_unions)]" } else { "" };
let code = "
#![allow(warnings)]
use std::mem::ManuallyDrop;
#[derive(Copy)]
pub union MaybeUninit<T> {
empty: (),
value: ManuallyDrop<T>,
}
impl<T> Clone for MaybeUninit<T> where T: Copy
{
fn clone(&self) -> Self { *self }
}
fn main() {
let value1 = MaybeUninit::<[i32; 3]> { empty: () };
let value2 = MaybeUninit { value: ManuallyDrop::new([1, 2, 3]) };
}
";
[feature, code].concat()
}
/// Test if a code snippet can be compiled
fn probe(code: &str) -> bool {
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
let out_dir = env::var_os("OUT_DIR").expect("environment variable OUT_DIR");
let mut child = Command::new(rustc)
.arg("--out-dir")
.arg(out_dir)
.arg("--emit=obj")
.arg("-")
.stdin(Stdio::piped())
.spawn()
.expect("rustc probe");
child
.stdin
.as_mut()
.expect("rustc stdin")
.write_all(code.as_bytes())
.expect("write rustc stdin");
child.wait().expect("rustc probe").success()
}

View File

@ -1,25 +0,0 @@
.docblock pre.rust { background: #eeeeff; }
pre.trait, pre.fn, pre.struct, pre.enum, pre.typedef { background: #fcfefc; }
/* Small “example” label for doc examples */
.docblock pre.rust::before {
content: "example";
float: right;
font-style: italic;
font-size: 0.8em;
margin-top: -10px;
margin-right: -5px;
}
/* Fixup where display in trait listing */
pre.trait .where::before {
content: '\a ';
}
.docblock code {
background-color: inherit;
font-weight: bold;
padding: 0 0.1em;
}

View File

@ -1,137 +0,0 @@
/// Trait for fixed size arrays.
///
/// This trait is implemented for some specific array sizes, see
/// the implementor list below. At the current state of Rust we can't
/// make this fully general for every array size.
///
/// The following crate features add more array sizes (and they are not
/// enabled by default due to their impact on compliation speed).
///
/// - `array-sizes-33-128`: All sizes 33 to 128 are implemented
/// (a few in this range are included by default).
/// - `array-sizes-129-255`: All sizes 129 to 255 are implemented
/// (a few in this range are included by default).
pub unsafe trait Array {
/// The arrays element type
type Item;
#[doc(hidden)]
/// The smallest index type that indexes the array.
type Index: Index;
#[doc(hidden)]
fn as_ptr(&self) -> *const Self::Item;
#[doc(hidden)]
fn as_mut_ptr(&mut self) -> *mut Self::Item;
#[doc(hidden)]
fn capacity() -> usize;
}
pub trait Index : PartialEq + Copy {
fn to_usize(self) -> usize;
fn from(usize) -> Self;
}
use std::slice::{from_raw_parts};
pub trait ArrayExt : Array {
#[inline(always)]
fn as_slice(&self) -> &[Self::Item] {
unsafe {
from_raw_parts(self.as_ptr(), Self::capacity())
}
}
}
impl<A> ArrayExt for A where A: Array { }
impl Index for u8 {
#[inline(always)]
fn to_usize(self) -> usize { self as usize }
#[inline(always)]
fn from(ix: usize) -> Self { ix as u8 }
}
impl Index for u16 {
#[inline(always)]
fn to_usize(self) -> usize { self as usize }
#[inline(always)]
fn from(ix: usize) -> Self { ix as u16 }
}
impl Index for u32 {
#[inline(always)]
fn to_usize(self) -> usize { self as usize }
#[inline(always)]
fn from(ix: usize) -> Self { ix as u32 }
}
impl Index for usize {
#[inline(always)]
fn to_usize(self) -> usize { self }
#[inline(always)]
fn from(ix: usize) -> Self { ix }
}
macro_rules! fix_array_impl {
($index_type:ty, $len:expr ) => (
unsafe impl<T> Array for [T; $len] {
type Item = T;
type Index = $index_type;
#[doc(hidden)]
#[inline(always)]
fn as_ptr(&self) -> *const T { self as *const _ as *const _ }
#[doc(hidden)]
#[inline(always)]
fn as_mut_ptr(&mut self) -> *mut T { self as *mut _ as *mut _}
#[doc(hidden)]
#[inline(always)]
fn capacity() -> usize { $len }
}
)
}
macro_rules! fix_array_impl_recursive {
($index_type:ty, ) => ();
($index_type:ty, $($len:expr,)*) => (
$(fix_array_impl!($index_type, $len);)*
);
}
fix_array_impl_recursive!(u8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, );
#[cfg(not(feature="array-sizes-33-128"))]
fix_array_impl_recursive!(u8, 32, 40, 48, 50, 56, 64, 72, 96, 100, 128, );
#[cfg(feature="array-sizes-33-128")]
fix_array_impl_recursive!(u8,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
125, 126, 127, 128,
);
#[cfg(not(feature="array-sizes-129-255"))]
fix_array_impl_recursive!(u8, 160, 192, 200, 224,);
#[cfg(feature="array-sizes-129-255")]
fix_array_impl_recursive!(u8,
129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236,
237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
253, 254, 255,
);
fix_array_impl_recursive!(u16, 256, 384, 512, 768, 1024, 2048, 4096, 8192, 16384, 32768,);
// This array size doesn't exist on 16-bit
#[cfg(any(target_pointer_width="32", target_pointer_width="64"))]
fix_array_impl_recursive!(u32, 1 << 16,);

View File

@ -1,516 +0,0 @@
use std::borrow::Borrow;
use std::cmp;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ptr;
use std::ops::{Deref, DerefMut};
use std::str;
use std::str::Utf8Error;
use std::slice;
use array::{Array, ArrayExt};
use array::Index;
use CapacityError;
use char::encode_utf8;
#[cfg(feature="serde-1")]
use serde::{Serialize, Deserialize, Serializer, Deserializer};
/// A string with a fixed capacity.
///
/// The `ArrayString` is a string backed by a fixed size array. It keeps track
/// of its length.
///
/// The string is a contiguous value that you can store directly on the stack
/// if needed.
#[derive(Copy)]
pub struct ArrayString<A: Array<Item=u8>> {
// FIXME: Use Copyable union for xs when we can
xs: A,
len: A::Index,
}
impl<A: Array<Item=u8>> Default for ArrayString<A> {
/// Return an empty `ArrayString`
fn default() -> ArrayString<A> {
ArrayString::new()
}
}
impl<A: Array<Item=u8>> ArrayString<A> {
/// Create a new empty `ArrayString`.
///
/// Capacity is inferred from the type parameter.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let mut string = ArrayString::<[_; 16]>::new();
/// string.push_str("foo");
/// assert_eq!(&string[..], "foo");
/// assert_eq!(string.capacity(), 16);
/// ```
pub fn new() -> ArrayString<A> {
unsafe {
ArrayString {
// FIXME: Use Copyable union for xs when we can
xs: mem::zeroed(),
len: Index::from(0),
}
}
}
/// Create a new `ArrayString` from a `str`.
///
/// Capacity is inferred from the type parameter.
///
/// **Errors** if the backing array is not large enough to fit the string.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let mut string = ArrayString::<[_; 3]>::from("foo").unwrap();
/// assert_eq!(&string[..], "foo");
/// assert_eq!(string.len(), 3);
/// assert_eq!(string.capacity(), 3);
/// ```
pub fn from(s: &str) -> Result<Self, CapacityError<&str>> {
let mut arraystr = Self::new();
arraystr.try_push_str(s)?;
Ok(arraystr)
}
/// Create a new `ArrayString` from a byte string literal.
///
/// **Errors** if the byte string literal is not valid UTF-8.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let string = ArrayString::from_byte_string(b"hello world").unwrap();
/// ```
pub fn from_byte_string(b: &A) -> Result<Self, Utf8Error> {
let mut arraystr = Self::new();
let s = try!(str::from_utf8(b.as_slice()));
let _result = arraystr.try_push_str(s);
debug_assert!(_result.is_ok());
Ok(arraystr)
}
/// Return the capacity of the `ArrayString`.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let string = ArrayString::<[_; 3]>::new();
/// assert_eq!(string.capacity(), 3);
/// ```
#[inline]
pub fn capacity(&self) -> usize { A::capacity() }
/// Return if the `ArrayString` is completely filled.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let mut string = ArrayString::<[_; 1]>::new();
/// assert!(!string.is_full());
/// string.push_str("A");
/// assert!(string.is_full());
/// ```
pub fn is_full(&self) -> bool { self.len() == self.capacity() }
/// Adds the given char to the end of the string.
///
/// ***Panics*** if the backing array is not large enough to fit the additional char.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let mut string = ArrayString::<[_; 2]>::new();
///
/// string.push('a');
/// string.push('b');
///
/// assert_eq!(&string[..], "ab");
/// ```
pub fn push(&mut self, c: char) {
self.try_push(c).unwrap();
}
/// Adds the given char to the end of the string.
///
/// Returns `Ok` if the push succeeds.
///
/// **Errors** if the backing array is not large enough to fit the additional char.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let mut string = ArrayString::<[_; 2]>::new();
///
/// string.try_push('a').unwrap();
/// string.try_push('b').unwrap();
/// let overflow = string.try_push('c');
///
/// assert_eq!(&string[..], "ab");
/// assert_eq!(overflow.unwrap_err().element(), 'c');
/// ```
pub fn try_push(&mut self, c: char) -> Result<(), CapacityError<char>> {
let len = self.len();
unsafe {
match encode_utf8(c, &mut self.raw_mut_bytes()[len..]) {
Ok(n) => {
self.set_len(len + n);
Ok(())
}
Err(_) => Err(CapacityError::new(c)),
}
}
}
/// Adds the given string slice to the end of the string.
///
/// ***Panics*** if the backing array is not large enough to fit the string.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let mut string = ArrayString::<[_; 2]>::new();
///
/// string.push_str("a");
/// string.push_str("d");
///
/// assert_eq!(&string[..], "ad");
/// ```
pub fn push_str(&mut self, s: &str) {
self.try_push_str(s).unwrap()
}
/// Adds the given string slice to the end of the string.
///
/// Returns `Ok` if the push succeeds.
///
/// **Errors** if the backing array is not large enough to fit the string.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let mut string = ArrayString::<[_; 2]>::new();
///
/// string.try_push_str("a").unwrap();
/// let overflow1 = string.try_push_str("bc");
/// string.try_push_str("d").unwrap();
/// let overflow2 = string.try_push_str("ef");
///
/// assert_eq!(&string[..], "ad");
/// assert_eq!(overflow1.unwrap_err().element(), "bc");
/// assert_eq!(overflow2.unwrap_err().element(), "ef");
/// ```
pub fn try_push_str<'a>(&mut self, s: &'a str) -> Result<(), CapacityError<&'a str>> {
if s.len() > self.capacity() - self.len() {
return Err(CapacityError::new(s));
}
unsafe {
let dst = self.xs.as_mut_ptr().offset(self.len() as isize);
let src = s.as_ptr();
ptr::copy_nonoverlapping(src, dst, s.len());
let newl = self.len() + s.len();
self.set_len(newl);
}
Ok(())
}
/// Removes the last character from the string and returns it.
///
/// Returns `None` if this `ArrayString` is empty.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let mut s = ArrayString::<[_; 3]>::from("foo").unwrap();
///
/// assert_eq!(s.pop(), Some('o'));
/// assert_eq!(s.pop(), Some('o'));
/// assert_eq!(s.pop(), Some('f'));
///
/// assert_eq!(s.pop(), None);
/// ```
#[inline]
pub fn pop(&mut self) -> Option<char> {
let ch = match self.chars().rev().next() {
Some(ch) => ch,
None => return None,
};
let new_len = self.len() - ch.len_utf8();
unsafe {
self.set_len(new_len);
}
Some(ch)
}
/// Shortens this `ArrayString` to the specified length.
///
/// If `new_len` is greater than the strings current length, this has no
/// effect.
///
/// ***Panics*** if `new_len` does not lie on a `char` boundary.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let mut string = ArrayString::<[_; 6]>::from("foobar").unwrap();
/// string.truncate(3);
/// assert_eq!(&string[..], "foo");
/// string.truncate(4);
/// assert_eq!(&string[..], "foo");
/// ```
#[inline]
pub fn truncate(&mut self, new_len: usize) {
if new_len <= self.len() {
assert!(self.is_char_boundary(new_len));
unsafe {
// In libstd truncate is called on the underlying vector,
// which in turns drops each element.
// As we know we don't have to worry about Drop,
// we can just set the length (a la clear.)
self.set_len(new_len);
}
}
}
/// Removes a `char` from this `ArrayString` at a byte position and returns it.
///
/// This is an `O(n)` operation, as it requires copying every element in the
/// array.
///
/// ***Panics*** if `idx` is larger than or equal to the `ArrayString`s length,
/// or if it does not lie on a `char` boundary.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let mut s = ArrayString::<[_; 3]>::from("foo").unwrap();
///
/// assert_eq!(s.remove(0), 'f');
/// assert_eq!(s.remove(1), 'o');
/// assert_eq!(s.remove(0), 'o');
/// ```
#[inline]
pub fn remove(&mut self, idx: usize) -> char {
let ch = match self[idx..].chars().next() {
Some(ch) => ch,
None => panic!("cannot remove a char from the end of a string"),
};
let next = idx + ch.len_utf8();
let len = self.len();
unsafe {
ptr::copy(self.xs.as_ptr().offset(next as isize),
self.xs.as_mut_ptr().offset(idx as isize),
len - next);
self.set_len(len - (next - idx));
}
ch
}
/// Make the string empty.
pub fn clear(&mut self) {
unsafe {
self.set_len(0);
}
}
/// Set the stringss length.
///
/// This function is `unsafe` because it changes the notion of the
/// number of “valid” bytes in the string. Use with care.
///
/// This method uses *debug assertions* to check the validity of `length`
/// and may use other debug assertions.
#[inline]
pub unsafe fn set_len(&mut self, length: usize) {
debug_assert!(length <= self.capacity());
self.len = Index::from(length);
}
/// Return a string slice of the whole `ArrayString`.
pub fn as_str(&self) -> &str {
self
}
/// Return a mutable slice of the whole strings buffer
unsafe fn raw_mut_bytes(&mut self) -> &mut [u8] {
slice::from_raw_parts_mut(self.xs.as_mut_ptr(), self.capacity())
}
}
impl<A: Array<Item=u8>> Deref for ArrayString<A> {
type Target = str;
#[inline]
fn deref(&self) -> &str {
unsafe {
let sl = slice::from_raw_parts(self.xs.as_ptr(), self.len.to_usize());
str::from_utf8_unchecked(sl)
}
}
}
impl<A: Array<Item=u8>> DerefMut for ArrayString<A> {
#[inline]
fn deref_mut(&mut self) -> &mut str {
unsafe {
let sl = slice::from_raw_parts_mut(self.xs.as_mut_ptr(), self.len.to_usize());
// FIXME: Nothing but transmute to do this right now
mem::transmute(sl)
}
}
}
impl<A: Array<Item=u8>> PartialEq for ArrayString<A> {
fn eq(&self, rhs: &Self) -> bool {
**self == **rhs
}
}
impl<A: Array<Item=u8>> PartialEq<str> for ArrayString<A> {
fn eq(&self, rhs: &str) -> bool {
&**self == rhs
}
}
impl<A: Array<Item=u8>> PartialEq<ArrayString<A>> for str {
fn eq(&self, rhs: &ArrayString<A>) -> bool {
self == &**rhs
}
}
impl<A: Array<Item=u8>> Eq for ArrayString<A> { }
impl<A: Array<Item=u8>> Hash for ArrayString<A> {
fn hash<H: Hasher>(&self, h: &mut H) {
(**self).hash(h)
}
}
impl<A: Array<Item=u8>> Borrow<str> for ArrayString<A> {
fn borrow(&self) -> &str { self }
}
impl<A: Array<Item=u8>> AsRef<str> for ArrayString<A> {
fn as_ref(&self) -> &str { self }
}
impl<A: Array<Item=u8>> fmt::Debug for ArrayString<A> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) }
}
impl<A: Array<Item=u8>> fmt::Display for ArrayString<A> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) }
}
/// `Write` appends written data to the end of the string.
impl<A: Array<Item=u8>> fmt::Write for ArrayString<A> {
fn write_char(&mut self, c: char) -> fmt::Result {
self.try_push(c).map_err(|_| fmt::Error)
}
fn write_str(&mut self, s: &str) -> fmt::Result {
self.try_push_str(s).map_err(|_| fmt::Error)
}
}
impl<A: Array<Item=u8> + Copy> Clone for ArrayString<A> {
fn clone(&self) -> ArrayString<A> {
*self
}
fn clone_from(&mut self, rhs: &Self) {
// guaranteed to fit due to types matching.
self.clear();
self.try_push_str(rhs).ok();
}
}
impl<A: Array<Item=u8>> PartialOrd for ArrayString<A> {
fn partial_cmp(&self, rhs: &Self) -> Option<cmp::Ordering> {
(**self).partial_cmp(&**rhs)
}
fn lt(&self, rhs: &Self) -> bool { **self < **rhs }
fn le(&self, rhs: &Self) -> bool { **self <= **rhs }
fn gt(&self, rhs: &Self) -> bool { **self > **rhs }
fn ge(&self, rhs: &Self) -> bool { **self >= **rhs }
}
impl<A: Array<Item=u8>> PartialOrd<str> for ArrayString<A> {
fn partial_cmp(&self, rhs: &str) -> Option<cmp::Ordering> {
(**self).partial_cmp(rhs)
}
fn lt(&self, rhs: &str) -> bool { &**self < rhs }
fn le(&self, rhs: &str) -> bool { &**self <= rhs }
fn gt(&self, rhs: &str) -> bool { &**self > rhs }
fn ge(&self, rhs: &str) -> bool { &**self >= rhs }
}
impl<A: Array<Item=u8>> PartialOrd<ArrayString<A>> for str {
fn partial_cmp(&self, rhs: &ArrayString<A>) -> Option<cmp::Ordering> {
self.partial_cmp(&**rhs)
}
fn lt(&self, rhs: &ArrayString<A>) -> bool { self < &**rhs }
fn le(&self, rhs: &ArrayString<A>) -> bool { self <= &**rhs }
fn gt(&self, rhs: &ArrayString<A>) -> bool { self > &**rhs }
fn ge(&self, rhs: &ArrayString<A>) -> bool { self >= &**rhs }
}
impl<A: Array<Item=u8>> Ord for ArrayString<A> {
fn cmp(&self, rhs: &Self) -> cmp::Ordering {
(**self).cmp(&**rhs)
}
}
#[cfg(feature="serde-1")]
/// Requires crate feature `"serde-1"`
impl<A: Array<Item=u8>> Serialize for ArrayString<A> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
serializer.serialize_str(&*self)
}
}
#[cfg(feature="serde-1")]
/// Requires crate feature `"serde-1"`
impl<'de, A: Array<Item=u8>> Deserialize<'de> for ArrayString<A> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>
{
use serde::de::{self, Visitor};
use std::marker::PhantomData;
struct ArrayStringVisitor<A: Array<Item=u8>>(PhantomData<A>);
impl<'de, A: Array<Item=u8>> Visitor<'de> for ArrayStringVisitor<A> {
type Value = ArrayString<A>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a string no more than {} bytes long", A::capacity())
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where E: de::Error,
{
ArrayString::from(v).map_err(|_| E::invalid_length(v.len(), &self))
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where E: de::Error,
{
let s = try!(str::from_utf8(v).map_err(|_| E::invalid_value(de::Unexpected::Bytes(v), &self)));
ArrayString::from(s).map_err(|_| E::invalid_length(s.len(), &self))
}
}
deserializer.deserialize_str(ArrayStringVisitor::<A>(PhantomData))
}
}

View File

@ -1,54 +0,0 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// Original authors: alexchrichton, bluss
// UTF-8 ranges and tags for encoding characters
const TAG_CONT: u8 = 0b1000_0000;
const TAG_TWO_B: u8 = 0b1100_0000;
const TAG_THREE_B: u8 = 0b1110_0000;
const TAG_FOUR_B: u8 = 0b1111_0000;
const MAX_ONE_B: u32 = 0x80;
const MAX_TWO_B: u32 = 0x800;
const MAX_THREE_B: u32 = 0x10000;
/// Placeholder
pub struct EncodeUtf8Error;
/// Encode a char into buf using UTF-8.
///
/// On success, return the byte length of the encoding (1, 2, 3 or 4).<br>
/// On error, return `EncodeUtf8Error` if the buffer was too short for the char.
#[inline]
pub fn encode_utf8(ch: char, buf: &mut [u8]) -> Result<usize, EncodeUtf8Error>
{
let code = ch as u32;
if code < MAX_ONE_B && buf.len() >= 1 {
buf[0] = code as u8;
return Ok(1);
} else if code < MAX_TWO_B && buf.len() >= 2 {
buf[0] = (code >> 6 & 0x1F) as u8 | TAG_TWO_B;
buf[1] = (code & 0x3F) as u8 | TAG_CONT;
return Ok(2);
} else if code < MAX_THREE_B && buf.len() >= 3 {
buf[0] = (code >> 12 & 0x0F) as u8 | TAG_THREE_B;
buf[1] = (code >> 6 & 0x3F) as u8 | TAG_CONT;
buf[2] = (code & 0x3F) as u8 | TAG_CONT;
return Ok(3);
} else if buf.len() >= 4 {
buf[0] = (code >> 18 & 0x07) as u8 | TAG_FOUR_B;
buf[1] = (code >> 12 & 0x3F) as u8 | TAG_CONT;
buf[2] = (code >> 6 & 0x3F) as u8 | TAG_CONT;
buf[3] = (code & 0x3F) as u8 | TAG_CONT;
return Ok(4);
};
Err(EncodeUtf8Error)
}

View File

@ -1,53 +0,0 @@
use std::fmt;
#[cfg(feature="std")]
use std::any::Any;
#[cfg(feature="std")]
use std::error::Error;
/// Error value indicating insufficient capacity
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
pub struct CapacityError<T = ()> {
element: T,
}
impl<T> CapacityError<T> {
/// Create a new `CapacityError` from `element`.
pub fn new(element: T) -> CapacityError<T> {
CapacityError {
element: element,
}
}
/// Extract the overflowing element
pub fn element(self) -> T {
self.element
}
/// Convert into a `CapacityError` that does not carry an element.
pub fn simplify(self) -> CapacityError {
CapacityError { element: () }
}
}
const CAPERROR: &'static str = "insufficient capacity";
#[cfg(feature="std")]
/// Requires `features="std"`.
impl<T: Any> Error for CapacityError<T> {
fn description(&self) -> &str {
CAPERROR
}
}
impl<T> fmt::Display for CapacityError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", CAPERROR)
}
}
impl<T> fmt::Debug for CapacityError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {}", "CapacityError", CAPERROR)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,46 +0,0 @@
use array::Array;
use std::mem::ManuallyDrop;
/// A combination of ManuallyDrop and “maybe uninitialized”;
/// this wraps a value that can be wholly or partially uninitialized;
/// it also has no drop regardless of the type of T.
#[repr(C)] // for cast from self ptr to value
pub union MaybeUninit<T> {
empty: (),
value: ManuallyDrop<T>,
}
// Why we don't use std's MaybeUninit on nightly? See the ptr method
impl<T> MaybeUninit<T> {
/// Create a new MaybeUninit with uninitialized interior
pub unsafe fn uninitialized() -> Self {
MaybeUninit { empty: () }
}
/// Create a new MaybeUninit from the value `v`.
pub fn from(v: T) -> Self {
MaybeUninit { value: ManuallyDrop::new(v) }
}
// Raw pointer casts written so that we don't reference or access the
// uninitialized interior value
/// Return a raw pointer to the start of the interior array
pub fn ptr(&self) -> *const T::Item
where T: Array
{
// std MaybeUninit creates a &self.value reference here which is
// not guaranteed to be sound in our case - we will partially
// initialize the value, not always wholly.
self as *const _ as *const T::Item
}
/// Return a mut raw pointer to the start of the interior array
pub fn ptr_mut(&mut self) -> *mut T::Item
where T: Array
{
self as *mut _ as *mut T::Item
}
}

View File

@ -1,41 +0,0 @@
use array::Array;
use nodrop::NoDrop;
use std::mem::uninitialized;
/// A combination of NoDrop and “maybe uninitialized”;
/// this wraps a value that can be wholly or partially uninitialized.
///
/// NOTE: This is known to not be a good solution, but it's the one we have kept
/// working on stable Rust. Stable improvements are encouraged, in any form,
/// but of course we are waiting for a real, stable, MaybeUninit.
pub struct MaybeUninit<T>(NoDrop<T>);
// why don't we use ManuallyDrop here: It doesn't inhibit
// enum layout optimizations that depend on T, and we support older Rust.
impl<T> MaybeUninit<T> {
/// Create a new MaybeUninit with uninitialized interior
pub unsafe fn uninitialized() -> Self {
Self::from(uninitialized())
}
/// Create a new MaybeUninit from the value `v`.
pub fn from(v: T) -> Self {
MaybeUninit(NoDrop::new(v))
}
/// Return a raw pointer to the start of the interior array
pub fn ptr(&self) -> *const T::Item
where T: Array
{
&*self.0 as *const T as *const _
}
/// Return a mut raw pointer to the start of the interior array
pub fn ptr_mut(&mut self) -> *mut T::Item
where T: Array
{
&mut *self.0 as *mut T as *mut _
}
}

View File

@ -1,40 +0,0 @@
use array::Array;
use std::mem::MaybeUninit as StdMaybeUninit;
pub struct MaybeUninit<T> {
inner: StdMaybeUninit<T>,
}
impl<T> MaybeUninit<T> {
/// Create a new MaybeUninit with uninitialized interior
pub unsafe fn uninitialized() -> Self {
MaybeUninit { inner: StdMaybeUninit::uninit() }
}
/// Create a new MaybeUninit from the value `v`.
pub fn from(v: T) -> Self {
MaybeUninit { inner: StdMaybeUninit::new(v) }
}
// Raw pointer casts written so that we don't reference or access the
// uninitialized interior value
/// Return a raw pointer to the start of the interior array
pub fn ptr(&self) -> *const T::Item
where T: Array
{
// std MaybeUninit creates a &self.value reference here which is
// not guaranteed to be sound in our case - we will partially
// initialize the value, not always wholly.
self.inner.as_ptr() as *const T::Item
}
/// Return a mut raw pointer to the start of the interior array
pub fn ptr_mut(&mut self) -> *mut T::Item
where T: Array
{
self.inner.as_mut_ptr() as *mut T::Item
}
}

View File

@ -1,42 +0,0 @@
use std::ops::{
RangeFull,
RangeFrom,
RangeTo,
Range,
};
/// `RangeArgument` is implemented by Rust's built-in range types, produced
/// by range syntax like `..`, `a..`, `..b` or `c..d`.
///
/// Note: This is arrayvec's provisional trait, waiting for stable Rust to
/// provide an equivalent.
pub trait RangeArgument {
#[inline]
/// Start index (inclusive)
fn start(&self) -> Option<usize> { None }
#[inline]
/// End index (exclusive)
fn end(&self) -> Option<usize> { None }
}
impl RangeArgument for RangeFull {}
impl RangeArgument for RangeFrom<usize> {
#[inline]
fn start(&self) -> Option<usize> { Some(self.start) }
}
impl RangeArgument for RangeTo<usize> {
#[inline]
fn end(&self) -> Option<usize> { Some(self.end) }
}
impl RangeArgument for Range<usize> {
#[inline]
fn start(&self) -> Option<usize> { Some(self.start) }
#[inline]
fn end(&self) -> Option<usize> { Some(self.end) }
}

View File

@ -1,79 +0,0 @@
#![cfg(feature = "serde-1")]
extern crate arrayvec;
extern crate serde_test;
mod array_vec {
use arrayvec::ArrayVec;
use serde_test::{Token, assert_tokens, assert_de_tokens_error};
#[test]
fn test_ser_de_empty() {
let vec = ArrayVec::<[u32; 0]>::new();
assert_tokens(&vec, &[
Token::Seq { len: Some(0) },
Token::SeqEnd,
]);
}
#[test]
fn test_ser_de() {
let mut vec = ArrayVec::<[u32; 3]>::new();
vec.push(20);
vec.push(55);
vec.push(123);
assert_tokens(&vec, &[
Token::Seq { len: Some(3) },
Token::U32(20),
Token::U32(55),
Token::U32(123),
Token::SeqEnd,
]);
}
#[test]
fn test_de_too_large() {
assert_de_tokens_error::<ArrayVec<[u32; 2]>>(&[
Token::Seq { len: Some(3) },
Token::U32(13),
Token::U32(42),
Token::U32(68),
], "invalid length 3, expected an array with no more than 2 items");
}
}
mod array_string {
use arrayvec::ArrayString;
use serde_test::{Token, assert_tokens, assert_de_tokens_error};
#[test]
fn test_ser_de_empty() {
let string = ArrayString::<[u8; 0]>::new();
assert_tokens(&string, &[
Token::Str(""),
]);
}
#[test]
fn test_ser_de() {
let string = ArrayString::<[u8; 9]>::from("1234 abcd")
.expect("expected exact specified capacity to be enough");
assert_tokens(&string, &[
Token::Str("1234 abcd"),
]);
}
#[test]
fn test_de_too_large() {
assert_de_tokens_error::<ArrayString<[u8; 2]>>(&[
Token::Str("afd")
], "invalid length 3, expected a string no more than 2 bytes long");
}
}

View File

@ -1,517 +0,0 @@
extern crate arrayvec;
#[macro_use] extern crate matches;
use arrayvec::ArrayVec;
use arrayvec::ArrayString;
use std::mem;
use arrayvec::CapacityError;
use std::collections::HashMap;
#[test]
fn test_simple() {
use std::ops::Add;
let mut vec: ArrayVec<[Vec<i32>; 3]> = ArrayVec::new();
vec.push(vec![1, 2, 3, 4]);
vec.push(vec![10]);
vec.push(vec![-1, 13, -2]);
for elt in &vec {
assert_eq!(elt.iter().fold(0, Add::add), 10);
}
let sum_len = vec.into_iter().map(|x| x.len()).fold(0, Add::add);
assert_eq!(sum_len, 8);
}
#[test]
fn test_u16_index() {
const N: usize = 4096;
let mut vec: ArrayVec<[_; N]> = ArrayVec::new();
for _ in 0..N {
assert!(vec.try_push(1u8).is_ok());
}
assert!(vec.try_push(0).is_err());
assert_eq!(vec.len(), N);
}
#[test]
fn test_iter() {
let mut iter = ArrayVec::from([1, 2, 3]).into_iter();
assert_eq!(iter.size_hint(), (3, Some(3)));
assert_eq!(iter.next_back(), Some(3));
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next_back(), Some(2));
assert_eq!(iter.size_hint(), (0, Some(0)));
assert_eq!(iter.next_back(), None);
}
#[test]
fn test_drop() {
use std::cell::Cell;
let flag = &Cell::new(0);
#[derive(Clone)]
struct Bump<'a>(&'a Cell<i32>);
impl<'a> Drop for Bump<'a> {
fn drop(&mut self) {
let n = self.0.get();
self.0.set(n + 1);
}
}
{
let mut array = ArrayVec::<[Bump; 128]>::new();
array.push(Bump(flag));
array.push(Bump(flag));
}
assert_eq!(flag.get(), 2);
// test something with the nullable pointer optimization
flag.set(0);
{
let mut array = ArrayVec::<[_; 3]>::new();
array.push(vec![Bump(flag)]);
array.push(vec![Bump(flag), Bump(flag)]);
array.push(vec![]);
let push4 = array.try_push(vec![Bump(flag)]);
assert_eq!(flag.get(), 0);
drop(push4);
assert_eq!(flag.get(), 1);
drop(array.pop());
assert_eq!(flag.get(), 1);
drop(array.pop());
assert_eq!(flag.get(), 3);
}
assert_eq!(flag.get(), 4);
// test into_inner
flag.set(0);
{
let mut array = ArrayVec::<[_; 3]>::new();
array.push(Bump(flag));
array.push(Bump(flag));
array.push(Bump(flag));
let inner = array.into_inner();
assert!(inner.is_ok());
assert_eq!(flag.get(), 0);
drop(inner);
assert_eq!(flag.get(), 3);
}
// test cloning into_iter
flag.set(0);
{
let mut array = ArrayVec::<[_; 3]>::new();
array.push(Bump(flag));
array.push(Bump(flag));
array.push(Bump(flag));
let mut iter = array.into_iter();
assert_eq!(flag.get(), 0);
iter.next();
assert_eq!(flag.get(), 1);
let clone = iter.clone();
assert_eq!(flag.get(), 1);
drop(clone);
assert_eq!(flag.get(), 3);
drop(iter);
assert_eq!(flag.get(), 5);
}
}
#[test]
fn test_extend() {
let mut range = 0..10;
let mut array: ArrayVec<[_; 5]> = range.by_ref().collect();
assert_eq!(&array[..], &[0, 1, 2, 3, 4]);
assert_eq!(range.next(), Some(5));
array.extend(range.by_ref());
assert_eq!(range.next(), Some(6));
let mut array: ArrayVec<[_; 10]> = (0..3).collect();
assert_eq!(&array[..], &[0, 1, 2]);
array.extend(3..5);
assert_eq!(&array[..], &[0, 1, 2, 3, 4]);
}
#[test]
fn test_is_send_sync() {
let data = ArrayVec::<[Vec<i32>; 5]>::new();
&data as &Send;
&data as &Sync;
}
#[test]
fn test_compact_size() {
// Future rust will kill these drop flags!
// 4 elements size + 1 len + 1 enum tag + [1 drop flag]
type ByteArray = ArrayVec<[u8; 4]>;
println!("{}", mem::size_of::<ByteArray>());
assert!(mem::size_of::<ByteArray>() <= 8);
// 12 element size + 1 enum tag + 3 padding + 1 len + 1 drop flag + 2 padding
type QuadArray = ArrayVec<[u32; 3]>;
println!("{}", mem::size_of::<QuadArray>());
assert!(mem::size_of::<QuadArray>() <= 24);
}
#[test]
fn test_still_works_with_option_arrayvec() {
type RefArray = ArrayVec<[&'static i32; 2]>;
let array = Some(RefArray::new());
assert!(array.is_some());
println!("{:?}", array);
}
#[test]
fn test_drain() {
let mut v = ArrayVec::from([0; 8]);
v.pop();
v.drain(0..7);
assert_eq!(&v[..], &[]);
v.extend(0..);
v.drain(1..4);
assert_eq!(&v[..], &[0, 4, 5, 6, 7]);
let u: ArrayVec<[_; 3]> = v.drain(1..4).rev().collect();
assert_eq!(&u[..], &[6, 5, 4]);
assert_eq!(&v[..], &[0, 7]);
v.drain(..);
assert_eq!(&v[..], &[]);
}
#[test]
fn test_retain() {
let mut v = ArrayVec::from([0; 8]);
for (i, elt) in v.iter_mut().enumerate() {
*elt = i;
}
v.retain(|_| true);
assert_eq!(&v[..], &[0, 1, 2, 3, 4, 5, 6, 7]);
v.retain(|elt| {
*elt /= 2;
*elt % 2 == 0
});
assert_eq!(&v[..], &[0, 0, 2, 2]);
v.retain(|_| false);
assert_eq!(&v[..], &[]);
}
#[test]
#[should_panic]
fn test_drain_oob() {
let mut v = ArrayVec::from([0; 8]);
v.pop();
v.drain(0..8);
}
#[test]
#[should_panic]
fn test_drop_panic() {
struct DropPanic;
impl Drop for DropPanic {
fn drop(&mut self) {
panic!("drop");
}
}
let mut array = ArrayVec::<[DropPanic; 1]>::new();
array.push(DropPanic);
}
#[test]
#[should_panic]
fn test_drop_panic_into_iter() {
struct DropPanic;
impl Drop for DropPanic {
fn drop(&mut self) {
panic!("drop");
}
}
let mut array = ArrayVec::<[DropPanic; 1]>::new();
array.push(DropPanic);
array.into_iter();
}
#[test]
fn test_insert() {
let mut v = ArrayVec::from([]);
assert_matches!(v.try_push(1), Err(_));
let mut v = ArrayVec::<[_; 3]>::new();
v.insert(0, 0);
v.insert(1, 1);
//let ret1 = v.try_insert(3, 3);
//assert_matches!(ret1, Err(InsertError::OutOfBounds(_)));
assert_eq!(&v[..], &[0, 1]);
v.insert(2, 2);
assert_eq!(&v[..], &[0, 1, 2]);
let ret2 = v.try_insert(1, 9);
assert_eq!(&v[..], &[0, 1, 2]);
assert_matches!(ret2, Err(_));
let mut v = ArrayVec::from([2]);
assert_matches!(v.try_insert(0, 1), Err(CapacityError { .. }));
assert_matches!(v.try_insert(1, 1), Err(CapacityError { .. }));
//assert_matches!(v.try_insert(2, 1), Err(CapacityError { .. }));
}
#[test]
fn test_into_inner_1() {
let mut v = ArrayVec::from([1, 2]);
v.pop();
let u = v.clone();
assert_eq!(v.into_inner(), Err(u));
}
#[test]
fn test_into_inner_2() {
let mut v = ArrayVec::<[String; 4]>::new();
v.push("a".into());
v.push("b".into());
v.push("c".into());
v.push("d".into());
assert_eq!(v.into_inner().unwrap(), ["a", "b", "c", "d"]);
}
#[test]
fn test_into_inner_3_() {
let mut v = ArrayVec::<[i32; 4]>::new();
v.extend(1..);
assert_eq!(v.into_inner().unwrap(), [1, 2, 3, 4]);
}
#[test]
fn test_write() {
use std::io::Write;
let mut v = ArrayVec::<[_; 8]>::new();
write!(&mut v, "\x01\x02\x03").unwrap();
assert_eq!(&v[..], &[1, 2, 3]);
let r = v.write(&[9; 16]).unwrap();
assert_eq!(r, 5);
assert_eq!(&v[..], &[1, 2, 3, 9, 9, 9, 9, 9]);
}
#[test]
fn array_clone_from() {
let mut v = ArrayVec::<[_; 4]>::new();
v.push(vec![1, 2]);
v.push(vec![3, 4, 5]);
v.push(vec![6]);
let reference = v.to_vec();
let mut u = ArrayVec::<[_; 4]>::new();
u.clone_from(&v);
assert_eq!(&u, &reference[..]);
let mut t = ArrayVec::<[_; 4]>::new();
t.push(vec![97]);
t.push(vec![]);
t.push(vec![5, 6, 2]);
t.push(vec![2]);
t.clone_from(&v);
assert_eq!(&t, &reference[..]);
t.clear();
t.clone_from(&v);
assert_eq!(&t, &reference[..]);
}
#[test]
fn test_string() {
use std::error::Error;
let text = "hello world";
let mut s = ArrayString::<[_; 16]>::new();
s.try_push_str(text).unwrap();
assert_eq!(&s, text);
assert_eq!(text, &s);
// Make sure Hash / Eq / Borrow match up so we can use HashMap
let mut map = HashMap::new();
map.insert(s, 1);
assert_eq!(map[text], 1);
let mut t = ArrayString::<[_; 2]>::new();
assert!(t.try_push_str(text).is_err());
assert_eq!(&t, "");
t.push_str("ab");
// DerefMut
let tmut: &mut str = &mut t;
assert_eq!(tmut, "ab");
// Test Error trait / try
let t = || -> Result<(), Box<Error>> {
let mut t = ArrayString::<[_; 2]>::new();
try!(t.try_push_str(text));
Ok(())
}();
assert!(t.is_err());
}
#[test]
fn test_string_from() {
let text = "hello world";
// Test `from` constructor
let u = ArrayString::<[_; 11]>::from(text).unwrap();
assert_eq!(&u, text);
assert_eq!(u.len(), text.len());
}
#[test]
fn test_string_from_bytes() {
let text = "hello world";
let u = ArrayString::from_byte_string(b"hello world").unwrap();
assert_eq!(&u, text);
assert_eq!(u.len(), text.len());
}
#[test]
fn test_string_clone() {
let text = "hi";
let mut s = ArrayString::<[_; 4]>::new();
s.push_str("abcd");
let t = ArrayString::<[_; 4]>::from(text).unwrap();
s.clone_from(&t);
assert_eq!(&t, &s);
}
#[test]
fn test_string_push() {
let text = "abcαβγ";
let mut s = ArrayString::<[_; 8]>::new();
for c in text.chars() {
if let Err(_) = s.try_push(c) {
break;
}
}
assert_eq!("abcαβ", &s[..]);
s.push('x');
assert_eq!("abcαβx", &s[..]);
assert!(s.try_push('x').is_err());
}
#[test]
fn test_insert_at_length() {
let mut v = ArrayVec::<[_; 8]>::new();
let result1 = v.try_insert(0, "a");
let result2 = v.try_insert(1, "b");
assert!(result1.is_ok() && result2.is_ok());
assert_eq!(&v[..], &["a", "b"]);
}
#[should_panic]
#[test]
fn test_insert_out_of_bounds() {
let mut v = ArrayVec::<[_; 8]>::new();
let _ = v.try_insert(1, "test");
}
/*
* insert that pushes out the last
let mut u = ArrayVec::from([1, 2, 3, 4]);
let ret = u.try_insert(3, 99);
assert_eq!(&u[..], &[1, 2, 3, 99]);
assert_matches!(ret, Err(_));
let ret = u.try_insert(4, 77);
assert_eq!(&u[..], &[1, 2, 3, 99]);
assert_matches!(ret, Err(_));
*/
#[test]
fn test_drop_in_insert() {
use std::cell::Cell;
let flag = &Cell::new(0);
struct Bump<'a>(&'a Cell<i32>);
impl<'a> Drop for Bump<'a> {
fn drop(&mut self) {
let n = self.0.get();
self.0.set(n + 1);
}
}
flag.set(0);
{
let mut array = ArrayVec::<[_; 2]>::new();
array.push(Bump(flag));
array.insert(0, Bump(flag));
assert_eq!(flag.get(), 0);
let ret = array.try_insert(1, Bump(flag));
assert_eq!(flag.get(), 0);
assert_matches!(ret, Err(_));
drop(ret);
assert_eq!(flag.get(), 1);
}
assert_eq!(flag.get(), 3);
}
#[test]
fn test_pop_at() {
let mut v = ArrayVec::<[String; 4]>::new();
let s = String::from;
v.push(s("a"));
v.push(s("b"));
v.push(s("c"));
v.push(s("d"));
assert_eq!(v.pop_at(4), None);
assert_eq!(v.pop_at(1), Some(s("b")));
assert_eq!(v.pop_at(1), Some(s("c")));
assert_eq!(v.pop_at(2), None);
assert_eq!(&v[..], &["a", "d"]);
}
#[test]
fn test_sizes() {
let v = ArrayVec::from([0u8; 1 << 16]);
assert_eq!(vec![0u8; v.len()], &v[..]);
}
#[test]
fn test_default() {
use std::net;
let s: ArrayString<[u8; 4]> = Default::default();
// Something without `Default` implementation.
let v: ArrayVec<[net::TcpStream; 4]> = Default::default();
assert_eq!(s.len(), 0);
assert_eq!(v.len(), 0);
}
#[cfg(feature="array-sizes-33-128")]
#[test]
fn test_sizes_33_128() {
ArrayVec::from([0u8; 52]);
ArrayVec::from([0u8; 127]);
}
#[cfg(feature="array-sizes-129-255")]
#[test]
fn test_sizes_129_255() {
ArrayVec::from([0u8; 237]);
ArrayVec::from([0u8; 255]);
}
#[test]
fn test_newish_stable_uses_maybe_uninit() {
if option_env!("ARRAYVECTEST_ENSURE_MAYBEUNINIT").map(|s| !s.is_empty()).unwrap_or(false) {
assert!(cfg!(has_stable_maybe_uninit));
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,61 +0,0 @@
"""
cargo-raze crate build file.
DO NOT EDIT! Replaced on runs of cargo-raze
"""
package(default_visibility = [
# Public for visibility by "@raze__crate__version//" targets.
#
# Prefer access through "//third_party/cargo", which limits external
# visibility to explicit Cargo.toml dependencies.
"//visibility:public",
])
licenses([
"notice", # "MIT,Apache-2.0"
])
load(
"@io_bazel_rules_rust//rust:rust.bzl",
"rust_library",
"rust_binary",
"rust_test",
)
# Unsupported target "accuracy" with type "test" omitted
# Unsupported target "backtrace" with type "example" omitted
rust_library(
name = "backtrace",
crate_root = "src/lib.rs",
crate_type = "lib",
edition = "2018",
srcs = glob(["**/*.rs"]),
deps = [
"//third_party/cargo/vendor/backtrace-sys-0.1.34:backtrace_sys",
"//third_party/cargo/vendor/cfg-if-0.1.10:cfg_if",
"//third_party/cargo/vendor/libc-0.2.67:libc",
"//third_party/cargo/vendor/rustc-demangle-0.1.16:rustc_demangle",
],
rustc_flags = [
"--cap-lints=allow",
],
version = "0.3.45",
crate_features = [
"backtrace-sys",
"dbghelp",
"default",
"dladdr",
"libbacktrace",
"libunwind",
"std",
],
)
# Unsupported target "benchmarks" with type "bench" omitted
# Unsupported target "concurrent-panics" with type "test" omitted
# Unsupported target "long_fn_name" with type "test" omitted
# Unsupported target "raw" with type "example" omitted
# Unsupported target "skip_inner_frames" with type "test" omitted
# Unsupported target "smoke" with type "test" omitted

View File

@ -1,314 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "addr2line"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gimli 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arrayvec"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "backtrace"
version = "0.3.45"
dependencies = [
"addr2line 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace-sys 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"compiler_builtins 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
"cpp_demangle 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"findshlibs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"goblin 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-std-workspace-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
"compiler_builtins 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-std-workspace-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "byteorder"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"compiler_builtins 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-std-workspace-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "compiler_builtins"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cpp_demangle"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "findshlibs"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gimli"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "goblin"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazycell"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc-std-workspace-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memmap"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"compiler_builtins 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-std-workspace-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-std-workspace-core"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "scroll"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "scroll_derive"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "smallvec"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "stable_deref_trait"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum addr2line 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1c4e698660ed2d0f625c39bb877332b4269668720e330e2aa3d67bb1187a656a"
"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
"checksum backtrace-sys 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "e17b52e737c40a7d75abca20b29a19a0eb7ba9fc72c5a72dd282a0a3c2c0dc35"
"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum compiler_builtins 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "036b035e9ebcd705affece16319223d19f229e2358be6e3b7b094e57193312e6"
"checksum cpp_demangle 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4115af6f575a7bc82c613e9e0ed7cc36a5e4fc3a8b54920dc0c820823a31a0d6"
"checksum fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
"checksum findshlibs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1260d61e4fe2a6ab845ffdc426a0bd68ffb240b91cf0ec5a8d1170cec535bd8"
"checksum gimli 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81dd6190aad0f05ddbbf3245c54ed14ca4aa6dd32f22312b70d8f168c3e3e633"
"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
"checksum goblin 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0259546d6aed5dd1f4efc3ae663cae62912ceb927c0e96ae1fc8a22ab1516763"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc-std-workspace-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1956f5517128a2b6f23ab2dadf1a976f4f5b27962e7724c2bf3d45e539ec098c"
"checksum scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1"
"checksum scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28"
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -1,132 +0,0 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
edition = "2018"
name = "backtrace"
version = "0.3.45"
authors = ["The Rust Project Developers"]
autoexamples = true
autotests = true
description = "A library to acquire a stack trace (backtrace) at runtime in a Rust program.\n"
homepage = "https://github.com/rust-lang/backtrace-rs"
documentation = "https://docs.rs/backtrace"
readme = "README.md"
license = "MIT/Apache-2.0"
repository = "https://github.com/rust-lang/backtrace-rs"
[[example]]
name = "backtrace"
required-features = ["std"]
[[example]]
name = "raw"
required-features = ["std"]
[[test]]
name = "skip_inner_frames"
required-features = ["std"]
[[test]]
name = "long_fn_name"
required-features = ["std"]
[[test]]
name = "smoke"
required-features = ["std"]
edition = "2018"
[[test]]
name = "accuracy"
required-features = ["std", "dbghelp", "libbacktrace", "libunwind"]
edition = "2018"
[[test]]
name = "concurrent-panics"
harness = false
required-features = ["std"]
[dependencies.addr2line]
version = "0.11.0"
features = ["std"]
optional = true
default-features = false
[dependencies.backtrace-sys]
version = "0.1.33"
optional = true
default_features = false
[dependencies.cfg-if]
version = "0.1.10"
[dependencies.compiler_builtins]
version = "0.1.2"
optional = true
[dependencies.core]
version = "1.0.0"
optional = true
package = "rustc-std-workspace-core"
[dependencies.cpp_demangle]
version = "0.2.3"
optional = true
default-features = false
[dependencies.findshlibs]
version = "0.5.0"
optional = true
[dependencies.goblin]
version = "0.2"
features = ["elf32", "elf64", "mach32", "mach64", "pe32", "pe64", "std"]
optional = true
default-features = false
[dependencies.libc]
version = "0.2.45"
default-features = false
[dependencies.memmap]
version = "0.7.0"
optional = true
[dependencies.rustc-demangle]
version = "0.1.4"
[dependencies.rustc-serialize]
version = "0.3"
optional = true
[dependencies.serde]
version = "1.0"
features = ["derive"]
optional = true
[features]
coresymbolication = []
dbghelp = []
default = ["std", "libunwind", "libbacktrace", "dladdr", "dbghelp"]
dladdr = []
gimli-symbolize = ["addr2line", "findshlibs", "memmap", "goblin"]
kernel32 = []
libbacktrace = ["backtrace-sys/backtrace-sys"]
libunwind = []
rustc-dep-of-std = ["backtrace-sys/rustc-dep-of-std", "cfg-if/rustc-dep-of-std", "core", "compiler_builtins", "libc/rustc-dep-of-std", "rustc-demangle/rustc-dep-of-std"]
serialize-rustc = ["rustc-serialize"]
serialize-serde = ["serde"]
std = []
unix-backtrace = []
verify-winapi = ["winapi/dbghelp", "winapi/handleapi", "winapi/libloaderapi", "winapi/minwindef", "winapi/processthreadsapi", "winapi/synchapi", "winapi/winbase", "winapi/winnt"]
[target."cfg(windows)".dependencies.winapi]
version = "0.3.3"
optional = true

View File

@ -1,81 +0,0 @@
# backtrace-rs
[Documentation](https://docs.rs/backtrace)
A library for acquiring backtraces at runtime for Rust. This library aims to
enhance the support of the standard library by providing a programmatic
interface to work with, but it also supports simply easily printing the current
backtrace like libstd's panics.
## Install
```toml
[dependencies]
backtrace = "0.3"
```
Note that this crate requires `cc` and `ar` to be present on Unix systems when
`libbacktrace` is used (which is the default). For configuring C compilers see
the [`cc` crate documentation](https://github.com/alexcrichton/cc-rs).
## Usage
To simply capture a backtrace and defer dealing with it until a later time,
you can use the top-level `Backtrace` type.
```rust
extern crate backtrace;
use backtrace::Backtrace;
fn main() {
let bt = Backtrace::new();
// do_some_work();
println!("{:?}", bt);
}
```
If, however, you'd like more raw access to the actual tracing functionality, you
can use the `trace` and `resolve` functions directly.
```rust
extern crate backtrace;
fn main() {
backtrace::trace(|frame| {
let ip = frame.ip();
let symbol_address = frame.symbol_address();
// Resolve this instruction pointer to a symbol name
backtrace::resolve_frame(frame, |symbol| {
if let Some(name) = symbol.name() {
// ...
}
if let Some(filename) = symbol.filename() {
// ...
}
});
true // keep going to the next frame
});
}
```
# License
This project is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in backtrace-rs by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.

View File

@ -1,94 +0,0 @@
#![feature(test)]
extern crate test;
extern crate backtrace;
#[cfg(feature = "std")]
use backtrace::Backtrace;
#[bench]
#[cfg(feature = "std")]
fn trace(b: &mut test::Bencher) {
#[inline(never)]
fn the_function() {
backtrace::trace(|frame| {
let ip = frame.ip();
test::black_box(ip);
true
});
}
b.iter(the_function);
}
#[bench]
#[cfg(feature = "std")]
fn trace_and_resolve_callback(b: &mut test::Bencher) {
#[inline(never)]
fn the_function() {
backtrace::trace(|frame| {
backtrace::resolve(frame.ip(), |symbol| {
let addr = symbol.addr();
test::black_box(addr);
});
true
});
}
b.iter(the_function);
}
#[bench]
#[cfg(feature = "std")]
fn trace_and_resolve_separate(b: &mut test::Bencher) {
#[inline(never)]
fn the_function(frames: &mut Vec<*mut std::ffi::c_void>) {
backtrace::trace(|frame| {
frames.push(frame.ip());
true
});
frames.iter().for_each(|frame_ip| {
backtrace::resolve(*frame_ip, |symbol| {
test::black_box(symbol);
});
});
}
let mut frames = Vec::with_capacity(1024);
b.iter(|| {
the_function(&mut frames);
frames.clear();
});
}
#[bench]
#[cfg(feature = "std")]
fn new_unresolved(b: &mut test::Bencher) {
#[inline(never)]
fn the_function() {
let bt = Backtrace::new_unresolved();
test::black_box(bt);
}
b.iter(the_function);
}
#[bench]
#[cfg(feature = "std")]
fn new(b: &mut test::Bencher) {
#[inline(never)]
fn the_function() {
let bt = Backtrace::new();
test::black_box(bt);
}
b.iter(the_function);
}
#[bench]
#[cfg(feature = "std")]
fn new_unresolved_and_resolve_separate(b: &mut test::Bencher) {
#[inline(never)]
fn the_function() {
let mut bt = Backtrace::new_unresolved();
bt.resolve();
test::black_box(bt);
}
b.iter(the_function);
}

View File

@ -1,23 +0,0 @@
set -ex
ANDROID_ARCH=$1
ANDROID_SDK_VERSION=4333796
mkdir /tmp/android
cd /tmp/android
curl -o android-sdk.zip \
"https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_VERSION}.zip"
unzip -q android-sdk.zip
yes | ./tools/bin/sdkmanager --licenses > /dev/null
./tools/bin/sdkmanager ndk-bundle > /dev/null
./ndk-bundle/build/tools/make_standalone_toolchain.py \
--arch $ANDROID_ARCH \
--stl=libc++ \
--api 21 \
--install-dir /android-toolchain
cd /tmp
rm -rf android

View File

@ -1,74 +0,0 @@
#!/usr/bin/env sh
# Copyright 2016 The Rust Project Developers. See the COPYRIGHT
# file at the top-level directory of this distribution and at
# http://rust-lang.org/COPYRIGHT.
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.
set -ex
# Prep the SDK and emulator
#
# Note that the update process requires that we accept a bunch of licenses, and
# we can't just pipe `yes` into it for some reason, so we take the same strategy
# located in https://github.com/appunite/docker by just wrapping it in a script
# which apparently magically accepts the licenses.
SDK=4333796
mkdir sdk
curl --retry 20 https://dl.google.com/android/repository/sdk-tools-linux-${SDK}.zip -O
unzip -q -d sdk sdk-tools-linux-${SDK}.zip
case "$1" in
arm | armv7)
api=24
image="system-images;android-${api};google_apis;armeabi-v7a"
;;
aarch64)
api=24
image="system-images;android-${api};google_apis;arm64-v8a"
;;
i686)
api=28
image="system-images;android-${api};default;x86"
;;
x86_64)
api=28
image="system-images;android-${api};default;x86_64"
;;
*)
echo "invalid arch: $1"
exit 1
;;
esac;
# Try to fix warning about missing file.
# See https://askubuntu.com/a/1078784
mkdir -p /root/.android/
echo '### User Sources for Android SDK Manager' >> /root/.android/repositories.cfg
echo '#Fri Nov 03 10:11:27 CET 2017 count=0' >> /root/.android/repositories.cfg
# Print all available packages
# yes | ./sdk/tools/bin/sdkmanager --list --verbose
# --no_https avoids
# javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
#
# | grep -v = || true removes the progress bar output from the sdkmanager
# which produces an insane amount of output.
yes | ./sdk/tools/bin/sdkmanager --licenses --no_https | grep -v = || true
yes | ./sdk/tools/bin/sdkmanager --no_https \
"emulator" \
"platform-tools" \
"platforms;android-${api}" \
"${image}" | grep -v = || true
echo "no" |
./sdk/tools/bin/avdmanager create avd \
--name "${1}" \
--package "${image}" | grep -v = || true

View File

@ -1,18 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
unzip \
openjdk-8-jre \
python \
gcc \
libc6-dev
COPY android-ndk.sh /
RUN /android-ndk.sh arm64
ENV PATH=$PATH:/android-toolchain/bin
# TODO: run tests in an emulator eventually
ENV CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=aarch64-linux-android-gcc \
CARGO_TARGET_AARCH64_LINUX_ANDROID_RUNNER=echo

View File

@ -1,11 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
ca-certificates \
libc6-dev \
gcc-aarch64-linux-gnu \
libc6-dev-arm64-cross \
qemu-user
ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-aarch64 -L /usr/aarch64-linux-gnu"

View File

@ -1,37 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
unzip \
openjdk-8-jre \
python \
gcc \
libc6-dev
COPY android-ndk.sh /
RUN /android-ndk.sh arm
WORKDIR /android
COPY android-sdk.sh /android/sdk.sh
RUN ./sdk.sh arm
RUN mv /root/.android /tmp
RUN chmod 777 -R /tmp/.android
RUN chmod 755 /android/sdk/tools/* /android/sdk/emulator/qemu/linux-x86_64/*
ENV PATH=$PATH:/android-toolchain/bin:/android/sdk/platform-tools
# TODO: run tests in an emulator eventually
ENV CARGO_TARGET_ARM_LINUX_ANDROIDEABI_LINKER=arm-linux-androideabi-gcc \
CARGO_TARGET_ARM_LINUX_ANDROIDEABI_RUNNER=/tmp/runtest \
HOME=/tmp
ADD runtest-android.rs /tmp/runtest.rs
ENTRYPOINT [ \
"bash", \
"-c", \
# set SHELL so android can detect a 64bits system, see
# http://stackoverflow.com/a/41789144
"SHELL=/bin/dash /android/sdk/emulator/emulator @arm -no-window & \
/rust/bin/rustc /tmp/runtest.rs -o /tmp/runtest && \
exec \"$@\"", \
"--" \
]

View File

@ -1,10 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
ca-certificates \
libc6-dev \
gcc-arm-linux-gnueabihf \
libc6-dev-armhf-cross \
qemu-user
ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_RUNNER="qemu-arm -L /usr/arm-linux-gnueabihf"

View File

@ -1,18 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
unzip \
openjdk-8-jre \
python \
gcc \
libc6-dev
COPY android-ndk.sh /
RUN /android-ndk.sh arm
ENV PATH=$PATH:/android-toolchain/bin
# TODO: run tests in an emulator eventually
ENV CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=arm-linux-androideabi-gcc \
CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_RUNNER=echo

View File

@ -1,10 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
ca-certificates \
libc6-dev \
gcc-arm-linux-gnueabihf \
libc6-dev-armhf-cross \
qemu-user
ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \
CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER="qemu-arm -L /usr/arm-linux-gnueabihf"

View File

@ -1,5 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc-multilib \
libc6-dev \
ca-certificates

View File

@ -1,18 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
unzip \
openjdk-8-jre \
python \
gcc \
libc6-dev
COPY android-ndk.sh /
RUN /android-ndk.sh x86
ENV PATH=$PATH:/android-toolchain/bin
# TODO: run tests in an emulator eventually
ENV CARGO_TARGET_I686_LINUX_ANDROID_LINKER=i686-linux-android-gcc \
CARGO_TARGET_I686_LINUX_ANDROID_RUNNER=echo

View File

@ -1,5 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc-multilib \
libc6-dev \
ca-certificates

View File

@ -1,16 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
ca-certificates \
libc6-dev \
gcc-powerpc64-linux-gnu \
libc6-dev-ppc64-cross \
qemu-user \
qemu-system-ppc
ENV CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_LINKER=powerpc64-linux-gnu-gcc \
# TODO: should actually run these tests
#CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_RUNNER="qemu-ppc64 -L /usr/powerpc64-linux-gnu" \
CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_RUNNER=echo \
CC=powerpc64-linux-gnu-gcc

View File

@ -1,18 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
unzip \
openjdk-8-jre \
python \
gcc \
libc6-dev
COPY android-ndk.sh /
RUN /android-ndk.sh x86_64
ENV PATH=$PATH:/android-toolchain/bin
# TODO: run tests in an emulator eventually
ENV CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=x86_64-linux-android-gcc \
CARGO_TARGET_X86_64_LINUX_ANDROID_RUNNER=echo

View File

@ -1,10 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libc6-dev \
ca-certificates \
gcc-mingw-w64-x86-64
# No need to run tests, we're just testing that it compiles
ENV CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUNNER=echo \
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=x86_64-w64-mingw32-gcc

View File

@ -1,5 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libc6-dev \
ca-certificates

View File

@ -1,6 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libc6-dev \
ca-certificates \
musl-tools

View File

@ -1,32 +0,0 @@
# Small script to run tests for a target (or all targets) inside all the
# respective docker images.
set -ex
run() {
docker build -t backtrace -f ci/docker/$1/Dockerfile ci
mkdir -p target
docker run \
--user `id -u`:`id -g` \
--rm \
--init \
--volume $(dirname $(dirname `which cargo`)):/cargo \
--env CARGO_HOME=/cargo \
--volume `rustc --print sysroot`:/rust:ro \
--env TARGET=$1 \
--volume `pwd`:/checkout:ro \
--volume `pwd`/target:/checkout/target \
--workdir /checkout \
--privileged \
backtrace \
bash \
-c 'PATH=$PATH:/rust/bin exec ci/run.sh'
}
if [ -z "$1" ]; then
for d in `ls ci/docker/`; do
run $d
done
else
run $1
fi

View File

@ -1,5 +0,0 @@
#!/bin/sh
set -ex
cargo test --target $TARGET

View File

@ -1,50 +0,0 @@
use std::env;
use std::process::Command;
use std::path::{Path, PathBuf};
fn main() {
let args = env::args_os()
.skip(1)
.filter(|arg| arg != "--quiet")
.collect::<Vec<_>>();
assert_eq!(args.len(), 1);
let test = PathBuf::from(&args[0]);
let dst = Path::new("/data/local/tmp").join(test.file_name().unwrap());
println!("waiting for device to come online...");
let status = Command::new("adb")
.arg("wait-for-device")
.status()
.expect("failed to run: adb wait-for-device");
assert!(status.success());
println!("pushing executable...");
let status = Command::new("adb")
.arg("push")
.arg(&test)
.arg(&dst)
.status()
.expect("failed to run: adb pushr");
assert!(status.success());
println!("executing tests...");
let output = Command::new("adb")
.arg("shell")
.arg(&dst)
.output()
.expect("failed to run: adb shell");
assert!(status.success());
println!("status: {}\nstdout ---\n{}\nstderr ---\n{}",
output.status,
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr));
let stdout = String::from_utf8_lossy(&output.stdout);
stdout.lines().find(|l|
(l.starts_with("PASSED ") && l.contains(" tests")) ||
l.starts_with("test result: ok")
).unwrap_or_else(|| {
panic!("failed to find successful test run");
});
}

View File

@ -1,7 +0,0 @@
extern crate backtrace;
use backtrace::Backtrace;
fn main() {
println!("{:?}", Backtrace::new());
}

View File

@ -1,54 +0,0 @@
extern crate backtrace;
fn main() {
foo();
}
fn foo() {
bar()
}
fn bar() {
baz()
}
fn baz() {
print()
}
#[cfg(target_pointer_width = "32")]
const HEX_WIDTH: usize = 10;
#[cfg(target_pointer_width = "64")]
const HEX_WIDTH: usize = 20;
fn print() {
let mut cnt = 0;
backtrace::trace(|frame| {
let ip = frame.ip();
print!("frame #{:<2} - {:#02$x}", cnt, ip as usize, HEX_WIDTH);
cnt += 1;
let mut resolved = false;
backtrace::resolve(frame.ip(), |symbol| {
if !resolved {
resolved = true;
} else {
print!("{}", vec![" "; 7 + 2 + 3 + HEX_WIDTH].join(""));
}
if let Some(name) = symbol.name() {
print!(" - {}", name);
} else {
print!(" - <unknown>");
}
if let Some(file) = symbol.filename() {
if let Some(l) = symbol.lineno() {
print!("\n{:13}{:4$}@ {}:{}", "", "", file.display(), l, HEX_WIDTH);
}
}
println!("");
});
if !resolved {
println!(" - <no info>");
}
true // keep going
});
}

View File

@ -1,233 +0,0 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Backtrace strategy for MSVC platforms.
//!
//! This module contains the ability to generate a backtrace on MSVC using one
//! of two possible methods. The `StackWalkEx` function is primarily used if
//! possible, but not all systems have that. Failing that the `StackWalk64`
//! function is used instead. Note that `StackWalkEx` is favored because it
//! handles debuginfo internally and returns inline frame information.
//!
//! Note that all dbghelp support is loaded dynamically, see `src/dbghelp.rs`
//! for more information about that.
#![allow(bad_style)]
use crate::dbghelp;
use crate::windows::*;
use core::ffi::c_void;
use core::mem;
#[derive(Clone, Copy)]
pub enum Frame {
New(STACKFRAME_EX),
Old(STACKFRAME64),
}
// we're just sending around raw pointers and reading them, never interpreting
// them so this should be safe to both send and share across threads.
unsafe impl Send for Frame {}
unsafe impl Sync for Frame {}
impl Frame {
pub fn ip(&self) -> *mut c_void {
self.addr_pc().Offset as *mut _
}
pub fn symbol_address(&self) -> *mut c_void {
self.ip()
}
fn addr_pc(&self) -> &ADDRESS64 {
match self {
Frame::New(new) => &new.AddrPC,
Frame::Old(old) => &old.AddrPC,
}
}
fn addr_pc_mut(&mut self) -> &mut ADDRESS64 {
match self {
Frame::New(new) => &mut new.AddrPC,
Frame::Old(old) => &mut old.AddrPC,
}
}
fn addr_frame_mut(&mut self) -> &mut ADDRESS64 {
match self {
Frame::New(new) => &mut new.AddrFrame,
Frame::Old(old) => &mut old.AddrFrame,
}
}
fn addr_stack_mut(&mut self) -> &mut ADDRESS64 {
match self {
Frame::New(new) => &mut new.AddrStack,
Frame::Old(old) => &mut old.AddrStack,
}
}
}
#[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in winapi right now
struct MyContext(CONTEXT);
#[inline(always)]
pub unsafe fn trace(cb: &mut FnMut(&super::Frame) -> bool) {
// Allocate necessary structures for doing the stack walk
let process = GetCurrentProcess();
let thread = GetCurrentThread();
let mut context = mem::zeroed::<MyContext>();
RtlCaptureContext(&mut context.0);
// Ensure this process's symbols are initialized
let dbghelp = match dbghelp::init() {
Ok(dbghelp) => dbghelp,
Err(()) => return, // oh well...
};
// On x86_64 and ARM64 we opt to not use the default `Sym*` functions from
// dbghelp for getting the function table and module base. Instead we use
// the `RtlLookupFunctionEntry` function in kernel32 which will account for
// JIT compiler frames as well. These should be equivalent, but using
// `Rtl*` allows us to backtrace through JIT frames.
//
// Note that `RtlLookupFunctionEntry` only works for in-process backtraces,
// but that's all we support anyway, so it all lines up well.
cfg_if::cfg_if! {
if #[cfg(target_pointer_width = "64")] {
use core::ptr;
unsafe extern "system" fn function_table_access(_process: HANDLE, addr: DWORD64) -> PVOID {
let mut base = 0;
RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut()).cast()
}
unsafe extern "system" fn get_module_base(_process: HANDLE, addr: DWORD64) -> DWORD64 {
let mut base = 0;
RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut());
base
}
} else {
let function_table_access = dbghelp.SymFunctionTableAccess64();
let get_module_base = dbghelp.SymGetModuleBase64();
}
}
// Attempt to use `StackWalkEx` if we can, but fall back to `StackWalk64`
// since it's in theory supported on more systems.
match (*dbghelp.dbghelp()).StackWalkEx() {
Some(StackWalkEx) => {
let mut frame = super::Frame {
inner: Frame::New(mem::zeroed()),
};
let image = init_frame(&mut frame.inner, &context.0);
let frame_ptr = match &mut frame.inner {
Frame::New(ptr) => ptr as *mut STACKFRAME_EX,
_ => unreachable!(),
};
while StackWalkEx(
image as DWORD,
process,
thread,
frame_ptr,
&mut context.0 as *mut CONTEXT as *mut _,
None,
Some(function_table_access),
Some(get_module_base),
None,
0,
) == TRUE
{
if !cb(&frame) {
break;
}
}
}
None => {
let mut frame = super::Frame {
inner: Frame::Old(mem::zeroed()),
};
let image = init_frame(&mut frame.inner, &context.0);
let frame_ptr = match &mut frame.inner {
Frame::Old(ptr) => ptr as *mut STACKFRAME64,
_ => unreachable!(),
};
while dbghelp.StackWalk64()(
image as DWORD,
process,
thread,
frame_ptr,
&mut context.0 as *mut CONTEXT as *mut _,
None,
Some(function_table_access),
Some(get_module_base),
None,
) == TRUE
{
if !cb(&frame) {
break;
}
}
}
}
}
#[cfg(target_arch = "x86_64")]
fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
frame.addr_pc_mut().Offset = ctx.Rip as u64;
frame.addr_pc_mut().Mode = AddrModeFlat;
frame.addr_stack_mut().Offset = ctx.Rsp as u64;
frame.addr_stack_mut().Mode = AddrModeFlat;
frame.addr_frame_mut().Offset = ctx.Rbp as u64;
frame.addr_frame_mut().Mode = AddrModeFlat;
IMAGE_FILE_MACHINE_AMD64
}
#[cfg(target_arch = "x86")]
fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
frame.addr_pc_mut().Offset = ctx.Eip as u64;
frame.addr_pc_mut().Mode = AddrModeFlat;
frame.addr_stack_mut().Offset = ctx.Esp as u64;
frame.addr_stack_mut().Mode = AddrModeFlat;
frame.addr_frame_mut().Offset = ctx.Ebp as u64;
frame.addr_frame_mut().Mode = AddrModeFlat;
IMAGE_FILE_MACHINE_I386
}
#[cfg(target_arch = "aarch64")]
fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
frame.addr_pc_mut().Offset = ctx.Pc as u64;
frame.addr_pc_mut().Mode = AddrModeFlat;
frame.addr_stack_mut().Offset = ctx.Sp as u64;
frame.addr_stack_mut().Mode = AddrModeFlat;
unsafe {
frame.addr_frame_mut().Offset = ctx.u.s().Fp as u64;
}
frame.addr_frame_mut().Mode = AddrModeFlat;
IMAGE_FILE_MACHINE_ARM64
}
#[cfg(target_arch = "arm")]
fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
frame.addr_pc_mut().Offset = ctx.Pc as u64;
frame.addr_pc_mut().Mode = AddrModeFlat;
frame.addr_stack_mut().Offset = ctx.Sp as u64;
frame.addr_stack_mut().Mode = AddrModeFlat;
unsafe {
frame.addr_frame_mut().Offset = ctx.R11 as u64;
}
frame.addr_frame_mut().Mode = AddrModeFlat;
IMAGE_FILE_MACHINE_ARMNT
}

View File

@ -1,231 +0,0 @@
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Backtrace support using libunwind/gcc_s/etc APIs.
//!
//! This module contains the ability to unwind the stack using libunwind-style
//! APIs. Note that there's a whole bunch of implementations of the
//! libunwind-like API, and this is just trying to be compatible with most of
//! them all at once instead of being picky.
//!
//! The libunwind API is powered by `_Unwind_Backtrace` and is in practice very
//! reliable at generating a backtrace. It's not entirely clear how it does it
//! (frame pointers? eh_frame info? both?) but it seems to work!
//!
//! Most of the complexity of this module is handling the various platform
//! differences across libunwind implementations. Otherwise this is a pretty
//! straightforward Rust binding to the libunwind APIs.
//!
//! This is the default unwinding API for all non-Windows platforms currently.
use core::ffi::c_void;
pub enum Frame {
Raw(*mut uw::_Unwind_Context),
Cloned {
ip: *mut c_void,
symbol_address: *mut c_void,
},
}
// With a raw libunwind pointer it should only ever be access in a readonly
// threadsafe fashion, so it's `Sync`. When sending to other threads via `Clone`
// we always switch to a version which doesn't retain interior pointers, so we
// should be `Send` as well.
unsafe impl Send for Frame {}
unsafe impl Sync for Frame {}
impl Frame {
pub fn ip(&self) -> *mut c_void {
let ctx = match *self {
Frame::Raw(ctx) => ctx,
Frame::Cloned { ip, .. } => return ip,
};
unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void }
}
pub fn symbol_address(&self) -> *mut c_void {
if let Frame::Cloned { symbol_address, .. } = *self {
return symbol_address;
}
// It seems that on OSX `_Unwind_FindEnclosingFunction` returns a
// pointer to... something that's unclear. It's definitely not always
// the enclosing function for whatever reason. It's not entirely clear
// to me what's going on here, so pessimize this for now and just always
// return the ip.
//
// Note the `skip_inner_frames.rs` test is skipped on OSX due to this
// clause, and if this is fixed that test in theory can be run on OSX!
if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
self.ip()
} else {
unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) }
}
}
}
impl Clone for Frame {
fn clone(&self) -> Frame {
Frame::Cloned {
ip: self.ip(),
symbol_address: self.symbol_address(),
}
}
}
#[inline(always)]
pub unsafe fn trace(mut cb: &mut FnMut(&super::Frame) -> bool) {
uw::_Unwind_Backtrace(trace_fn, &mut cb as *mut _ as *mut _);
extern "C" fn trace_fn(
ctx: *mut uw::_Unwind_Context,
arg: *mut c_void,
) -> uw::_Unwind_Reason_Code {
let cb = unsafe { &mut *(arg as *mut &mut FnMut(&super::Frame) -> bool) };
let cx = super::Frame {
inner: Frame::Raw(ctx),
};
let mut bomb = crate::Bomb { enabled: true };
let keep_going = cb(&cx);
bomb.enabled = false;
if keep_going {
uw::_URC_NO_REASON
} else {
uw::_URC_FAILURE
}
}
}
/// Unwind library interface used for backtraces
///
/// Note that dead code is allowed as here are just bindings
/// iOS doesn't use all of them it but adding more
/// platform-specific configs pollutes the code too much
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
#[allow(dead_code)]
mod uw {
pub use self::_Unwind_Reason_Code::*;
use core::ffi::c_void;
#[repr(C)]
pub enum _Unwind_Reason_Code {
_URC_NO_REASON = 0,
_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
_URC_FATAL_PHASE2_ERROR = 2,
_URC_FATAL_PHASE1_ERROR = 3,
_URC_NORMAL_STOP = 4,
_URC_END_OF_STACK = 5,
_URC_HANDLER_FOUND = 6,
_URC_INSTALL_CONTEXT = 7,
_URC_CONTINUE_UNWIND = 8,
_URC_FAILURE = 9, // used only by ARM EABI
}
pub enum _Unwind_Context {}
pub type _Unwind_Trace_Fn =
extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code;
extern "C" {
// No native _Unwind_Backtrace on iOS
#[cfg(not(all(target_os = "ios", target_arch = "arm")))]
pub fn _Unwind_Backtrace(
trace: _Unwind_Trace_Fn,
trace_argument: *mut c_void,
) -> _Unwind_Reason_Code;
// available since GCC 4.2.0, should be fine for our purpose
#[cfg(all(
not(all(target_os = "android", target_arch = "arm")),
not(all(target_os = "freebsd", target_arch = "arm")),
not(all(target_os = "linux", target_arch = "arm"))
))]
pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
#[cfg(all(
not(target_os = "android"),
not(all(target_os = "freebsd", target_arch = "arm")),
not(all(target_os = "linux", target_arch = "arm"))
))]
pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
}
// On android, the function _Unwind_GetIP is a macro, and this is the
// expansion of the macro. This is all copy/pasted directly from the
// header file with the definition of _Unwind_GetIP.
#[cfg(any(
all(target_os = "android", target_arch = "arm"),
all(target_os = "freebsd", target_arch = "arm"),
all(target_os = "linux", target_arch = "arm")
))]
pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
#[repr(C)]
enum _Unwind_VRS_Result {
_UVRSR_OK = 0,
_UVRSR_NOT_IMPLEMENTED = 1,
_UVRSR_FAILED = 2,
}
#[repr(C)]
enum _Unwind_VRS_RegClass {
_UVRSC_CORE = 0,
_UVRSC_VFP = 1,
_UVRSC_FPA = 2,
_UVRSC_WMMXD = 3,
_UVRSC_WMMXC = 4,
}
#[repr(C)]
enum _Unwind_VRS_DataRepresentation {
_UVRSD_UINT32 = 0,
_UVRSD_VFPX = 1,
_UVRSD_FPAX = 2,
_UVRSD_UINT64 = 3,
_UVRSD_FLOAT = 4,
_UVRSD_DOUBLE = 5,
}
type _Unwind_Word = libc::c_uint;
extern "C" {
fn _Unwind_VRS_Get(
ctx: *mut _Unwind_Context,
klass: _Unwind_VRS_RegClass,
word: _Unwind_Word,
repr: _Unwind_VRS_DataRepresentation,
data: *mut c_void,
) -> _Unwind_VRS_Result;
}
let mut val: _Unwind_Word = 0;
let ptr = &mut val as *mut _Unwind_Word;
let _ = _Unwind_VRS_Get(
ctx,
_Unwind_VRS_RegClass::_UVRSC_CORE,
15,
_Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
ptr as *mut c_void,
);
(val & !1) as libc::uintptr_t
}
// This function also doesn't exist on Android or ARM/Linux, so make it
// a no-op
#[cfg(any(
target_os = "android",
all(target_os = "freebsd", target_arch = "arm"),
all(target_os = "linux", target_arch = "arm")
))]
pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
pc
}
}

View File

@ -1,152 +0,0 @@
use core::ffi::c_void;
use core::fmt;
/// Inspects the current call-stack, passing all active frames into the closure
/// provided to calculate a stack trace.
///
/// This function is the workhorse of this library in calculating the stack
/// traces for a program. The given closure `cb` is yielded instances of a
/// `Frame` which represent information about that call frame on the stack. The
/// closure is yielded frames in a top-down fashion (most recently called
/// functions first).
///
/// The closure's return value is an indication of whether the backtrace should
/// continue. A return value of `false` will terminate the backtrace and return
/// immediately.
///
/// Once a `Frame` is acquired you will likely want to call `backtrace::resolve`
/// to convert the `ip` (instruction pointer) or symbol address to a `Symbol`
/// through which the name and/or filename/line number can be learned.
///
/// Note that this is a relatively low-level function and if you'd like to, for
/// example, capture a backtrace to be inspected later, then the `Backtrace`
/// type may be more appropriate.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
///
/// # Panics
///
/// This function strives to never panic, but if the `cb` provided panics then
/// some platforms will force a double panic to abort the process. Some
/// platforms use a C library which internally uses callbacks which cannot be
/// unwound through, so panicking from `cb` may trigger a process abort.
///
/// # Example
///
/// ```
/// extern crate backtrace;
///
/// fn main() {
/// backtrace::trace(|frame| {
/// // ...
///
/// true // continue the backtrace
/// });
/// }
/// ```
#[cfg(feature = "std")]
pub fn trace<F: FnMut(&Frame) -> bool>(cb: F) {
let _guard = crate::lock::lock();
unsafe { trace_unsynchronized(cb) }
}
/// Same as `trace`, only unsafe as it's unsynchronized.
///
/// This function does not have synchronization guarentees but is available
/// when the `std` feature of this crate isn't compiled in. See the `trace`
/// function for more documentation and examples.
///
/// # Panics
///
/// See information on `trace` for caveats on `cb` panicking.
pub unsafe fn trace_unsynchronized<F: FnMut(&Frame) -> bool>(mut cb: F) {
trace_imp(&mut cb)
}
/// A trait representing one frame of a backtrace, yielded to the `trace`
/// function of this crate.
///
/// The tracing function's closure will be yielded frames, and the frame is
/// virtually dispatched as the underlying implementation is not always known
/// until runtime.
#[derive(Clone)]
pub struct Frame {
pub(crate) inner: FrameImp,
}
impl Frame {
/// Returns the current instruction pointer of this frame.
///
/// This is normally the next instruction to execute in the frame, but not
/// all implementations list this with 100% accuracy (but it's generally
/// pretty close).
///
/// It is recommended to pass this value to `backtrace::resolve` to turn it
/// into a symbol name.
pub fn ip(&self) -> *mut c_void {
self.inner.ip()
}
/// Returns the starting symbol address of the frame of this function.
///
/// This will attempt to rewind the instruction pointer returned by `ip` to
/// the start of the function, returning that value. In some cases, however,
/// backends will just return `ip` from this function.
///
/// The returned value can sometimes be used if `backtrace::resolve` failed
/// on the `ip` given above.
pub fn symbol_address(&self) -> *mut c_void {
self.inner.symbol_address()
}
}
impl fmt::Debug for Frame {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Frame")
.field("ip", &self.ip())
.field("symbol_address", &self.symbol_address())
.finish()
}
}
cfg_if::cfg_if! {
if #[cfg(
any(
all(
unix,
not(target_os = "emscripten"),
not(all(target_os = "ios", target_arch = "arm")),
feature = "libunwind",
),
all(
target_env = "sgx",
target_vendor = "fortanix",
),
)
)] {
mod libunwind;
use self::libunwind::trace as trace_imp;
pub(crate) use self::libunwind::Frame as FrameImp;
} else if #[cfg(
all(
unix,
not(target_os = "emscripten"),
feature = "unix-backtrace",
)
)] {
mod unix_backtrace;
use self::unix_backtrace::trace as trace_imp;
pub(crate) use self::unix_backtrace::Frame as FrameImp;
} else if #[cfg(all(windows, feature = "dbghelp", not(target_vendor = "uwp")))] {
mod dbghelp;
use self::dbghelp::trace as trace_imp;
pub(crate) use self::dbghelp::Frame as FrameImp;
} else {
mod noop;
use self::noop::trace as trace_imp;
pub(crate) use self::noop::Frame as FrameImp;
}
}

View File

@ -1,20 +0,0 @@
//! Empty implementation of unwinding used when no other implementation is
//! appropriate.
use core::ffi::c_void;
#[inline(always)]
pub fn trace(_cb: &mut FnMut(&super::Frame) -> bool) {}
#[derive(Clone)]
pub struct Frame;
impl Frame {
pub fn ip(&self) -> *mut c_void {
0 as *mut _
}
pub fn symbol_address(&self) -> *mut c_void {
0 as *mut _
}
}

View File

@ -1,61 +0,0 @@
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Unwinding through the `backtrace` function provided in Unix.
//!
//! This is an alternative unwinding strategy for Unix platforms which don't
//! have support for libunwind but do have support for `backtrace`. Currently
//! there's not a whole lot of those though. This module is a relatively
//! straightforward binding of the `backtrace` API to the `Frame` API that we'd
//! like to have.
use core::ffi::c_void;
use core::mem;
use libc::c_int;
#[derive(Clone)]
pub struct Frame {
addr: usize,
}
impl Frame {
pub fn ip(&self) -> *mut c_void {
self.addr as *mut c_void
}
pub fn symbol_address(&self) -> *mut c_void {
self.ip()
}
}
extern "C" {
fn backtrace(buf: *mut *mut c_void, sz: c_int) -> c_int;
}
#[inline(always)]
pub unsafe fn trace(cb: &mut FnMut(&super::Frame) -> bool) {
const SIZE: usize = 100;
let mut buf: [*mut c_void; SIZE];
let cnt;
buf = mem::zeroed();
cnt = backtrace(buf.as_mut_ptr(), SIZE as c_int);
for addr in buf[..cnt as usize].iter() {
let cx = super::Frame {
inner: Frame {
addr: *addr as usize,
},
};
if !cb(&cx) {
return;
}
}
}

View File

@ -1,479 +0,0 @@
use crate::PrintFmt;
use crate::{resolve, resolve_frame, trace, BacktraceFmt, Symbol, SymbolName};
use std::ffi::c_void;
use std::fmt;
use std::path::{Path, PathBuf};
use std::prelude::v1::*;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Representation of an owned and self-contained backtrace.
///
/// This structure can be used to capture a backtrace at various points in a
/// program and later used to inspect what the backtrace was at that time.
///
/// `Backtrace` supports pretty-printing of backtraces through its `Debug`
/// implementation.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
#[derive(Clone)]
#[cfg_attr(feature = "serialize-rustc", derive(RustcDecodable, RustcEncodable))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Backtrace {
// Frames here are listed from top-to-bottom of the stack
frames: Vec<BacktraceFrame>,
// The index we believe is the actual start of the backtrace, omitting
// frames like `Backtrace::new` and `backtrace::trace`.
actual_start_index: usize,
}
fn _assert_send_sync() {
fn _assert<T: Send + Sync>() {}
_assert::<Backtrace>();
}
/// Captured version of a frame in a backtrace.
///
/// This type is returned as a list from `Backtrace::frames` and represents one
/// stack frame in a captured backtrace.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
#[derive(Clone)]
pub struct BacktraceFrame {
frame: Frame,
symbols: Option<Vec<BacktraceSymbol>>,
}
#[derive(Clone)]
enum Frame {
Raw(crate::Frame),
#[allow(dead_code)]
Deserialized {
ip: usize,
symbol_address: usize,
},
}
impl Frame {
fn ip(&self) -> *mut c_void {
match *self {
Frame::Raw(ref f) => f.ip(),
Frame::Deserialized { ip, .. } => ip as *mut c_void,
}
}
fn symbol_address(&self) -> *mut c_void {
match *self {
Frame::Raw(ref f) => f.symbol_address(),
Frame::Deserialized { symbol_address, .. } => symbol_address as *mut c_void,
}
}
}
/// Captured version of a symbol in a backtrace.
///
/// This type is returned as a list from `BacktraceFrame::symbols` and
/// represents the metadata for a symbol in a backtrace.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
#[derive(Clone)]
#[cfg_attr(feature = "serialize-rustc", derive(RustcDecodable, RustcEncodable))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct BacktraceSymbol {
name: Option<Vec<u8>>,
addr: Option<usize>,
filename: Option<PathBuf>,
lineno: Option<u32>,
}
impl Backtrace {
/// Captures a backtrace at the callsite of this function, returning an
/// owned representation.
///
/// This function is useful for representing a backtrace as an object in
/// Rust. This returned value can be sent across threads and printed
/// elsewhere, and the purpose of this value is to be entirely self
/// contained.
///
/// Note that on some platforms acquiring a full backtrace and resolving it
/// can be extremely expensive. If the cost is too much for your application
/// it's recommended to instead use `Backtrace::new_unresolved()` which
/// avoids the symbol resolution step (which typically takes the longest)
/// and allows deferring that to a later date.
///
/// # Examples
///
/// ```
/// use backtrace::Backtrace;
///
/// let current_backtrace = Backtrace::new();
/// ```
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
#[inline(never)] // want to make sure there's a frame here to remove
pub fn new() -> Backtrace {
let mut bt = Self::create(Self::new as usize);
bt.resolve();
bt
}
/// Similar to `new` except that this does not resolve any symbols, this
/// simply captures the backtrace as a list of addresses.
///
/// At a later time the `resolve` function can be called to resolve this
/// backtrace's symbols into readable names. This function exists because
/// the resolution process can sometimes take a significant amount of time
/// whereas any one backtrace may only be rarely printed.
///
/// # Examples
///
/// ```
/// use backtrace::Backtrace;
///
/// let mut current_backtrace = Backtrace::new_unresolved();
/// println!("{:?}", current_backtrace); // no symbol names
/// current_backtrace.resolve();
/// println!("{:?}", current_backtrace); // symbol names now present
/// ```
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
#[inline(never)] // want to make sure there's a frame here to remove
pub fn new_unresolved() -> Backtrace {
Self::create(Self::new_unresolved as usize)
}
fn create(ip: usize) -> Backtrace {
let mut frames = Vec::new();
let mut actual_start_index = None;
trace(|frame| {
frames.push(BacktraceFrame {
frame: Frame::Raw(frame.clone()),
symbols: None,
});
if frame.symbol_address() as usize == ip && actual_start_index.is_none() {
actual_start_index = Some(frames.len());
}
true
});
Backtrace {
frames,
actual_start_index: actual_start_index.unwrap_or(0),
}
}
/// Returns the frames from when this backtrace was captured.
///
/// The first entry of this slice is likely the function `Backtrace::new`,
/// and the last frame is likely something about how this thread or the main
/// function started.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
pub fn frames(&self) -> &[BacktraceFrame] {
&self.frames[self.actual_start_index..]
}
/// If this backtrace was created from `new_unresolved` then this function
/// will resolve all addresses in the backtrace to their symbolic names.
///
/// If this backtrace has been previously resolved or was created through
/// `new`, this function does nothing.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
pub fn resolve(&mut self) {
for frame in self.frames.iter_mut().filter(|f| f.symbols.is_none()) {
let mut symbols = Vec::new();
{
let sym = |symbol: &Symbol| {
symbols.push(BacktraceSymbol {
name: symbol.name().map(|m| m.as_bytes().to_vec()),
addr: symbol.addr().map(|a| a as usize),
filename: symbol.filename().map(|m| m.to_owned()),
lineno: symbol.lineno(),
});
};
match frame.frame {
Frame::Raw(ref f) => resolve_frame(f, sym),
Frame::Deserialized { ip, .. } => {
resolve(ip as *mut c_void, sym);
}
}
}
frame.symbols = Some(symbols);
}
}
}
impl From<Vec<BacktraceFrame>> for Backtrace {
fn from(frames: Vec<BacktraceFrame>) -> Self {
Backtrace {
frames,
actual_start_index: 0,
}
}
}
impl Into<Vec<BacktraceFrame>> for Backtrace {
fn into(self) -> Vec<BacktraceFrame> {
self.frames
}
}
impl BacktraceFrame {
/// Same as `Frame::ip`
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
pub fn ip(&self) -> *mut c_void {
self.frame.ip() as *mut c_void
}
/// Same as `Frame::symbol_address`
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
pub fn symbol_address(&self) -> *mut c_void {
self.frame.symbol_address() as *mut c_void
}
/// Returns the list of symbols that this frame corresponds to.
///
/// Normally there is only one symbol per frame, but sometimes if a number
/// of functions are inlined into one frame then multiple symbols will be
/// returned. The first symbol listed is the "innermost function", whereas
/// the last symbol is the outermost (last caller).
///
/// Note that if this frame came from an unresolved backtrace then this will
/// return an empty list.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
pub fn symbols(&self) -> &[BacktraceSymbol] {
self.symbols.as_ref().map(|s| &s[..]).unwrap_or(&[])
}
}
impl BacktraceSymbol {
/// Same as `Symbol::name`
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
pub fn name(&self) -> Option<SymbolName> {
self.name.as_ref().map(|s| SymbolName::new(s))
}
/// Same as `Symbol::addr`
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
pub fn addr(&self) -> Option<*mut c_void> {
self.addr.map(|s| s as *mut c_void)
}
/// Same as `Symbol::filename`
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
pub fn filename(&self) -> Option<&Path> {
self.filename.as_ref().map(|p| &**p)
}
/// Same as `Symbol::lineno`
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
pub fn lineno(&self) -> Option<u32> {
self.lineno
}
}
impl fmt::Debug for Backtrace {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let full = fmt.alternate();
let (frames, style) = if full {
(&self.frames[..], PrintFmt::Full)
} else {
(&self.frames[self.actual_start_index..], PrintFmt::Short)
};
// When printing paths we try to strip the cwd if it exists, otherwise
// we just print the path as-is. Note that we also only do this for the
// short format, because if it's full we presumably want to print
// everything.
let cwd = std::env::current_dir();
let mut print_path = move |fmt: &mut fmt::Formatter, path: crate::BytesOrWideString| {
let path = path.into_path_buf();
if !full {
if let Ok(cwd) = &cwd {
if let Ok(suffix) = path.strip_prefix(cwd) {
return fmt::Display::fmt(&suffix.display(), fmt);
}
}
}
fmt::Display::fmt(&path.display(), fmt)
};
let mut f = BacktraceFmt::new(fmt, style, &mut print_path);
f.add_context()?;
for frame in frames {
f.frame().backtrace_frame(frame)?;
}
f.finish()?;
Ok(())
}
}
impl Default for Backtrace {
fn default() -> Backtrace {
Backtrace::new()
}
}
impl fmt::Debug for BacktraceFrame {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("BacktraceFrame")
.field("ip", &self.ip())
.field("symbol_address", &self.symbol_address())
.finish()
}
}
impl fmt::Debug for BacktraceSymbol {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("BacktraceSymbol")
.field("name", &self.name())
.field("addr", &self.addr())
.field("filename", &self.filename())
.field("lineno", &self.lineno())
.finish()
}
}
#[cfg(feature = "serialize-rustc")]
mod rustc_serialize_impls {
use super::*;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
#[derive(RustcEncodable, RustcDecodable)]
struct SerializedFrame {
ip: usize,
symbol_address: usize,
symbols: Option<Vec<BacktraceSymbol>>,
}
impl Decodable for BacktraceFrame {
fn decode<D>(d: &mut D) -> Result<Self, D::Error>
where
D: Decoder,
{
let frame: SerializedFrame = SerializedFrame::decode(d)?;
Ok(BacktraceFrame {
frame: Frame::Deserialized {
ip: frame.ip,
symbol_address: frame.symbol_address,
},
symbols: frame.symbols,
})
}
}
impl Encodable for BacktraceFrame {
fn encode<E>(&self, e: &mut E) -> Result<(), E::Error>
where
E: Encoder,
{
let BacktraceFrame { frame, symbols } = self;
SerializedFrame {
ip: frame.ip() as usize,
symbol_address: frame.symbol_address() as usize,
symbols: symbols.clone(),
}
.encode(e)
}
}
}
#[cfg(feature = "serde")]
mod serde_impls {
extern crate serde;
use self::serde::de::Deserializer;
use self::serde::ser::Serializer;
use self::serde::{Deserialize, Serialize};
use super::*;
#[derive(Serialize, Deserialize)]
struct SerializedFrame {
ip: usize,
symbol_address: usize,
symbols: Option<Vec<BacktraceSymbol>>,
}
impl Serialize for BacktraceFrame {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let BacktraceFrame { frame, symbols } = self;
SerializedFrame {
ip: frame.ip() as usize,
symbol_address: frame.symbol_address() as usize,
symbols: symbols.clone(),
}
.serialize(s)
}
}
impl<'a> Deserialize<'a> for BacktraceFrame {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
let frame: SerializedFrame = SerializedFrame::deserialize(d)?;
Ok(BacktraceFrame {
frame: Frame::Deserialized {
ip: frame.ip,
symbol_address: frame.symbol_address,
},
symbols: frame.symbols,
})
}
}
}

View File

@ -1,370 +0,0 @@
//! A module to assist in managing dbghelp bindings on Windows
//!
//! Backtraces on Windows (at least for MSVC) are largely powered through
//! `dbghelp.dll` and the various functions that it contains. These functions
//! are currently loaded *dynamically* rather than linking to `dbghelp.dll`
//! statically. This is currently done by the standard library (and is in theory
//! required there), but is an effort to help reduce the static dll dependencies
//! of a library since backtraces are typically pretty optional. That being
//! said, `dbghelp.dll` almost always successfully loads on Windows.
//!
//! Note though that since we're loading all this support dynamically we can't
//! actually use the raw definitions in `winapi`, but rather we need to define
//! the function pointer types ourselves and use that. We don't really want to
//! be in the business of duplicating winapi, so we have a Cargo feature
//! `verify-winapi` which asserts that all bindings match those in winapi and
//! this feature is enabled on CI.
//!
//! Finally, you'll note here that the dll for `dbghelp.dll` is never unloaded,
//! and that's currently intentional. The thinking is that we can globally cache
//! it and use it between calls to the API, avoiding expensive loads/unloads. If
//! this is a problem for leak detectors or something like that we can cross the
//! bridge when we get there.
#![allow(non_snake_case)]
use crate::windows::*;
use core::mem;
use core::ptr;
// Work around `SymGetOptions` and `SymSetOptions` not being present in winapi
// itself. Otherwise this is only used when we're double-checking types against
// winapi.
#[cfg(feature = "verify-winapi")]
mod dbghelp {
use crate::windows::*;
pub use winapi::um::dbghelp::{
StackWalk64, SymCleanup, SymFromAddrW, SymFunctionTableAccess64, SymGetLineFromAddrW64,
SymGetModuleBase64, SymInitializeW,
};
extern "system" {
// Not defined in winapi yet
pub fn SymGetOptions() -> u32;
pub fn SymSetOptions(_: u32);
// This is defined in winapi, but it's incorrect (FIXME winapi-rs#768)
pub fn StackWalkEx(
MachineType: DWORD,
hProcess: HANDLE,
hThread: HANDLE,
StackFrame: LPSTACKFRAME_EX,
ContextRecord: PVOID,
ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64,
FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64,
GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64,
TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64,
Flags: DWORD,
) -> BOOL;
// Not defined in winapi yet
pub fn SymFromInlineContextW(
hProcess: HANDLE,
Address: DWORD64,
InlineContext: ULONG,
Displacement: PDWORD64,
Symbol: PSYMBOL_INFOW,
) -> BOOL;
pub fn SymGetLineFromInlineContextW(
hProcess: HANDLE,
dwAddr: DWORD64,
InlineContext: ULONG,
qwModuleBaseAddress: DWORD64,
pdwDisplacement: PDWORD,
Line: PIMAGEHLP_LINEW64,
) -> BOOL;
}
pub fn assert_equal_types<T>(a: T, _b: T) -> T {
a
}
}
// This macro is used to define a `Dbghelp` structure which internally contains
// all the function pointers that we might load.
macro_rules! dbghelp {
(extern "system" {
$(fn $name:ident($($arg:ident: $argty:ty),*) -> $ret: ty;)*
}) => (
pub struct Dbghelp {
/// The loaded DLL for `dbghelp.dll`
dll: HMODULE,
// Each function pointer for each function we might use
$($name: usize,)*
}
static mut DBGHELP: Dbghelp = Dbghelp {
// Initially we haven't loaded the DLL
dll: 0 as *mut _,
// Initiall all functions are set to zero to say they need to be
// dynamically loaded.
$($name: 0,)*
};
// Convenience typedef for each function type.
$(pub type $name = unsafe extern "system" fn($($argty),*) -> $ret;)*
impl Dbghelp {
/// Attempts to open `dbghelp.dll`. Returns success if it works or
/// error if `LoadLibraryW` fails.
///
/// Panics if library is already loaded.
fn ensure_open(&mut self) -> Result<(), ()> {
if !self.dll.is_null() {
return Ok(())
}
let lib = b"dbghelp.dll\0";
unsafe {
self.dll = LoadLibraryA(lib.as_ptr() as *const i8);
if self.dll.is_null() {
Err(())
} else {
Ok(())
}
}
}
// Function for each method we'd like to use. When called it will
// either read the cached function pointer or load it and return the
// loaded value. Loads are asserted to succeed.
$(pub fn $name(&mut self) -> Option<$name> {
unsafe {
if self.$name == 0 {
let name = concat!(stringify!($name), "\0");
self.$name = self.symbol(name.as_bytes())?;
}
let ret = mem::transmute::<usize, $name>(self.$name);
#[cfg(feature = "verify-winapi")]
dbghelp::assert_equal_types(ret, dbghelp::$name);
Some(ret)
}
})*
fn symbol(&self, symbol: &[u8]) -> Option<usize> {
unsafe {
match GetProcAddress(self.dll, symbol.as_ptr() as *const _) as usize {
0 => None,
n => Some(n),
}
}
}
}
// Convenience proxy to use the cleanup locks to reference dbghelp
// functions.
#[allow(dead_code)]
impl Init {
$(pub fn $name(&self) -> $name {
unsafe {
DBGHELP.$name().unwrap()
}
})*
pub fn dbghelp(&self) -> *mut Dbghelp {
unsafe {
&mut DBGHELP
}
}
}
)
}
const SYMOPT_DEFERRED_LOADS: DWORD = 0x00000004;
dbghelp! {
extern "system" {
fn SymGetOptions() -> DWORD;
fn SymSetOptions(options: DWORD) -> ();
fn SymInitializeW(
handle: HANDLE,
path: PCWSTR,
invade: BOOL
) -> BOOL;
fn SymCleanup(handle: HANDLE) -> BOOL;
fn StackWalk64(
MachineType: DWORD,
hProcess: HANDLE,
hThread: HANDLE,
StackFrame: LPSTACKFRAME64,
ContextRecord: PVOID,
ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64,
FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64,
GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64,
TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64
) -> BOOL;
fn SymFunctionTableAccess64(
hProcess: HANDLE,
AddrBase: DWORD64
) -> PVOID;
fn SymGetModuleBase64(
hProcess: HANDLE,
AddrBase: DWORD64
) -> DWORD64;
fn SymFromAddrW(
hProcess: HANDLE,
Address: DWORD64,
Displacement: PDWORD64,
Symbol: PSYMBOL_INFOW
) -> BOOL;
fn SymGetLineFromAddrW64(
hProcess: HANDLE,
dwAddr: DWORD64,
pdwDisplacement: PDWORD,
Line: PIMAGEHLP_LINEW64
) -> BOOL;
fn StackWalkEx(
MachineType: DWORD,
hProcess: HANDLE,
hThread: HANDLE,
StackFrame: LPSTACKFRAME_EX,
ContextRecord: PVOID,
ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64,
FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64,
GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64,
TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64,
Flags: DWORD
) -> BOOL;
fn SymFromInlineContextW(
hProcess: HANDLE,
Address: DWORD64,
InlineContext: ULONG,
Displacement: PDWORD64,
Symbol: PSYMBOL_INFOW
) -> BOOL;
fn SymGetLineFromInlineContextW(
hProcess: HANDLE,
dwAddr: DWORD64,
InlineContext: ULONG,
qwModuleBaseAddress: DWORD64,
pdwDisplacement: PDWORD,
Line: PIMAGEHLP_LINEW64
) -> BOOL;
}
}
pub struct Init {
lock: HANDLE,
}
/// Initialize all support necessary to access `dbghelp` API functions from this
/// crate.
///
/// Note that this function is **safe**, it internally has its own
/// synchronization. Also note that it is safe to call this function multiple
/// times recursively.
#[cfg(all(windows, feature = "dbghelp"))]
pub fn init() -> Result<Init, ()> {
use core::sync::atomic::{AtomicUsize, Ordering::SeqCst};
unsafe {
// First thing we need to do is to synchronize this function. This can
// be called concurrently from other threads or recursively within one
// thread. Note that it's trickier than that though because what we're
// using here, `dbghelp`, *also* needs to be synchronized with all other
// callers to `dbghelp` in this process.
//
// Typically there aren't really that many calls to `dbghelp` within the
// same process and we can probably safely assume that we're the only
// ones accessing it. There is, however, one primary other user we have
// to worry about which is ironically ourselves, but in the standard
// library. The Rust standard library depends on this crate for
// backtrace support, and this crate also exists on crates.io. This
// means that if the standard library is printing a panic backtrace it
// may race with this crate coming from crates.io, causing segfaults.
//
// To help solve this synchronization problem we employ a
// Windows-specific trick here (it is, after all, a Windows-specific
// restriction about synchronization). We create a *session-local* named
// mutex to protect this call. The intention here is that the standard
// library and this crate don't have to share Rust-level APIs to
// synchronize here but can instead work behind the scenes to make sure
// they're synchronizing with one another. That way when this function
// is called through the standard library or through crates.io we can be
// sure that the same mutex is being acquired.
//
// So all of that is to say that the first thing we do here is we
// atomically create a `HANDLE` which is a named mutex on Windows. We
// synchronize a bit with other threads sharing this function
// specifically and ensure that only one handle is created per instance
// of this function. Note that the handle is never closed once it's
// stored in the global.
//
// After we've actually go the lock we simply acquire it, and our `Init`
// handle we hand out will be responsible for dropping it eventually.
static LOCK: AtomicUsize = AtomicUsize::new(0);
let mut lock = LOCK.load(SeqCst);
if lock == 0 {
lock = CreateMutexA(
ptr::null_mut(),
0,
"Local\\RustBacktraceMutex\0".as_ptr() as _,
) as usize;
if lock == 0 {
return Err(());
}
if let Err(other) = LOCK.compare_exchange(0, lock, SeqCst, SeqCst) {
debug_assert!(other != 0);
CloseHandle(lock as HANDLE);
lock = other;
}
}
debug_assert!(lock != 0);
let lock = lock as HANDLE;
let r = WaitForSingleObjectEx(lock, INFINITE, FALSE);
debug_assert_eq!(r, 0);
let ret = Init { lock };
// Ok, phew! Now that we're all safely synchronized, let's actually
// start processing everything. First up we need to ensure that
// `dbghelp.dll` is actually loaded in this process. We do this
// dynamically to avoid a static dependency. This has historically been
// done to work around weird linking issues and is intended at making
// binaries a bit more portable since this is largely just a debugging
// utility.
//
// Once we've opened `dbghelp.dll` we need to call some initialization
// functions in it, and that's detailed more below. We only do this
// once, though, so we've got a global boolean indicating whether we're
// done yet or not.
DBGHELP.ensure_open()?;
static mut INITIALIZED: bool = false;
if INITIALIZED {
return Ok(ret);
}
let orig = DBGHELP.SymGetOptions().unwrap()();
// Ensure that the `SYMOPT_DEFERRED_LOADS` flag is set, because
// according to MSVC's own docs about this: "This is the fastest, most
// efficient way to use the symbol handler.", so let's do that!
DBGHELP.SymSetOptions().unwrap()(orig | SYMOPT_DEFERRED_LOADS);
// Actually initialize symbols with MSVC. Note that this can fail, but we
// ignore it. There's not a ton of prior art for this per se, but LLVM
// internally seems to ignore the return value here and one of the
// sanitizer libraries in LLVM prints a scary warning if this fails but
// basically ignores it in the long run.
//
// One case this comes up a lot for Rust is that the standard library and
// this crate on crates.io both want to compete for `SymInitializeW`. The
// standard library historically wanted to initialize then cleanup most of
// the time, but now that it's using this crate it means that someone will
// get to initialization first and the other will pick up that
// initialization.
DBGHELP.SymInitializeW().unwrap()(GetCurrentProcess(), ptr::null_mut(), TRUE);
INITIALIZED = true;
Ok(ret)
}
}
impl Drop for Init {
fn drop(&mut self) {
unsafe {
let r = ReleaseMutex(self.lock);
debug_assert!(r != 0);
}
}
}

View File

@ -1,159 +0,0 @@
//! A library for acquiring a backtrace at runtime
//!
//! This library is meant to supplement the `RUST_BACKTRACE=1` support of the
//! standard library by allowing an acquisition of a backtrace at runtime
//! programmatically. The backtraces generated by this library do not need to be
//! parsed, for example, and expose the functionality of multiple backend
//! implementations.
//!
//! # Implementation
//!
//! This library makes use of a number of strategies for actually acquiring a
//! backtrace. For example unix uses libgcc's libunwind bindings by default to
//! acquire a backtrace, but coresymbolication or dladdr is used on OSX to
//! acquire symbol names while linux uses gcc's libbacktrace.
//!
//! When using the default feature set of this library the "most reasonable" set
//! of defaults is chosen for the current platform, but the features activated
//! can also be controlled at a finer granularity.
//!
//! # API Principles
//!
//! This library attempts to be as flexible as possible to accommodate different
//! backend implementations of acquiring a backtrace. Consequently the currently
//! exported functions are closure-based as opposed to the likely expected
//! iterator-based versions. This is done due to limitations of the underlying
//! APIs used from the system.
//!
//! # Usage
//!
//! First, add this to your Cargo.toml
//!
//! ```toml
//! [dependencies]
//! backtrace = "0.3"
//! ```
//!
//! Next:
//!
//! ```
//! extern crate backtrace;
//!
//! fn main() {
//! # // Unsafe here so test passes on no_std.
//! # #[cfg(feature = "std")] {
//! backtrace::trace(|frame| {
//! let ip = frame.ip();
//! let symbol_address = frame.symbol_address();
//!
//! // Resolve this instruction pointer to a symbol name
//! backtrace::resolve_frame(frame, |symbol| {
//! if let Some(name) = symbol.name() {
//! // ...
//! }
//! if let Some(filename) = symbol.filename() {
//! // ...
//! }
//! });
//!
//! true // keep going to the next frame
//! });
//! }
//! # }
//! ```
#![doc(html_root_url = "https://docs.rs/backtrace")]
#![deny(missing_docs)]
#![no_std]
#![cfg_attr(
all(feature = "std", target_env = "sgx", target_vendor = "fortanix"),
feature(sgx_platform)
)]
#![allow(bare_trait_objects)] // TODO: remove when updating to 2018 edition
#![allow(rust_2018_idioms)] // TODO: remove when updating to 2018 edition
#[cfg(feature = "std")]
#[macro_use]
extern crate std;
pub use crate::backtrace::{trace_unsynchronized, Frame};
mod backtrace;
pub use crate::symbolize::resolve_frame_unsynchronized;
pub use crate::symbolize::{resolve_unsynchronized, Symbol, SymbolName};
mod symbolize;
pub use crate::types::BytesOrWideString;
mod types;
#[cfg(feature = "std")]
pub use crate::symbolize::clear_symbol_cache;
mod print;
pub use print::{BacktraceFmt, BacktraceFrameFmt, PrintFmt};
cfg_if::cfg_if! {
if #[cfg(feature = "std")] {
pub use crate::backtrace::trace;
pub use crate::symbolize::{resolve, resolve_frame};
pub use crate::capture::{Backtrace, BacktraceFrame, BacktraceSymbol};
mod capture;
}
}
#[allow(dead_code)]
struct Bomb {
enabled: bool,
}
#[allow(dead_code)]
impl Drop for Bomb {
fn drop(&mut self) {
if self.enabled {
panic!("cannot panic during the backtrace function");
}
}
}
#[allow(dead_code)]
#[cfg(feature = "std")]
mod lock {
use std::boxed::Box;
use std::cell::Cell;
use std::sync::{Mutex, MutexGuard, Once};
pub struct LockGuard(Option<MutexGuard<'static, ()>>);
static mut LOCK: *mut Mutex<()> = 0 as *mut _;
static INIT: Once = Once::new();
thread_local!(static LOCK_HELD: Cell<bool> = Cell::new(false));
impl Drop for LockGuard {
fn drop(&mut self) {
if self.0.is_some() {
LOCK_HELD.with(|slot| {
assert!(slot.get());
slot.set(false);
});
}
}
}
pub fn lock() -> LockGuard {
if LOCK_HELD.with(|l| l.get()) {
return LockGuard(None);
}
LOCK_HELD.with(|s| s.set(true));
unsafe {
INIT.call_once(|| {
LOCK = Box::into_raw(Box::new(Mutex::new(())));
});
LockGuard(Some((*LOCK).lock().unwrap()))
}
}
}
#[cfg(all(windows, feature = "dbghelp", not(target_vendor = "uwp")))]
mod dbghelp;
#[cfg(windows)]
mod windows;

View File

@ -1,267 +0,0 @@
use crate::BytesOrWideString;
use core::ffi::c_void;
use core::fmt;
const HEX_WIDTH: usize = 2 + 2 * core::mem::size_of::<usize>();
#[cfg(target_os = "fuchsia")]
mod fuchsia;
/// A formatter for backtraces.
///
/// This type can be used to print a backtrace regardless of where the backtrace
/// itself comes from. If you have a `Backtrace` type then its `Debug`
/// implementation already uses this printing format.
pub struct BacktraceFmt<'a, 'b> {
fmt: &'a mut fmt::Formatter<'b>,
frame_index: usize,
format: PrintFmt,
print_path: &'a mut (FnMut(&mut fmt::Formatter, BytesOrWideString) -> fmt::Result + 'b),
}
/// The styles of printing that we can print
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum PrintFmt {
/// Prints a terser backtrace which ideally only contains relevant information
Short,
/// Prints a backtrace that contains all possible information
Full,
#[doc(hidden)]
__Nonexhaustive,
}
impl<'a, 'b> BacktraceFmt<'a, 'b> {
/// Create a new `BacktraceFmt` which will write output to the provided
/// `fmt`.
///
/// The `format` argument will control the style in which the backtrace is
/// printed, and the `print_path` argument will be used to print the
/// `BytesOrWideString` instances of filenames. This type itself doesn't do
/// any printing of filenames, but this callback is required to do so.
pub fn new(
fmt: &'a mut fmt::Formatter<'b>,
format: PrintFmt,
print_path: &'a mut (FnMut(&mut fmt::Formatter, BytesOrWideString) -> fmt::Result + 'b),
) -> Self {
BacktraceFmt {
fmt,
frame_index: 0,
format,
print_path,
}
}
/// Prints a preamble for the backtrace about to be printed.
///
/// This is required on some platforms for backtraces to be fully
/// sumbolicated later, and otherwise this should just be the first method
/// you call after creating a `BacktraceFmt`.
pub fn add_context(&mut self) -> fmt::Result {
#[cfg(target_os = "fuchsia")]
fuchsia::print_dso_context(self.fmt)?;
Ok(())
}
/// Adds a frame to the backtrace output.
///
/// This commit returns an RAII instance of a `BacktraceFrameFmt` which can be used
/// to actually print a frame, and on destruction it will increment the
/// frame counter.
pub fn frame(&mut self) -> BacktraceFrameFmt<'_, 'a, 'b> {
BacktraceFrameFmt {
fmt: self,
symbol_index: 0,
}
}
/// Completes the backtrace output.
///
/// This is currently a no-op but is added for future compatibility with
/// backtrace formats.
pub fn finish(&mut self) -> fmt::Result {
// Currently a no-op-- including this hook to allow for future additions.
Ok(())
}
}
/// A formatter for just one frame of a backtrace.
///
/// This type is created by the `BacktraceFmt::frame` function.
pub struct BacktraceFrameFmt<'fmt, 'a, 'b> {
fmt: &'fmt mut BacktraceFmt<'a, 'b>,
symbol_index: usize,
}
impl BacktraceFrameFmt<'_, '_, '_> {
/// Prints a `BacktraceFrame` with this frame formatter.
///
/// This will recusrively print all `BacktraceSymbol` instances within the
/// `BacktraceFrame`.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
#[cfg(feature = "std")]
pub fn backtrace_frame(&mut self, frame: &crate::BacktraceFrame) -> fmt::Result {
let symbols = frame.symbols();
for symbol in symbols {
self.backtrace_symbol(frame, symbol)?;
}
if symbols.is_empty() {
self.print_raw(frame.ip(), None, None, None)?;
}
Ok(())
}
/// Prints a `BacktraceSymbol` within a `BacktraceFrame`.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
#[cfg(feature = "std")]
pub fn backtrace_symbol(
&mut self,
frame: &crate::BacktraceFrame,
symbol: &crate::BacktraceSymbol,
) -> fmt::Result {
self.print_raw(
frame.ip(),
symbol.name(),
// TODO: this isn't great that we don't end up printing anything
// with non-utf8 filenames. Thankfully almost everything is utf8 so
// this shouldn't be too too bad.
symbol
.filename()
.and_then(|p| Some(BytesOrWideString::Bytes(p.to_str()?.as_bytes()))),
symbol.lineno(),
)?;
Ok(())
}
/// Prints a raw traced `Frame` and `Symbol`, typically from within the raw
/// callbacks of this crate.
pub fn symbol(&mut self, frame: &crate::Frame, symbol: &crate::Symbol) -> fmt::Result {
self.print_raw(
frame.ip(),
symbol.name(),
symbol.filename_raw(),
symbol.lineno(),
)?;
Ok(())
}
/// Adds a raw frame to the backtrace output.
///
/// This method, unlike the previous, takes the raw arguments in case
/// they're being source from different locations. Note that this may be
/// called multiple times for one frame.
pub fn print_raw(
&mut self,
frame_ip: *mut c_void,
symbol_name: Option<crate::SymbolName>,
filename: Option<BytesOrWideString>,
lineno: Option<u32>,
) -> fmt::Result {
// Fuchsia is unable to symbolize within a process so it has a special
// format which can be used to symbolize later. Print that instead of
// printing addresses in our own format here.
if cfg!(target_os = "fuchsia") {
self.print_raw_fuchsia(frame_ip)?;
} else {
self.print_raw_generic(frame_ip, symbol_name, filename, lineno)?;
}
self.symbol_index += 1;
Ok(())
}
#[allow(unused_mut)]
fn print_raw_generic(
&mut self,
mut frame_ip: *mut c_void,
symbol_name: Option<crate::SymbolName>,
filename: Option<BytesOrWideString>,
lineno: Option<u32>,
) -> fmt::Result {
// No need to print "null" frames, it basically just means that the
// system backtrace was a bit eager to trace back super far.
if let PrintFmt::Short = self.fmt.format {
if frame_ip.is_null() {
return Ok(());
}
}
// To reduce TCB size in Sgx enclave, we do not want to implement symbol
// resolution functionality. Rather, we can print the offset of the
// address here, which could be later mapped to correct function.
#[cfg(all(feature = "std", target_env = "sgx", target_vendor = "fortanix"))]
{
let image_base = std::os::fortanix_sgx::mem::image_base();
frame_ip = usize::wrapping_sub(frame_ip as usize, image_base as _) as _;
}
// Print the index of the frame as well as the optional instruction
// pointer of the frame. If we're beyond the first symbol of this frame
// though we just print appropriate whitespace.
if self.symbol_index == 0 {
write!(self.fmt.fmt, "{:4}: ", self.fmt.frame_index)?;
if let PrintFmt::Full = self.fmt.format {
write!(self.fmt.fmt, "{:1$?} - ", frame_ip, HEX_WIDTH)?;
}
} else {
write!(self.fmt.fmt, " ")?;
if let PrintFmt::Full = self.fmt.format {
write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH + 3)?;
}
}
// Next up write out the symbol name, using the alternate formatting for
// more information if we're a full backtrace. Here we also handle
// symbols which don't have a name,
match (symbol_name, &self.fmt.format) {
(Some(name), PrintFmt::Short) => write!(self.fmt.fmt, "{:#}", name)?,
(Some(name), PrintFmt::Full) => write!(self.fmt.fmt, "{}", name)?,
(None, _) | (_, PrintFmt::__Nonexhaustive) => write!(self.fmt.fmt, "<unknown>")?,
}
self.fmt.fmt.write_str("\n")?;
// And last up, print out the filename/line number if they're available.
if let (Some(file), Some(line)) = (filename, lineno) {
self.print_fileline(file, line)?;
}
Ok(())
}
fn print_fileline(&mut self, file: BytesOrWideString, line: u32) -> fmt::Result {
// Filename/line are printed on lines under the symbol name, so print
// some appropriate whitespace to sort of right-align ourselves.
if let PrintFmt::Full = self.fmt.format {
write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH)?;
}
write!(self.fmt.fmt, " at ")?;
// Delegate to our internal callback to print the filename and then
// print out the line number.
(self.fmt.print_path)(self.fmt.fmt, file)?;
write!(self.fmt.fmt, ":{}\n", line)?;
Ok(())
}
fn print_raw_fuchsia(&mut self, frame_ip: *mut c_void) -> fmt::Result {
// We only care about the first symbol of a frame
if self.symbol_index == 0 {
self.fmt.fmt.write_str("{{{bt:")?;
write!(self.fmt.fmt, "{}:{:?}", self.fmt.frame_index, frame_ip)?;
self.fmt.fmt.write_str("}}}\n")?;
}
Ok(())
}
}
impl Drop for BacktraceFrameFmt<'_, '_, '_> {
fn drop(&mut self) {
self.fmt.frame_index += 1;
}
}

View File

@ -1,432 +0,0 @@
use core::fmt::{self, Write};
use core::mem::{size_of, transmute};
use core::slice::from_raw_parts;
use libc::c_char;
extern "C" {
// dl_iterate_phdr takes a callback that will receive a dl_phdr_info pointer
// for every DSO that has been linked into the process. dl_iterate_phdr also
// ensures that the dynamic linker is locked from start to finish of the
// iteration. If the callback returns a non-zero value the iteration is
// terminated early. 'data' will be passed as the third argument to the
// callback on each call. 'size' gives the size of the dl_phdr_info.
#[allow(improper_ctypes)]
fn dl_iterate_phdr(
f: extern "C" fn(info: &dl_phdr_info, size: usize, data: &mut DsoPrinter) -> i32,
data: &mut DsoPrinter,
) -> i32;
}
// We need to parse out the build ID and some basic program header data
// which means that we need a bit of stuff from the ELF spec as well.
const PT_LOAD: u32 = 1;
const PT_NOTE: u32 = 4;
// Now we have to replicate, bit for bit, the structure of the dl_phdr_info
// type used by fuchsia's current dynamic linker. Chromium also has this ABI
// boundary as well as crashpad. Eventully we'd like to move these cases to
// use elf-search but we'd need to provide that in the SDK and that has not
// yet been done. Thus we (and they) are stuck having to use this method
// which incurs a tight coupling with the fuchsia libc.
#[allow(non_camel_case_types)]
#[repr(C)]
struct dl_phdr_info {
addr: *const u8,
name: *const c_char,
phdr: *const Elf_Phdr,
phnum: u16,
adds: u64,
subs: u64,
tls_modid: usize,
tls_data: *const u8,
}
impl dl_phdr_info {
fn program_headers(&self) -> PhdrIter<'_> {
PhdrIter {
phdrs: self.phdr_slice(),
base: self.addr,
}
}
// We have no way of knowing of checking if e_phoff and e_phnum are valid.
// libc should ensure this for us however so it's safe to form a slice here.
fn phdr_slice(&self) -> &[Elf_Phdr] {
unsafe { from_raw_parts(self.phdr, self.phnum as usize) }
}
}
struct PhdrIter<'a> {
phdrs: &'a [Elf_Phdr],
base: *const u8,
}
impl<'a> Iterator for PhdrIter<'a> {
type Item = Phdr<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.phdrs.split_first().map(|(phdr, new_phdrs)| {
self.phdrs = new_phdrs;
Phdr {
phdr,
base: self.base,
}
})
}
}
// Elf_Phdr represents a 64-bit ELF program header in the endianness of the target
// architecture.
#[allow(non_camel_case_types)]
#[derive(Clone, Debug)]
#[repr(C)]
struct Elf_Phdr {
p_type: u32,
p_flags: u32,
p_offset: u64,
p_vaddr: u64,
p_paddr: u64,
p_filesz: u64,
p_memsz: u64,
p_align: u64,
}
// Phdr represents a valid ELF program header and its contents.
struct Phdr<'a> {
phdr: &'a Elf_Phdr,
base: *const u8,
}
impl<'a> Phdr<'a> {
// We have no way of checking if p_addr or p_memsz are valid. Fuchsia's libc
// parses the notes first however so by virtue of being here these headers
// must be valid. NoteIter does not require the underlying data to be valid
// but it does require the bounds to be valid. We trust that libc has ensured
// that this is the case for us here.
fn notes(&self) -> NoteIter<'a> {
unsafe {
NoteIter::new(
self.base.add(self.phdr.p_offset as usize),
self.phdr.p_memsz as usize,
)
}
}
}
// The note type for build IDs.
const NT_GNU_BUILD_ID: u32 = 3;
// Elf_Nhdr represents an ELF note header in the endianness of the target.
#[allow(non_camel_case_types)]
#[repr(C)]
struct Elf_Nhdr {
n_namesz: u32,
n_descsz: u32,
n_type: u32,
}
// Note represents an ELF note (header + contents). The name is left as a u8
// slice because it is not always null terminated and rust makes it easy enough
// to check that the bytes match eitherway.
struct Note<'a> {
name: &'a [u8],
desc: &'a [u8],
tipe: u32,
}
// NoteIter lets you safely iterate over a note segment. It terminates as soon
// as an error occurs or there are no more notes. If you iterate over invalid
// data it will function as though no notes were found.
struct NoteIter<'a> {
base: &'a [u8],
error: bool,
}
impl<'a> NoteIter<'a> {
// It is an invariant of function that the pointer and size given denote a
// valid range of bytes that can all be read. The contents of these bytes
// can be anything but the range must be valid for this to be safe.
unsafe fn new(base: *const u8, size: usize) -> Self {
NoteIter {
base: from_raw_parts(base, size),
error: false,
}
}
}
// align_to aligns 'x' to 'to'-byte alignment assuming 'to' is a power of 2.
// This follows a standard pattern in C/C++ ELF parsing code where
// (x + to - 1) & -to is used. Rust does not let you negate usize so I use
// 2's-complement conversion to recreate that.
fn align_to(x: usize, to: usize) -> usize {
(x + to - 1) & (!to + 1)
}
// take_bytes_align4 consumes num bytes from the slice (if present) and
// additionally ensures that the final slice is properlly aligned. If an
// either the number of bytes requested is too large or the slice can't be
// realigned afterwards due to not enough remaining bytes existing, None is
// returned and the slice is not modified.
fn take_bytes_align4<'a>(num: usize, bytes: &mut &'a [u8]) -> Option<&'a [u8]> {
if bytes.len() < align_to(num, 4) {
return None;
}
let (out, bytes_new) = bytes.split_at(num);
*bytes = &bytes_new[align_to(num, 4) - num..];
Some(out)
}
// This function has no real invariants the caller must uphold other than
// perhaps that 'bytes' should be aligned for performance (and on some
// architectures correctness). The values in the Elf_Nhdr fields might
// be nonsense but this function ensures no such thing.
fn take_nhdr<'a>(bytes: &mut &'a [u8]) -> Option<&'a Elf_Nhdr> {
if size_of::<Elf_Nhdr>() > bytes.len() {
return None;
}
// This is safe as long as there is enough space and we just confirmed that
// in the if statement above so this should not be unsafe.
let out = unsafe { transmute::<*const u8, &'a Elf_Nhdr>(bytes.as_ptr()) };
// Note that sice_of::<Elf_Nhdr>() is always 4-byte aligned.
*bytes = &bytes[size_of::<Elf_Nhdr>()..];
Some(out)
}
impl<'a> Iterator for NoteIter<'a> {
type Item = Note<'a>;
fn next(&mut self) -> Option<Self::Item> {
// Check if we've reached the end.
if self.base.len() == 0 || self.error {
return None;
}
// We transmute out an nhdr but we carefully consider the resulting
// struct. We don't trust the namesz or descsz and we make no unsafe
// decisions based on the type. So even if we get out complete garbage
// we should still be safe.
let nhdr = take_nhdr(&mut self.base)?;
let name = take_bytes_align4(nhdr.n_namesz as usize, &mut self.base)?;
let desc = take_bytes_align4(nhdr.n_descsz as usize, &mut self.base)?;
Some(Note {
name: name,
desc: desc,
tipe: nhdr.n_type,
})
}
}
struct Perm(u32);
/// Indicates that a segment is executable.
const PERM_X: u32 = 0b00000001;
/// Indicates that a segment is writable.
const PERM_W: u32 = 0b00000010;
/// Indicates that a segment is readable.
const PERM_R: u32 = 0b00000100;
impl core::fmt::Display for Perm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = self.0;
if v & PERM_R != 0 {
f.write_char('r')?
}
if v & PERM_W != 0 {
f.write_char('w')?
}
if v & PERM_X != 0 {
f.write_char('x')?
}
Ok(())
}
}
/// Represents an ELF segment at runtime.
struct Segment {
/// Gives the runtime virtual address of this segment's contents.
addr: usize,
/// Gives the memory size of this segment's contents.
size: usize,
/// Gives the module virtual address of this segment with the ELF file.
mod_rel_addr: usize,
/// Gives the permissions found in the ELF file. These permissions are not
/// necessarily the permissions present at runtime however.
flags: Perm,
}
/// Lets one iterate over Segments from a DSO.
struct SegmentIter<'a> {
phdrs: &'a [Elf_Phdr],
base: usize,
}
impl Iterator for SegmentIter<'_> {
type Item = Segment;
fn next(&mut self) -> Option<Self::Item> {
self.phdrs.split_first().and_then(|(phdr, new_phdrs)| {
self.phdrs = new_phdrs;
if phdr.p_type != PT_LOAD {
self.next()
} else {
Some(Segment {
addr: phdr.p_vaddr as usize + self.base,
size: phdr.p_memsz as usize,
mod_rel_addr: phdr.p_vaddr as usize,
flags: Perm(phdr.p_flags),
})
}
})
}
}
/// Represents an ELF DSO (Dynamic Shared Object). This type references
/// the data stored in the actual DSO rather than making its own copy.
struct Dso<'a> {
/// The dynamic linker always gives us a name, even if the name is empty.
/// In the case of the main executable this name will be empty. In the case
/// of a shared object it will be the soname (see DT_SONAME).
name: &'a str,
/// On Fuchsia virtually all binaries have build IDs but this is not a strict
/// requierment. There's no way to match up DSO information with a real ELF
/// file afterwards if there is no build_id so we require that every DSO
/// have one here. DSO's without a build_id are ignored.
build_id: &'a [u8],
base: usize,
phdrs: &'a [Elf_Phdr],
}
impl Dso<'_> {
/// Returns an iterator over Segments in this DSO.
fn segments(&self) -> SegmentIter<'_> {
SegmentIter {
phdrs: self.phdrs.as_ref(),
base: self.base,
}
}
}
struct HexSlice<'a> {
bytes: &'a [u8],
}
impl fmt::Display for HexSlice<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for byte in self.bytes {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
}
fn get_build_id<'a>(info: &'a dl_phdr_info) -> Option<&'a [u8]> {
for phdr in info.program_headers() {
if phdr.phdr.p_type == PT_NOTE {
for note in phdr.notes() {
if note.tipe == NT_GNU_BUILD_ID && (note.name == b"GNU\0" || note.name == b"GNU") {
return Some(note.desc);
}
}
}
}
None
}
/// These errors encode issues that arise while parsing information about
/// each DSO.
enum Error {
/// NameError means that an error occurred while converting a C style string
/// into a rust string.
NameError(core::str::Utf8Error),
/// BuildIDError means that we didn't find a build ID. This could either be
/// because the DSO had no build ID or because the segment containing the
/// build ID was malformed.
BuildIDError,
}
/// Calls either 'dso' or 'error' for each DSO linked into the process by the
/// dynamic linker.
///
/// # Arguments
///
/// * `visitor` - A DsoPrinter that will have one of eats methods called foreach DSO.
fn for_each_dso(mut visitor: &mut DsoPrinter) {
extern "C" fn callback(info: &dl_phdr_info, _size: usize, visitor: &mut DsoPrinter) -> i32 {
// dl_iterate_phdr ensures that info.name will point to a valid
// location.
let name_len = unsafe { libc::strlen(info.name) };
let name_slice: &[u8] =
unsafe { core::slice::from_raw_parts(info.name as *const u8, name_len) };
let name = match core::str::from_utf8(name_slice) {
Ok(name) => name,
Err(err) => {
return visitor.error(Error::NameError(err)) as i32;
}
};
let build_id = match get_build_id(info) {
Some(build_id) => build_id,
None => {
return visitor.error(Error::BuildIDError) as i32;
}
};
visitor.dso(Dso {
name: name,
build_id: build_id,
phdrs: info.phdr_slice(),
base: info.addr as usize,
}) as i32
}
unsafe { dl_iterate_phdr(callback, &mut visitor) };
}
struct DsoPrinter<'a, 'b> {
writer: &'a mut core::fmt::Formatter<'b>,
module_count: usize,
error: core::fmt::Result,
}
impl DsoPrinter<'_, '_> {
fn dso(&mut self, dso: Dso<'_>) -> bool {
let mut write = || {
write!(
self.writer,
"{{{{{{module:{:#x}:{}:elf:{}}}}}}}\n",
self.module_count,
dso.name,
HexSlice {
bytes: dso.build_id.as_ref()
}
)?;
for seg in dso.segments() {
write!(
self.writer,
"{{{{{{mmap:{:#x}:{:#x}:load:{:#x}:{}:{:#x}}}}}}}\n",
seg.addr, seg.size, self.module_count, seg.flags, seg.mod_rel_addr
)?;
}
self.module_count += 1;
Ok(())
};
match write() {
Ok(()) => false,
Err(err) => {
self.error = Err(err);
true
}
}
}
fn error(&mut self, _error: Error) -> bool {
false
}
}
/// This function prints the Fuchsia symbolizer markup for all information contained in a DSO.
pub fn print_dso_context(out: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
out.write_str("{{{reset}}}\n")?;
let mut visitor = DsoPrinter {
writer: out,
module_count: 0,
error: Ok(()),
};
for_each_dso(&mut visitor);
visitor.error
}

View File

@ -1,277 +0,0 @@
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Symbolication strategy that's OSX-specific and uses the `CoreSymbolication`
//! framework, if possible.
//!
//! This strategy uses internal private APIs that are somewhat undocumented but
//! seem to be widely used on OSX. This is the default symbolication strategy
//! for OSX, but is turned off in release builds for iOS due to reports of apps
//! being rejected due to using these APIs.
//!
//! This would probably be good to get official one day and not using private
//! APIs, but for now it should largely suffice.
//!
//! Note that this module will dynamically load `CoreSymbolication` and its APIs
//! through dlopen/dlsym, and if the loading fails this falls back to `dladdr`
//! as a symbolication strategy.
#![allow(bad_style)]
use crate::symbolize::dladdr;
use crate::symbolize::ResolveWhat;
use crate::types::BytesOrWideString;
use crate::SymbolName;
use core::ffi::c_void;
use core::mem;
use core::ptr;
use core::slice;
use libc::{c_char, c_int};
#[repr(C)]
#[derive(Copy, Clone, PartialEq)]
pub struct CSTypeRef {
cpp_data: *const c_void,
cpp_obj: *const c_void,
}
const CS_NOW: u64 = 0x80000000;
const CSREF_NULL: CSTypeRef = CSTypeRef {
cpp_data: 0 as *const c_void,
cpp_obj: 0 as *const c_void,
};
pub enum Symbol<'a> {
Core {
path: *const c_char,
lineno: u32,
name: *const c_char,
addr: *mut c_void,
},
Dladdr(dladdr::Symbol<'a>),
}
impl Symbol<'_> {
pub fn name(&self) -> Option<SymbolName> {
let name = match *self {
Symbol::Core { name, .. } => name,
Symbol::Dladdr(ref info) => return info.name(),
};
if name.is_null() {
None
} else {
Some(SymbolName::new(unsafe {
let len = libc::strlen(name);
slice::from_raw_parts(name as *const u8, len)
}))
}
}
pub fn addr(&self) -> Option<*mut c_void> {
match *self {
Symbol::Core { addr, .. } => Some(addr),
Symbol::Dladdr(ref info) => info.addr(),
}
}
fn filename_bytes(&self) -> Option<&[u8]> {
match *self {
Symbol::Core { path, .. } => {
if path.is_null() {
None
} else {
Some(unsafe {
let len = libc::strlen(path);
slice::from_raw_parts(path as *const u8, len)
})
}
}
Symbol::Dladdr(_) => None,
}
}
pub fn filename_raw(&self) -> Option<BytesOrWideString> {
self.filename_bytes().map(BytesOrWideString::Bytes)
}
#[cfg(feature = "std")]
pub fn filename(&self) -> Option<&::std::path::Path> {
use std::ffi::OsStr;
use std::os::unix::prelude::*;
use std::path::Path;
self.filename_bytes().map(OsStr::from_bytes).map(Path::new)
}
pub fn lineno(&self) -> Option<u32> {
match *self {
Symbol::Core { lineno: 0, .. } => None,
Symbol::Core { lineno, .. } => Some(lineno),
Symbol::Dladdr(_) => None,
}
}
}
macro_rules! coresymbolication {
(#[load_path = $path:tt] extern "C" {
$(fn $name:ident($($arg:ident: $argty:ty),*) -> $ret: ty;)*
}) => (
pub struct CoreSymbolication {
// The loaded dynamic library
dll: *mut c_void,
// Each function pointer for each function we might use
$($name: usize,)*
}
static mut CORESYMBOLICATION: CoreSymbolication = CoreSymbolication {
// Initially we haven't loaded the dynamic library
dll: 0 as *mut _,
// Initiall all functions are set to zero to say they need to be
// dynamically loaded.
$($name: 0,)*
};
// Convenience typedef for each function type.
$(pub type $name = unsafe extern "C" fn($($argty),*) -> $ret;)*
impl CoreSymbolication {
/// Attempts to open `dbghelp.dll`. Returns `true` if it works or
/// `false` if `dlopen` fails.
fn open(&mut self) -> bool {
if !self.dll.is_null() {
return true;
}
let lib = concat!($path, "\0").as_bytes();
unsafe {
self.dll = libc::dlopen(lib.as_ptr() as *const _, libc::RTLD_LAZY);
!self.dll.is_null()
}
}
// Function for each method we'd like to use. When called it will
// either read the cached function pointer or load it and return the
// loaded value. Loads are asserted to succeed.
$(pub fn $name(&mut self) -> $name {
unsafe {
if self.$name == 0 {
let name = concat!(stringify!($name), "\0");
self.$name = self.symbol(name.as_bytes())
.expect(concat!("symbol ", stringify!($name), " is missing"));
}
mem::transmute::<usize, $name>(self.$name)
}
})*
fn symbol(&self, symbol: &[u8]) -> Option<usize> {
unsafe {
match libc::dlsym(self.dll, symbol.as_ptr() as *const _) as usize {
0 => None,
n => Some(n),
}
}
}
}
)
}
coresymbolication! {
#[load_path = "/System/Library/PrivateFrameworks/CoreSymbolication.framework\
/Versions/A/CoreSymbolication"]
extern "C" {
fn CSSymbolicatorCreateWithPid(pid: c_int) -> CSTypeRef;
fn CSRelease(rf: CSTypeRef) -> ();
fn CSSymbolicatorGetSymbolWithAddressAtTime(
cs: CSTypeRef, addr: *const c_void, time: u64) -> CSTypeRef;
fn CSSymbolicatorGetSourceInfoWithAddressAtTime(
cs: CSTypeRef, addr: *const c_void, time: u64) -> CSTypeRef;
fn CSSourceInfoGetLineNumber(info: CSTypeRef) -> c_int;
fn CSSourceInfoGetPath(info: CSTypeRef) -> *const c_char;
fn CSSourceInfoGetSymbol(info: CSTypeRef) -> CSTypeRef;
fn CSSymbolGetMangledName(sym: CSTypeRef) -> *const c_char;
fn CSSymbolGetSymbolOwner(sym: CSTypeRef) -> CSTypeRef;
fn CSSymbolOwnerGetBaseAddress(symowner: CSTypeRef) -> *mut c_void;
}
}
unsafe fn try_resolve(addr: *mut c_void, cb: &mut FnMut(&super::Symbol)) -> bool {
// Note that this is externally synchronized so there's no need for
// synchronization here, making this `static mut` safer.
let lib = &mut CORESYMBOLICATION;
if !lib.open() {
return false;
}
let cs = lib.CSSymbolicatorCreateWithPid()(libc::getpid());
if cs == CSREF_NULL {
return false;
}
let _dtor = OwnedCSTypeRef {
ptr: cs,
CSRelease: lib.CSRelease(),
};
let info = lib.CSSymbolicatorGetSourceInfoWithAddressAtTime()(cs, addr, CS_NOW);
let sym = if info == CSREF_NULL {
lib.CSSymbolicatorGetSymbolWithAddressAtTime()(cs, addr, CS_NOW)
} else {
lib.CSSourceInfoGetSymbol()(info)
};
if sym == CSREF_NULL {
return false;
}
let owner = lib.CSSymbolGetSymbolOwner()(sym);
if owner == CSREF_NULL {
return false;
}
cb(&super::Symbol {
inner: Symbol::Core {
path: if info != CSREF_NULL {
lib.CSSourceInfoGetPath()(info)
} else {
ptr::null()
},
lineno: if info != CSREF_NULL {
lib.CSSourceInfoGetLineNumber()(info) as u32
} else {
0
},
name: lib.CSSymbolGetMangledName()(sym),
addr: lib.CSSymbolOwnerGetBaseAddress()(owner),
},
});
true
}
struct OwnedCSTypeRef {
ptr: CSTypeRef,
CSRelease: unsafe extern "C" fn(CSTypeRef),
}
impl Drop for OwnedCSTypeRef {
fn drop(&mut self) {
unsafe {
(self.CSRelease)(self.ptr);
}
}
}
pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) {
let addr = what.address_or_ip();
if try_resolve(addr, cb) {
return;
}
dladdr::resolve(addr, &mut |sym| {
cb(&super::Symbol {
inner: Symbol::Dladdr(sym),
})
})
}

View File

@ -1,226 +0,0 @@
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Symbolication strategy using `dbghelp.dll` on Windows, only used for MSVC
//!
//! This symbolication strategy, like with backtraces, uses dynamically loaded
//! information from `dbghelp.dll`. (see `src/dbghelp.rs` for info about why
//! it's dynamically loaded).
//!
//! This API selects its resolution strategy based on the frame provided or the
//! information we have at hand. If a frame from `StackWalkEx` is given to us
//! then we use similar APIs to generate correct information about inlined
//! functions. Otherwise if all we have is an address or an older stack frame
//! from `StackWalk64` we use the older APIs for symbolication.
//!
//! There's a good deal of support in this module, but a good chunk of it is
//! converting back and forth between Windows types and Rust types. For example
//! symbols come to us as wide strings which we then convert to utf-8 strings if
//! we can.
#![allow(bad_style)]
use crate::backtrace::FrameImp as Frame;
use crate::dbghelp;
use crate::symbolize::ResolveWhat;
use crate::types::BytesOrWideString;
use crate::windows::*;
use crate::SymbolName;
use core::char;
use core::ffi::c_void;
use core::marker;
use core::mem;
use core::slice;
// Store an OsString on std so we can provide the symbol name and filename.
pub struct Symbol<'a> {
name: *const [u8],
addr: *mut c_void,
line: Option<u32>,
filename: Option<*const [u16]>,
#[cfg(feature = "std")]
_filename_cache: Option<::std::ffi::OsString>,
#[cfg(not(feature = "std"))]
_filename_cache: (),
_marker: marker::PhantomData<&'a i32>,
}
impl Symbol<'_> {
pub fn name(&self) -> Option<SymbolName> {
Some(SymbolName::new(unsafe { &*self.name }))
}
pub fn addr(&self) -> Option<*mut c_void> {
Some(self.addr as *mut _)
}
pub fn filename_raw(&self) -> Option<BytesOrWideString> {
self.filename
.map(|slice| unsafe { BytesOrWideString::Wide(&*slice) })
}
pub fn lineno(&self) -> Option<u32> {
self.line
}
#[cfg(feature = "std")]
pub fn filename(&self) -> Option<&::std::path::Path> {
use std::path::Path;
self._filename_cache.as_ref().map(Path::new)
}
}
#[repr(C, align(8))]
struct Aligned8<T>(T);
pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) {
// Ensure this process's symbols are initialized
let dbghelp = match dbghelp::init() {
Ok(dbghelp) => dbghelp,
Err(()) => return, // oh well...
};
match what {
ResolveWhat::Address(_) => resolve_without_inline(&dbghelp, what.address_or_ip(), cb),
ResolveWhat::Frame(frame) => match &frame.inner {
Frame::New(frame) => resolve_with_inline(&dbghelp, frame, cb),
Frame::Old(_) => resolve_without_inline(&dbghelp, frame.ip(), cb),
},
}
}
unsafe fn resolve_with_inline(
dbghelp: &dbghelp::Init,
frame: &STACKFRAME_EX,
cb: &mut FnMut(&super::Symbol),
) {
do_resolve(
|info| {
dbghelp.SymFromInlineContextW()(
GetCurrentProcess(),
super::adjust_ip(frame.AddrPC.Offset as *mut _) as u64,
frame.InlineFrameContext,
&mut 0,
info,
)
},
|line| {
dbghelp.SymGetLineFromInlineContextW()(
GetCurrentProcess(),
super::adjust_ip(frame.AddrPC.Offset as *mut _) as u64,
frame.InlineFrameContext,
0,
&mut 0,
line,
)
},
cb,
)
}
unsafe fn resolve_without_inline(
dbghelp: &dbghelp::Init,
addr: *mut c_void,
cb: &mut FnMut(&super::Symbol),
) {
do_resolve(
|info| dbghelp.SymFromAddrW()(GetCurrentProcess(), addr as DWORD64, &mut 0, info),
|line| dbghelp.SymGetLineFromAddrW64()(GetCurrentProcess(), addr as DWORD64, &mut 0, line),
cb,
)
}
unsafe fn do_resolve(
sym_from_addr: impl FnOnce(*mut SYMBOL_INFOW) -> BOOL,
get_line_from_addr: impl FnOnce(&mut IMAGEHLP_LINEW64) -> BOOL,
cb: &mut FnMut(&super::Symbol),
) {
const SIZE: usize = 2 * MAX_SYM_NAME + mem::size_of::<SYMBOL_INFOW>();
let mut data = Aligned8([0u8; SIZE]);
let data = &mut data.0;
let info = &mut *(data.as_mut_ptr() as *mut SYMBOL_INFOW);
info.MaxNameLen = MAX_SYM_NAME as ULONG;
// the struct size in C. the value is different to
// `size_of::<SYMBOL_INFOW>() - MAX_SYM_NAME + 1` (== 81)
// due to struct alignment.
info.SizeOfStruct = 88;
if sym_from_addr(info) != TRUE {
return;
}
// If the symbol name is greater than MaxNameLen, SymFromAddrW will
// give a buffer of (MaxNameLen - 1) characters and set NameLen to
// the real value.
let name_len = ::core::cmp::min(info.NameLen as usize, info.MaxNameLen as usize - 1);
let name_ptr = info.Name.as_ptr() as *const u16;
let name = slice::from_raw_parts(name_ptr, name_len);
// Reencode the utf-16 symbol to utf-8 so we can use `SymbolName::new` like
// all other platforms
let mut name_len = 0;
let mut name_buffer = [0; 256];
{
let mut remaining = &mut name_buffer[..];
for c in char::decode_utf16(name.iter().cloned()) {
let c = c.unwrap_or(char::REPLACEMENT_CHARACTER);
let len = c.len_utf8();
if len < remaining.len() {
c.encode_utf8(remaining);
let tmp = remaining;
remaining = &mut tmp[len..];
name_len += len;
} else {
break;
}
}
}
let name = &name_buffer[..name_len] as *const [u8];
let mut line = mem::zeroed::<IMAGEHLP_LINEW64>();
line.SizeOfStruct = mem::size_of::<IMAGEHLP_LINEW64>() as DWORD;
let mut filename = None;
let mut lineno = None;
if get_line_from_addr(&mut line) == TRUE {
lineno = Some(line.LineNumber as u32);
let base = line.FileName;
let mut len = 0;
while *base.offset(len) != 0 {
len += 1;
}
let len = len as usize;
filename = Some(slice::from_raw_parts(base, len) as *const [u16]);
}
cb(&super::Symbol {
inner: Symbol {
name,
addr: info.Address as *mut _,
line: lineno,
filename,
_filename_cache: cache(filename),
_marker: marker::PhantomData,
},
})
}
#[cfg(feature = "std")]
unsafe fn cache(filename: Option<*const [u16]>) -> Option<::std::ffi::OsString> {
use std::os::windows::ffi::OsStringExt;
filename.map(|f| ::std::ffi::OsString::from_wide(&*f))
}
#[cfg(not(feature = "std"))]
unsafe fn cache(_filename: Option<*const [u16]>) {}

View File

@ -1,112 +0,0 @@
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Common support for resolving with `dladdr`, often used as a fallback if
//! other strategies don't work.
#![allow(dead_code)]
cfg_if::cfg_if! {
if #[cfg(all(unix, not(target_os = "emscripten"), feature = "dladdr"))] {
use core::ffi::c_void;
use core::marker;
use core::{mem, slice};
use crate::SymbolName;
use crate::types::BytesOrWideString;
use libc::{self, Dl_info};
pub struct Symbol<'a> {
inner: Dl_info,
_marker: marker::PhantomData<&'a i32>,
}
impl Symbol<'_> {
pub fn name(&self) -> Option<SymbolName> {
if self.inner.dli_sname.is_null() {
None
} else {
let ptr = self.inner.dli_sname as *const u8;
unsafe {
let len = libc::strlen(self.inner.dli_sname);
Some(SymbolName::new(slice::from_raw_parts(ptr, len)))
}
}
}
pub fn addr(&self) -> Option<*mut c_void> {
Some(self.inner.dli_saddr as *mut _)
}
pub fn filename_raw(&self) -> Option<BytesOrWideString> {
None
}
#[cfg(feature = "std")]
pub fn filename(&self) -> Option<&::std::path::Path> {
None
}
pub fn lineno(&self) -> Option<u32> {
None
}
}
pub unsafe fn resolve(addr: *mut c_void, cb: &mut FnMut(Symbol<'static>)) {
let mut info = Symbol {
inner: mem::zeroed(),
_marker: marker::PhantomData,
};
// Skip null addresses to avoid calling into libc and having it do
// things with the dynamic symbol table for no reason.
if !addr.is_null() && libc::dladdr(addr as *mut _, &mut info.inner) != 0 {
cb(info)
}
}
} else {
use core::ffi::c_void;
use core::marker;
use crate::symbolize::SymbolName;
use crate::types::BytesOrWideString;
pub struct Symbol<'a> {
a: Void,
_b: marker::PhantomData<&'a i32>,
}
enum Void {}
impl Symbol<'_> {
pub fn name(&self) -> Option<SymbolName> {
match self.a {}
}
pub fn addr(&self) -> Option<*mut c_void> {
match self.a {}
}
pub fn filename_raw(&self) -> Option<BytesOrWideString> {
match self.a {}
}
#[cfg(feature = "std")]
pub fn filename(&self) -> Option<&::std::path::Path> {
match self.a {}
}
pub fn lineno(&self) -> Option<u32> {
match self.a {}
}
}
pub unsafe fn resolve(addr: *mut c_void, cb: &mut FnMut(Symbol<'static>)) {
drop((addr, cb));
}
}
}

View File

@ -1,50 +0,0 @@
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Symbolication strategy using `dladdr`
//!
//! The `dladdr` API is available on most Unix implementations but it's quite
//! basic, not handling inline frame information at all. Since it's so prevalent
//! though we have an option to use it!
use crate::symbolize::{dladdr, ResolveWhat, SymbolName};
use crate::types::BytesOrWideString;
use core::ffi::c_void;
pub struct Symbol<'a>(dladdr::Symbol<'a>);
impl Symbol<'_> {
pub fn name(&self) -> Option<SymbolName> {
self.0.name()
}
pub fn addr(&self) -> Option<*mut c_void> {
self.0.addr()
}
pub fn filename_raw(&self) -> Option<BytesOrWideString> {
self.0.filename_raw()
}
#[cfg(feature = "std")]
pub fn filename(&self) -> Option<&::std::path::Path> {
self.0.filename()
}
pub fn lineno(&self) -> Option<u32> {
self.0.lineno()
}
}
pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) {
dladdr::resolve(what.address_or_ip(), &mut |sym| {
cb(&super::Symbol { inner: Symbol(sym) })
});
}

View File

@ -1,676 +0,0 @@
//! Support for symbolication using the `gimli` crate on crates.io
//!
//! This implementation is largely a work in progress and is off by default for
//! all platforms, but it's hoped to be developed over time! Long-term this is
//! intended to wholesale replace the `libbacktrace.rs` implementation.
use self::gimli::read::EndianSlice;
use self::gimli::LittleEndian as Endian;
use crate::symbolize::dladdr;
use crate::symbolize::ResolveWhat;
use crate::types::BytesOrWideString;
use crate::SymbolName;
use addr2line::gimli;
use core::mem;
use core::u32;
use findshlibs::{self, Segment, SharedLibrary};
use libc::c_void;
use memmap::Mmap;
use std::env;
use std::ffi::OsString;
use std::fs::File;
use std::path::Path;
use std::prelude::v1::*;
const MAPPINGS_CACHE_SIZE: usize = 4;
struct Context<'a> {
dwarf: addr2line::Context<EndianSlice<'a, Endian>>,
object: Object<'a>,
}
struct Mapping {
// 'static lifetime is a lie to hack around lack of support for self-referential structs.
cx: Context<'static>,
_map: Mmap,
}
fn cx<'data>(object: Object<'data>) -> Option<Context<'data>> {
fn load_section<'data, S>(obj: &Object<'data>) -> S
where
S: gimli::Section<gimli::EndianSlice<'data, Endian>>,
{
let data = obj.section(S::section_name()).unwrap_or(&[]);
S::from(EndianSlice::new(data, Endian))
}
let dwarf = addr2line::Context::from_sections(
load_section(&object),
load_section(&object),
load_section(&object),
load_section(&object),
load_section(&object),
load_section(&object),
load_section(&object),
load_section(&object),
load_section(&object),
gimli::EndianSlice::new(&[], Endian),
)
.ok()?;
Some(Context { dwarf, object })
}
fn assert_lifetimes<'a>(_: &'a Mmap, _: &Context<'a>) {}
macro_rules! mk {
(Mapping { $map:expr, $inner:expr }) => {{
assert_lifetimes(&$map, &$inner);
Mapping {
// Convert to 'static lifetimes since the symbols should
// only borrow `map` and we're preserving `map` below.
cx: unsafe { mem::transmute::<Context<'_>, Context<'static>>($inner) },
_map: $map,
}
}};
}
fn mmap(path: &Path) -> Option<Mmap> {
let file = File::open(path).ok()?;
// TODO: not completely safe, see https://github.com/danburkert/memmap-rs/issues/25
unsafe { Mmap::map(&file).ok() }
}
cfg_if::cfg_if! {
if #[cfg(windows)] {
use goblin::pe::{self, PE};
use goblin::strtab::Strtab;
use std::cmp;
use std::convert::TryFrom;
struct Object<'a> {
pe: PE<'a>,
data: &'a [u8],
symbols: Vec<(usize, pe::symbol::Symbol)>,
strtab: Strtab<'a>,
}
impl<'a> Object<'a> {
fn parse(data: &'a [u8]) -> Option<Object<'a>> {
let pe = PE::parse(data).ok()?;
let syms = pe.header.coff_header.symbols(data).ok()?;
let strtab = pe.header.coff_header.strings(data).ok()?;
// Collect all the symbols into a local vector which is sorted
// by address and contains enough data to learn about the symbol
// name. Note that we only look at function symbols and also
// note that the sections are 1-indexed because the zero section
// is special (apparently).
let mut symbols = Vec::new();
for (_, _, sym) in syms.iter() {
if sym.derived_type() != pe::symbol::IMAGE_SYM_DTYPE_FUNCTION
|| sym.section_number == 0
{
continue;
}
let addr = usize::try_from(sym.value).ok()?;
let section = pe.sections.get(usize::try_from(sym.section_number).ok()? - 1)?;
let va = usize::try_from(section.virtual_address).ok()?;
symbols.push((addr + va + pe.image_base, sym));
}
symbols.sort_unstable_by_key(|x| x.0);
Some(Object { pe, data, symbols, strtab })
}
fn section(&self, name: &str) -> Option<&'a [u8]> {
let section = self.pe
.sections
.iter()
.find(|section| section.name().ok() == Some(name));
section
.and_then(|section| {
let offset = section.pointer_to_raw_data as usize;
let size = cmp::min(section.virtual_size, section.size_of_raw_data) as usize;
self.data.get(offset..).and_then(|data| data.get(..size))
})
}
fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
// Note that unlike other formats COFF doesn't embed the size of
// each symbol. As a last ditch effort search for the *closest*
// symbol to a particular address and return that one. This gets
// really wonky once symbols start getting removed because the
// symbols returned here can be totally incorrect, but we have
// no idea of knowing how to detect that.
let addr = usize::try_from(addr).ok()?;
let i = match self.symbols.binary_search_by_key(&addr, |p| p.0) {
Ok(i) => i,
// typically `addr` isn't in the array, but `i` is where
// we'd insert it, so the previous position must be the
// greatest less than `addr`
Err(i) => i.checked_sub(1)?,
};
Some(self.symbols[i].1.name(&self.strtab).ok()?.as_bytes())
}
}
} else if #[cfg(target_os = "macos")] {
use goblin::mach::MachO;
struct Object<'a> {
macho: MachO<'a>,
dwarf: Option<usize>,
syms: Vec<(&'a str, u64)>,
}
impl<'a> Object<'a> {
fn parse(macho: MachO<'a>) -> Option<Object<'a>> {
if !macho.little_endian {
return None;
}
let dwarf = macho
.segments
.iter()
.enumerate()
.find(|(_, segment)| segment.name().ok() == Some("__DWARF"))
.map(|p| p.0);
let mut syms = Vec::new();
if let Some(s) = &macho.symbols {
syms = s.iter()
.filter_map(|e| e.ok())
.filter(|(name, nlist)| name.len() > 0 && !nlist.is_undefined())
.map(|(name, nlist)| (name, nlist.n_value))
.collect();
}
syms.sort_unstable_by_key(|(_, addr)| *addr);
Some(Object { macho, dwarf, syms })
}
fn section(&self, name: &str) -> Option<&'a [u8]> {
let dwarf = self.dwarf?;
let dwarf = &self.macho.segments[dwarf];
dwarf
.into_iter()
.filter_map(|s| s.ok())
.find(|(section, _data)| {
let section_name = match section.name() {
Ok(s) => s,
Err(_) => return false,
};
&section_name[..] == name || {
section_name.starts_with("__")
&& name.starts_with(".")
&& &section_name[2..] == &name[1..]
}
})
.map(|p| p.1)
}
fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
let i = match self.syms.binary_search_by_key(&addr, |(_, addr)| *addr) {
Ok(i) => i,
Err(i) => i.checked_sub(1)?,
};
let (sym, _addr) = self.syms.get(i)?;
Some(sym.as_bytes())
}
}
} else {
use goblin::elf::Elf;
struct Object<'a> {
elf: Elf<'a>,
data: &'a [u8],
// List of pre-parsed and sorted symbols by base address. The
// boolean indicates whether it comes from the dynamic symbol table
// or the normal symbol table, affecting where it's symbolicated.
syms: Vec<(goblin::elf::Sym, bool)>,
}
impl<'a> Object<'a> {
fn parse(data: &'a [u8]) -> Option<Object<'a>> {
let elf = Elf::parse(data).ok()?;
if !elf.little_endian {
return None;
}
let mut syms = elf
.syms
.iter()
.map(|s| (s, false))
.chain(elf.dynsyms.iter().map(|s| (s, true)))
// Only look at function/object symbols. This mirrors what
// libbacktrace does and in general we're only symbolicating
// function addresses in theory. Object symbols correspond
// to data, and maybe someone's crazy enough to have a
// function go into static data?
.filter(|(s, _)| {
s.is_function() || s.st_type() == goblin::elf::sym::STT_OBJECT
})
// skip anything that's in an undefined section header,
// since it means it's an imported function and we're only
// symbolicating with locally defined functions.
.filter(|(s, _)| {
s.st_shndx != goblin::elf::section_header::SHN_UNDEF as usize
})
.collect::<Vec<_>>();
syms.sort_unstable_by_key(|s| s.0.st_value);
Some(Object {
syms,
elf,
data,
})
}
fn section(&self, name: &str) -> Option<&'a [u8]> {
let section = self.elf.section_headers.iter().find(|section| {
match self.elf.shdr_strtab.get(section.sh_name) {
Some(Ok(section_name)) => section_name == name,
_ => false,
}
});
section
.and_then(|section| {
self.data.get(section.sh_offset as usize..)
.and_then(|data| data.get(..section.sh_size as usize))
})
}
fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
// Same sort of binary search as Windows above
let i = match self.syms.binary_search_by_key(&addr, |s| s.0.st_value) {
Ok(i) => i,
Err(i) => i.checked_sub(1)?,
};
let (sym, dynamic) = self.syms.get(i)?;
if sym.st_value <= addr && addr <= sym.st_value + sym.st_size {
let strtab = if *dynamic {
&self.elf.dynstrtab
} else {
&self.elf.strtab
};
Some(strtab.get(sym.st_name)?.ok()?.as_bytes())
} else {
None
}
}
}
}
}
impl Mapping {
#[cfg(not(target_os = "macos"))]
fn new(path: &Path) -> Option<Mapping> {
let map = mmap(path)?;
let cx = cx(Object::parse(&map)?)?;
Some(mk!(Mapping { map, cx }))
}
// The loading path for OSX is is so different we just have a completely
// different implementation of the function here. On OSX we need to go
// probing the filesystem for a bunch of files.
#[cfg(target_os = "macos")]
fn new(path: &Path) -> Option<Mapping> {
// First up we need to load the unique UUID which is stored in the macho
// header of the file we're reading, specified at `path`.
let map = mmap(path)?;
let macho = MachO::parse(&map, 0).ok()?;
let uuid = find_uuid(&macho)?;
// Next we need to look for a `*.dSYM` file. For now we just probe the
// containing directory and look around for something that matches
// `*.dSYM`. Once it's found we root through the dwarf resources that it
// contains and try to find a macho file which has a matching UUID as
// the one of our own file. If we find a match that's the dwarf file we
// want to return.
let parent = path.parent()?;
for entry in parent.read_dir().ok()? {
let entry = entry.ok()?;
let filename = match entry.file_name().into_string() {
Ok(name) => name,
Err(_) => continue,
};
if !filename.ends_with(".dSYM") {
continue;
}
let candidates = entry.path().join("Contents/Resources/DWARF");
if let Some(mapping) = load_dsym(&candidates, &uuid) {
return Some(mapping);
}
}
// Looks like nothing matched our UUID, so let's at least return our own
// file. This should have the symbol table for at least some
// symbolication purposes.
let inner = cx(Object::parse(macho)?)?;
return Some(mk!(Mapping { map, inner }));
fn load_dsym(dir: &Path, uuid: &[u8; 16]) -> Option<Mapping> {
for entry in dir.read_dir().ok()? {
let entry = entry.ok()?;
let map = mmap(&entry.path())?;
let macho = MachO::parse(&map, 0).ok()?;
let entry_uuid = find_uuid(&macho)?;
if entry_uuid != uuid {
continue;
}
if let Some(cx) = Object::parse(macho).and_then(cx) {
return Some(mk!(Mapping { map, cx }));
}
}
None
}
fn find_uuid<'a>(object: &'a MachO) -> Option<&'a [u8; 16]> {
use goblin::mach::load_command::CommandVariant;
object
.load_commands
.iter()
.filter_map(|cmd| match &cmd.command {
CommandVariant::Uuid(u) => Some(&u.uuid),
_ => None,
})
.next()
}
}
}
#[derive(Default)]
struct Cache {
/// All known shared libraries that have been loaded.
libraries: Vec<Library>,
/// Mappings cache where we retain parsed dwarf information.
///
/// This list has a fixed capacity for its entire liftime which never
/// increases. The `usize` element of each pair is an index into `libraries`
/// above where `usize::max_value()` represents the current executable. The
/// `Mapping` is corresponding parsed dwarf information.
///
/// Note that this is basically an LRU cache and we'll be shifting things
/// around in here as we symbolize addresses.
mappings: Vec<(usize, Mapping)>,
}
struct Library {
name: OsString,
segments: Vec<LibrarySegment>,
bias: findshlibs::Bias,
}
struct LibrarySegment {
len: usize,
stated_virtual_memory_address: findshlibs::Svma,
}
// unsafe because this is required to be externally synchronized
pub unsafe fn clear_symbol_cache() {
Cache::with_global(|cache| cache.mappings.clear());
}
impl Cache {
fn new() -> Cache {
let mut libraries = Vec::new();
// Iterate over all loaded shared libraries and cache information we
// learn about them. This way we only load information here once instead
// of once per symbol.
findshlibs::TargetSharedLibrary::each(|so| {
use findshlibs::IterationControl::*;
libraries.push(Library {
name: so.name().to_owned(),
segments: so
.segments()
.map(|s| LibrarySegment {
len: s.len(),
stated_virtual_memory_address: s.stated_virtual_memory_address(),
})
.collect(),
bias: so.virtual_memory_bias(),
});
Continue
});
Cache {
mappings: Vec::with_capacity(MAPPINGS_CACHE_SIZE),
libraries,
}
}
// unsafe because this is required to be externally synchronized
unsafe fn with_global(f: impl FnOnce(&mut Self)) {
// A very small, very simple LRU cache for debug info mappings.
//
// The hit rate should be very high, since the typical stack doesn't cross
// between many shared libraries.
//
// The `addr2line::Context` structures are pretty expensive to create. Its
// cost is expected to be amortized by subsequent `locate` queries, which
// leverage the structures built when constructing `addr2line::Context`s to
// get nice speedups. If we didn't have this cache, that amortization would
// never happen, and symbolicating backtraces would be ssssllllooooowwww.
static mut MAPPINGS_CACHE: Option<Cache> = None;
f(MAPPINGS_CACHE.get_or_insert_with(|| Cache::new()))
}
fn avma_to_svma(&self, addr: *const u8) -> Option<(usize, findshlibs::Svma)> {
// Note that for now `findshlibs` is unimplemented on Windows, so we
// just unhelpfully assume that the address is an SVMA. Surprisingly it
// seems to at least somewhat work on Wine on Linux though...
//
// This probably means ASLR on Windows is busted.
if cfg!(windows) {
let addr = findshlibs::Svma(addr);
return Some((usize::max_value(), addr));
}
self.libraries
.iter()
.enumerate()
.filter_map(|(i, lib)| {
// First up, test if this `lib` has any segment containing the
// `addr` (handling relocation). If this check passes then we
// can continue below and actually translate the address.
//
// Note that this is an inlining of `contains_avma` in the
// `findshlibs` crate here.
if !lib.segments.iter().any(|s| {
let svma = s.stated_virtual_memory_address;
let start = unsafe { svma.0.offset(lib.bias.0) as usize };
let end = start + s.len;
let address = addr as usize;
start <= address && address < end
}) {
return None;
}
// Now that we know `lib` contains `addr`, do the equiavlent of
// `avma_to_svma` in the `findshlibs` crate.
let reverse_bias = -lib.bias.0;
let svma = findshlibs::Svma(unsafe { addr.offset(reverse_bias) });
Some((i, svma))
})
.next()
}
fn mapping_for_lib<'a>(&'a mut self, lib: usize) -> Option<&'a Context<'a>> {
let idx = self.mappings.iter().position(|(idx, _)| *idx == lib);
// Invariant: after this conditional completes without early returning
// from an error, the cache entry for this path is at index 0.
if let Some(idx) = idx {
// When the mapping is already in the cache, move it to the front.
if idx != 0 {
let entry = self.mappings.remove(idx);
self.mappings.insert(0, entry);
}
} else {
// When the mapping is not in the cache, create a new mapping,
// insert it into the front of the cache, and evict the oldest cache
// entry if necessary.
let storage;
let path = match self.libraries.get(lib) {
Some(lib) => &lib.name,
None => {
storage = env::current_exe().ok()?.into();
&storage
}
};
let mapping = Mapping::new(path.as_ref())?;
if self.mappings.len() == MAPPINGS_CACHE_SIZE {
self.mappings.pop();
}
self.mappings.insert(0, (lib, mapping));
}
let cx: &'a Context<'static> = &self.mappings[0].1.cx;
// don't leak the `'static` lifetime, make sure it's scoped to just
// ourselves
Some(unsafe { mem::transmute::<&'a Context<'static>, &'a Context<'a>>(cx) })
}
}
pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) {
let addr = what.address_or_ip();
let mut cb = DladdrFallback {
cb,
addr,
called: false,
};
Cache::with_global(|cache| {
let (lib, addr) = match cache.avma_to_svma(addr as *const u8) {
Some(pair) => pair,
None => return,
};
// Finally, get a cached mapping or create a new mapping for this file, and
// evaluate the DWARF info to find the file/line/name for this address.
let cx = match cache.mapping_for_lib(lib) {
Some(cx) => cx,
None => return,
};
if let Ok(mut frames) = cx.dwarf.find_frames(addr.0 as u64) {
while let Ok(Some(frame)) = frames.next() {
cb.call(Symbol::Frame {
addr: addr.0 as *mut c_void,
location: frame.location,
name: frame.function.map(|f| f.name.slice()),
});
}
}
if !cb.called {
if let Some(name) = cx.object.search_symtab(addr.0 as u64) {
cb.call(Symbol::Symtab {
addr: addr.0 as *mut c_void,
name,
});
}
}
});
drop(cb);
}
struct DladdrFallback<'a, 'b> {
addr: *mut c_void,
called: bool,
cb: &'a mut (FnMut(&super::Symbol) + 'b),
}
impl DladdrFallback<'_, '_> {
fn call(&mut self, sym: Symbol) {
self.called = true;
// Extend the lifetime of `sym` to `'static` since we are unfortunately
// required to here, but it's ony ever going out as a reference so no
// reference to it should be persisted beyond this frame anyway.
let sym = unsafe { mem::transmute::<Symbol, Symbol<'static>>(sym) };
(self.cb)(&super::Symbol { inner: sym });
}
}
impl Drop for DladdrFallback<'_, '_> {
fn drop(&mut self) {
if self.called {
return;
}
unsafe {
dladdr::resolve(self.addr, &mut |sym| {
(self.cb)(&super::Symbol {
inner: Symbol::Dladdr(sym),
})
});
}
}
}
pub enum Symbol<'a> {
/// We were able to locate frame information for this symbol, and
/// `addr2line`'s frame internally has all the nitty gritty details.
Frame {
addr: *mut c_void,
location: Option<addr2line::Location<'a>>,
name: Option<&'a [u8]>,
},
/// Couldn't find debug information, but we found it in the symbol table of
/// the elf executable.
Symtab { addr: *mut c_void, name: &'a [u8] },
/// We weren't able to find anything in the original file, so we had to fall
/// back to using `dladdr` which had a hit.
Dladdr(dladdr::Symbol<'a>),
}
impl Symbol<'_> {
pub fn name(&self) -> Option<SymbolName> {
match self {
Symbol::Dladdr(s) => s.name(),
Symbol::Frame { name, .. } => {
let name = name.as_ref()?;
Some(SymbolName::new(name))
}
Symbol::Symtab { name, .. } => Some(SymbolName::new(name)),
}
}
pub fn addr(&self) -> Option<*mut c_void> {
match self {
Symbol::Dladdr(s) => s.addr(),
Symbol::Frame { addr, .. } => Some(*addr),
Symbol::Symtab { .. } => None,
}
}
pub fn filename_raw(&self) -> Option<BytesOrWideString> {
match self {
Symbol::Dladdr(s) => return s.filename_raw(),
Symbol::Frame { location, .. } => {
let file = location.as_ref()?.file?;
Some(BytesOrWideString::Bytes(file.as_bytes()))
}
Symbol::Symtab { .. } => None,
}
}
pub fn filename(&self) -> Option<&Path> {
match self {
Symbol::Dladdr(s) => return s.filename(),
Symbol::Frame { location, .. } => {
let file = location.as_ref()?.file?;
Some(Path::new(file))
}
Symbol::Symtab { .. } => None,
}
}
pub fn lineno(&self) -> Option<u32> {
match self {
Symbol::Dladdr(s) => return s.lineno(),
Symbol::Frame { location, .. } => location.as_ref()?.line,
Symbol::Symtab { .. } => None,
}
}
}

View File

@ -1,489 +0,0 @@
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Symbolication strategy using the DWARF-parsing code in libbacktrace.
//!
//! The libbacktrace C library, typically distributed with gcc, supports not
//! only generating a backtrace (which we don't actually use) but also
//! symbolicating the backtrace and handling dwarf debug information about
//! things like inlined frames and whatnot.
//!
//! This is relatively complicated due to lots of various concerns here, but the
//! basic idea is:
//!
//! * First we call `backtrace_syminfo`. This gets symbol information from the
//! dynamic symbol table if we can.
//! * Next we call `backtrace_pcinfo`. This will parse debuginfo tables if
//! they're available and allow us to recover information about inline frames,
//! filenames, line numbers, etc.
//!
//! There's lots of trickery about getting the dwarf tables into libbacktrace,
//! but hopefully it's not the end of the world and is clear enough when reading
//! below.
//!
//! This is the default symbolication strategy for non-MSVC and non-OSX
//! platforms. In libstd though this is the default strategy for OSX.
#![allow(bad_style)]
extern crate backtrace_sys as bt;
use core::{ptr, slice};
use libc::{self, c_char, c_int, c_void, uintptr_t};
use crate::symbolize::dladdr;
use crate::symbolize::{ResolveWhat, SymbolName};
use crate::types::BytesOrWideString;
pub enum Symbol<'a> {
Syminfo {
pc: uintptr_t,
symname: *const c_char,
},
Pcinfo {
pc: uintptr_t,
filename: *const c_char,
lineno: c_int,
function: *const c_char,
symname: *const c_char,
},
Dladdr(dladdr::Symbol<'a>),
}
impl Symbol<'_> {
pub fn name(&self) -> Option<SymbolName> {
let symbol = |ptr: *const c_char| unsafe {
if ptr.is_null() {
None
} else {
let len = libc::strlen(ptr);
Some(SymbolName::new(slice::from_raw_parts(
ptr as *const u8,
len,
)))
}
};
match *self {
Symbol::Syminfo { symname, .. } => symbol(symname),
Symbol::Pcinfo {
function, symname, ..
} => {
// If possible prefer the `function` name which comes from
// debuginfo and can typically be more accurate for inline
// frames for example. If that's not present though fall back to
// the symbol table name specified in `symname`.
//
// Note that sometimes `function` can feel somewhat less
// accurate, for example being listed as `try<i32,closure>`
// isntead of `std::panicking::try::do_call`. It's not really
// clear why, but overall the `function` name seems more accurate.
if let Some(sym) = symbol(function) {
return Some(sym);
}
symbol(symname)
}
Symbol::Dladdr(ref s) => s.name(),
}
}
pub fn addr(&self) -> Option<*mut c_void> {
let pc = match *self {
Symbol::Syminfo { pc, .. } => pc,
Symbol::Pcinfo { pc, .. } => pc,
Symbol::Dladdr(ref s) => return s.addr(),
};
if pc == 0 {
None
} else {
Some(pc as *mut _)
}
}
fn filename_bytes(&self) -> Option<&[u8]> {
match *self {
Symbol::Syminfo { .. } => None,
Symbol::Pcinfo { filename, .. } => {
let ptr = filename as *const u8;
unsafe {
let len = libc::strlen(filename);
Some(slice::from_raw_parts(ptr, len))
}
}
Symbol::Dladdr(_) => None,
}
}
pub fn filename_raw(&self) -> Option<BytesOrWideString> {
self.filename_bytes().map(BytesOrWideString::Bytes)
}
#[cfg(feature = "std")]
pub fn filename(&self) -> Option<&::std::path::Path> {
use std::path::Path;
#[cfg(unix)]
fn bytes2path(bytes: &[u8]) -> Option<&Path> {
use std::ffi::OsStr;
use std::os::unix::prelude::*;
Some(Path::new(OsStr::from_bytes(bytes)))
}
#[cfg(windows)]
fn bytes2path(bytes: &[u8]) -> Option<&Path> {
use std::str;
str::from_utf8(bytes).ok().map(Path::new)
}
self.filename_bytes().and_then(bytes2path)
}
pub fn lineno(&self) -> Option<u32> {
match *self {
Symbol::Syminfo { .. } => None,
Symbol::Pcinfo { lineno, .. } => Some(lineno as u32),
Symbol::Dladdr(ref s) => s.lineno(),
}
}
}
extern "C" fn error_cb(_data: *mut c_void, _msg: *const c_char, _errnum: c_int) {
// do nothing for now
}
/// Type of the `data` pointer passed into `syminfo_cb`
struct SyminfoState<'a> {
cb: &'a mut (FnMut(&super::Symbol) + 'a),
pc: usize,
}
extern "C" fn syminfo_cb(
data: *mut c_void,
pc: uintptr_t,
symname: *const c_char,
_symval: uintptr_t,
_symsize: uintptr_t,
) {
let mut bomb = crate::Bomb { enabled: true };
// Once this callback is invoked from `backtrace_syminfo` when we start
// resolving we go further to call `backtrace_pcinfo`. The
// `backtrace_pcinfo` function will consult debug information and attemp tto
// do things like recover file/line information as well as inlined frames.
// Note though that `backtrace_pcinfo` can fail or not do much if there's
// not debug info, so if that happens we're sure to call the callback with
// at least one symbol from the `syminfo_cb`.
unsafe {
let syminfo_state = &mut *(data as *mut SyminfoState);
let mut pcinfo_state = PcinfoState {
symname,
called: false,
cb: syminfo_state.cb,
};
bt::backtrace_pcinfo(
init_state(),
syminfo_state.pc as uintptr_t,
pcinfo_cb,
error_cb,
&mut pcinfo_state as *mut _ as *mut _,
);
if !pcinfo_state.called {
(pcinfo_state.cb)(&super::Symbol {
inner: Symbol::Syminfo {
pc: pc,
symname: symname,
},
});
}
}
bomb.enabled = false;
}
/// Type of the `data` pointer passed into `pcinfo_cb`
struct PcinfoState<'a> {
cb: &'a mut (FnMut(&super::Symbol) + 'a),
symname: *const c_char,
called: bool,
}
extern "C" fn pcinfo_cb(
data: *mut c_void,
pc: uintptr_t,
filename: *const c_char,
lineno: c_int,
function: *const c_char,
) -> c_int {
if filename.is_null() || function.is_null() {
return -1;
}
let mut bomb = crate::Bomb { enabled: true };
unsafe {
let state = &mut *(data as *mut PcinfoState);
state.called = true;
(state.cb)(&super::Symbol {
inner: Symbol::Pcinfo {
pc: pc,
filename: filename,
lineno: lineno,
symname: state.symname,
function,
},
});
}
bomb.enabled = false;
return 0;
}
// The libbacktrace API supports creating a state, but it does not
// support destroying a state. I personally take this to mean that a
// state is meant to be created and then live forever.
//
// I would love to register an at_exit() handler which cleans up this
// state, but libbacktrace provides no way to do so.
//
// With these constraints, this function has a statically cached state
// that is calculated the first time this is requested. Remember that
// backtracing all happens serially (one global lock).
//
// Note the lack of synchronization here is due to the requirement that
// `resolve` is externally synchronized.
unsafe fn init_state() -> *mut bt::backtrace_state {
static mut STATE: *mut bt::backtrace_state = 0 as *mut _;
if !STATE.is_null() {
return STATE;
}
STATE = bt::backtrace_create_state(
load_filename(),
// Don't exercise threadsafe capabilities of libbacktrace since
// we're always calling it in a synchronized fashion.
0,
error_cb,
ptr::null_mut(), // no extra data
);
return STATE;
// Note that for libbacktrace to operate at all it needs to find the DWARF
// debug info for the current executable. It typically does that via a
// number of mechanisms including, but not limited to:
//
// * /proc/self/exe on supported platforms
// * The filename passed in explicitly when creating state
//
// The libbacktrace library is a big wad of C code. This naturally means
// it's got memory safety vulnerabilities, especially when handling
// malformed debuginfo. Libstd has run into plenty of these historically.
//
// If /proc/self/exe is used then we can typically ignore these as we
// assume that libbacktrace is "mostly correct" and otherwise doesn't do
// weird things with "attempted to be correct" dwarf debug info.
//
// If we pass in a filename, however, then it's possible on some platforms
// (like BSDs) where a malicious actor can cause an arbitrary file to be
// placed at that location. This means that if we tell libbacktrace about a
// filename it may be using an arbitrary file, possibly causing segfaults.
// If we don't tell libbacktrace anything though then it won't do anything
// on platforms that don't support paths like /proc/self/exe!
//
// Given all that we try as hard as possible to *not* pass in a filename,
// but we must on platforms that don't support /proc/self/exe at all.
cfg_if::cfg_if! {
if #[cfg(any(target_os = "macos", target_os = "ios"))] {
// Note that ideally we'd use `std::env::current_exe`, but we can't
// require `std` here.
//
// Use `_NSGetExecutablePath` to load the current executable path
// into a static area (which if it's too small just give up).
//
// Note that we're seriously trusting libbacktrace here to not die
// on corrupt executables, but it surely does...
unsafe fn load_filename() -> *const libc::c_char {
const N: usize = 256;
static mut BUF: [u8; N] = [0; N];
extern {
fn _NSGetExecutablePath(
buf: *mut libc::c_char,
bufsize: *mut u32,
) -> libc::c_int;
}
let mut sz: u32 = BUF.len() as u32;
let ptr = BUF.as_mut_ptr() as *mut libc::c_char;
if _NSGetExecutablePath(ptr, &mut sz) == 0 {
ptr
} else {
ptr::null()
}
}
} else if #[cfg(windows)] {
use crate::windows::*;
// Windows has a mode of opening files where after it's opened it
// can't be deleted. That's in general what we want here because we
// want to ensure that our executable isn't changing out from under
// us after we hand it off to libbacktrace, hopefully mitigating the
// ability to pass in arbitrary data into libbacktrace (which may be
// mishandled).
//
// Given that we do a bit of a dance here to attempt to get a sort
// of lock on our own image:
//
// * Get a handle to the current process, load its filename.
// * Open a file to that filename with the right flags.
// * Reload the current process's filename, making sure it's the same
//
// If that all passes we in theory have indeed opened our process's
// file and we're guaranteed it won't change. FWIW a bunch of this
// is copied from libstd historically, so this is my best
// interpretation of what was happening.
unsafe fn load_filename() -> *const libc::c_char {
load_filename_opt().unwrap_or(ptr::null())
}
unsafe fn load_filename_opt() -> Result<*const libc::c_char, ()> {
const N: usize = 256;
// This lives in static memory so we can return it..
static mut BUF: [i8; N] = [0; N];
// ... and this lives on the stack since it's temporary
let mut stack_buf = [0; N];
let name1 = query_full_name(&mut BUF)?;
let handle = CreateFileA(
name1.as_ptr(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
ptr::null_mut(),
OPEN_EXISTING,
0,
ptr::null_mut(),
);
if handle.is_null() {
return Err(());
}
let name2 = query_full_name(&mut stack_buf)?;
if name1 != name2 {
CloseHandle(handle);
return Err(())
}
// intentionally leak `handle` here because having that open
// should preserve our lock on this file name.
Ok(name1.as_ptr())
}
unsafe fn query_full_name(buf: &mut [i8]) -> Result<&[i8], ()> {
let dll = GetModuleHandleA(b"kernel32.dll\0".as_ptr() as *const i8);
if dll.is_null() {
return Err(())
}
let ptrQueryFullProcessImageNameA =
GetProcAddress(dll, b"QueryFullProcessImageNameA\0".as_ptr() as *const _) as usize;
if ptrQueryFullProcessImageNameA == 0
{
return Err(());
}
use core::mem;
let p1 = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
let mut len = buf.len() as u32;
let pfnQueryFullProcessImageNameA : extern "system" fn(
hProcess: HANDLE,
dwFlags: DWORD,
lpExeName: LPSTR,
lpdwSize: PDWORD,
) -> BOOL = mem::transmute(ptrQueryFullProcessImageNameA);
let rc = pfnQueryFullProcessImageNameA(p1, 0, buf.as_mut_ptr(), &mut len);
CloseHandle(p1);
// We want to return a slice that is nul-terminated, so if
// everything was filled in and it equals the total length
// then equate that to failure.
//
// Otherwise when returning success make sure the nul byte is
// included in the slice.
if rc == 0 || len == buf.len() as u32 {
Err(())
} else {
assert_eq!(buf[len as usize], 0);
Ok(&buf[..(len + 1) as usize])
}
}
} else if #[cfg(target_os = "vxworks")] {
unsafe fn load_filename() -> *const libc::c_char {
use libc;
use core::mem;
const N: usize = libc::VX_RTP_NAME_LENGTH as usize + 1;
static mut BUF: [libc::c_char; N] = [0; N];
let mut rtp_desc : libc::RTP_DESC = mem::zeroed();
if (libc::rtpInfoGet(0, &mut rtp_desc as *mut libc::RTP_DESC) == 0) {
BUF.copy_from_slice(&rtp_desc.pathName);
BUF.as_ptr()
} else {
ptr::null()
}
}
} else {
unsafe fn load_filename() -> *const libc::c_char {
ptr::null()
}
}
}
}
pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) {
let symaddr = what.address_or_ip() as usize;
// backtrace errors are currently swept under the rug
let state = init_state();
if state.is_null() {
return dladdr_fallback(what.address_or_ip(), cb);
}
// Call the `backtrace_syminfo` API first. This is (from reading the code)
// guaranteed to call `syminfo_cb` exactly once (or fail with an error
// presumably). We then handle more within the `syminfo_cb`.
//
// Note that we do this since `syminfo` will consult the symbol table,
// finding symbol names even if there's no debug information in the binary.
let mut called = false;
{
let mut syminfo_state = SyminfoState {
pc: symaddr,
cb: &mut |sym| {
called = true;
cb(sym);
},
};
bt::backtrace_syminfo(
state,
symaddr as uintptr_t,
syminfo_cb,
error_cb,
&mut syminfo_state as *mut _ as *mut _,
);
}
if !called {
dladdr_fallback(what.address_or_ip(), cb);
}
}
unsafe fn dladdr_fallback(addr: *mut c_void, cb: &mut FnMut(&super::Symbol)) {
dladdr::resolve(addr, &mut |sym| {
cb(&super::Symbol {
inner: Symbol::Dladdr(sym),
})
});
}

View File

@ -1,514 +0,0 @@
use core::{fmt, str};
cfg_if::cfg_if! {
if #[cfg(feature = "std")] {
use std::path::Path;
use std::prelude::v1::*;
}
}
use crate::backtrace::Frame;
use crate::types::BytesOrWideString;
use core::ffi::c_void;
use rustc_demangle::{try_demangle, Demangle};
/// Resolve an address to a symbol, passing the symbol to the specified
/// closure.
///
/// This function will look up the given address in areas such as the local
/// symbol table, dynamic symbol table, or DWARF debug info (depending on the
/// activated implementation) to find symbols to yield.
///
/// The closure may not be called if resolution could not be performed, and it
/// also may be called more than once in the case of inlined functions.
///
/// Symbols yielded represent the execution at the specified `addr`, returning
/// file/line pairs for that address (if available).
///
/// Note that if you have a `Frame` then it's recommended to use the
/// `resolve_frame` function instead of this one.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
///
/// # Panics
///
/// This function strives to never panic, but if the `cb` provided panics then
/// some platforms will force a double panic to abort the process. Some
/// platforms use a C library which internally uses callbacks which cannot be
/// unwound through, so panicking from `cb` may trigger a process abort.
///
/// # Example
///
/// ```
/// extern crate backtrace;
///
/// fn main() {
/// backtrace::trace(|frame| {
/// let ip = frame.ip();
///
/// backtrace::resolve(ip, |symbol| {
/// // ...
/// });
///
/// false // only look at the top frame
/// });
/// }
/// ```
#[cfg(feature = "std")]
pub fn resolve<F: FnMut(&Symbol)>(addr: *mut c_void, cb: F) {
let _guard = crate::lock::lock();
unsafe { resolve_unsynchronized(addr, cb) }
}
/// Resolve a previously capture frame to a symbol, passing the symbol to the
/// specified closure.
///
/// This functin performs the same function as `resolve` except that it takes a
/// `Frame` as an argument instead of an address. This can allow some platform
/// implementations of backtracing to provide more accurate symbol information
/// or information about inline frames for example. It's recommended to use this
/// if you can.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
///
/// # Panics
///
/// This function strives to never panic, but if the `cb` provided panics then
/// some platforms will force a double panic to abort the process. Some
/// platforms use a C library which internally uses callbacks which cannot be
/// unwound through, so panicking from `cb` may trigger a process abort.
///
/// # Example
///
/// ```
/// extern crate backtrace;
///
/// fn main() {
/// backtrace::trace(|frame| {
/// backtrace::resolve_frame(frame, |symbol| {
/// // ...
/// });
///
/// false // only look at the top frame
/// });
/// }
/// ```
#[cfg(feature = "std")]
pub fn resolve_frame<F: FnMut(&Symbol)>(frame: &Frame, cb: F) {
let _guard = crate::lock::lock();
unsafe { resolve_frame_unsynchronized(frame, cb) }
}
pub enum ResolveWhat<'a> {
Address(*mut c_void),
Frame(&'a Frame),
}
impl<'a> ResolveWhat<'a> {
#[allow(dead_code)]
fn address_or_ip(&self) -> *mut c_void {
match self {
ResolveWhat::Address(a) => adjust_ip(*a),
ResolveWhat::Frame(f) => adjust_ip(f.ip()),
}
}
}
// IP values from stack frames are typically (always?) the instruction
// *after* the call that's the actual stack trace. Symbolizing this on
// causes the filename/line number to be one ahead and perhaps into
// the void if it's near the end of the function.
//
// This appears to basically always be the case on all platforms, so we always
// subtract one from a resolved ip to resolve it to the previous call
// instruction instead of the instruction being returned to.
//
// Ideally we would not do this. Ideally we would require callers of the
// `resolve` APIs here to manually do the -1 and account that they want location
// information for the *previous* instruction, not the current. Ideally we'd
// also expose on `Frame` if we are indeed the address of the next instruction
// or the current.
//
// For now though this is a pretty niche concern so we just internally always
// subtract one. Consumers should keep working and getting pretty good results,
// so we should be good enough.
fn adjust_ip(a: *mut c_void) -> *mut c_void {
if a.is_null() {
a
} else {
(a as usize - 1) as *mut c_void
}
}
/// Same as `resolve`, only unsafe as it's unsynchronized.
///
/// This function does not have synchronization guarentees but is available when
/// the `std` feature of this crate isn't compiled in. See the `resolve`
/// function for more documentation and examples.
///
/// # Panics
///
/// See information on `resolve` for caveats on `cb` panicking.
pub unsafe fn resolve_unsynchronized<F>(addr: *mut c_void, mut cb: F)
where
F: FnMut(&Symbol),
{
resolve_imp(ResolveWhat::Address(addr), &mut cb)
}
/// Same as `resolve_frame`, only unsafe as it's unsynchronized.
///
/// This function does not have synchronization guarentees but is available
/// when the `std` feature of this crate isn't compiled in. See the
/// `resolve_frame` function for more documentation and examples.
///
/// # Panics
///
/// See information on `resolve_frame` for caveats on `cb` panicking.
pub unsafe fn resolve_frame_unsynchronized<F>(frame: &Frame, mut cb: F)
where
F: FnMut(&Symbol),
{
resolve_imp(ResolveWhat::Frame(frame), &mut cb)
}
/// A trait representing the resolution of a symbol in a file.
///
/// This trait is yielded as a trait object to the closure given to the
/// `backtrace::resolve` function, and it is virtually dispatched as it's
/// unknown which implementation is behind it.
///
/// A symbol can give contextual information about a function, for example the
/// name, filename, line number, precise address, etc. Not all information is
/// always available in a symbol, however, so all methods return an `Option`.
pub struct Symbol {
// TODO: this lifetime bound needs to be persisted eventually to `Symbol`,
// but that's currently a breaking change. For now this is safe since
// `Symbol` is only ever handed out by reference and can't be cloned.
inner: SymbolImp<'static>,
}
impl Symbol {
/// Returns the name of this function.
///
/// The returned structure can be used to query various properties about the
/// symbol name:
///
/// * The `Display` implementation will print out the demangled symbol.
/// * The raw `str` value of the symbol can be accessed (if it's valid
/// utf-8).
/// * The raw bytes for the symbol name can be accessed.
pub fn name(&self) -> Option<SymbolName> {
self.inner.name()
}
/// Returns the starting address of this function.
pub fn addr(&self) -> Option<*mut c_void> {
self.inner.addr().map(|p| p as *mut _)
}
/// Returns the raw filename as a slice. This is mainly useful for `no_std`
/// environments.
pub fn filename_raw(&self) -> Option<BytesOrWideString> {
self.inner.filename_raw()
}
/// Returns the line number for where this symbol is currently executing.
///
/// This return value is typically `Some` if `filename` returns `Some`, and
/// is consequently subject to similar caveats.
pub fn lineno(&self) -> Option<u32> {
self.inner.lineno()
}
/// Returns the file name where this function was defined.
///
/// This is currently only available when libbacktrace is being used (e.g.
/// unix platforms other than OSX) and when a binary is compiled with
/// debuginfo. If neither of these conditions is met then this will likely
/// return `None`.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
#[cfg(feature = "std")]
#[allow(unreachable_code)]
pub fn filename(&self) -> Option<&Path> {
self.inner.filename()
}
}
impl fmt::Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut d = f.debug_struct("Symbol");
if let Some(name) = self.name() {
d.field("name", &name);
}
if let Some(addr) = self.addr() {
d.field("addr", &addr);
}
#[cfg(feature = "std")]
{
if let Some(filename) = self.filename() {
d.field("filename", &filename);
}
}
if let Some(lineno) = self.lineno() {
d.field("lineno", &lineno);
}
d.finish()
}
}
cfg_if::cfg_if! {
if #[cfg(feature = "cpp_demangle")] {
// Maybe a parsed C++ symbol, if parsing the mangled symbol as Rust
// failed.
struct OptionCppSymbol<'a>(Option<::cpp_demangle::BorrowedSymbol<'a>>);
impl<'a> OptionCppSymbol<'a> {
fn parse(input: &'a [u8]) -> OptionCppSymbol<'a> {
OptionCppSymbol(::cpp_demangle::BorrowedSymbol::new(input).ok())
}
fn none() -> OptionCppSymbol<'a> {
OptionCppSymbol(None)
}
}
} else {
use core::marker::PhantomData;
// Make sure to keep this zero-sized, so that the `cpp_demangle` feature
// has no cost when disabled.
struct OptionCppSymbol<'a>(PhantomData<&'a ()>);
impl<'a> OptionCppSymbol<'a> {
fn parse(_: &'a [u8]) -> OptionCppSymbol<'a> {
OptionCppSymbol(PhantomData)
}
fn none() -> OptionCppSymbol<'a> {
OptionCppSymbol(PhantomData)
}
}
}
}
/// A wrapper around a symbol name to provide ergonomic accessors to the
/// demangled name, the raw bytes, the raw string, etc.
// Allow dead code for when the `cpp_demangle` feature is not enabled.
#[allow(dead_code)]
pub struct SymbolName<'a> {
bytes: &'a [u8],
demangled: Option<Demangle<'a>>,
cpp_demangled: OptionCppSymbol<'a>,
}
impl<'a> SymbolName<'a> {
/// Creates a new symbol name from the raw underlying bytes.
pub fn new(bytes: &'a [u8]) -> SymbolName<'a> {
let str_bytes = str::from_utf8(bytes).ok();
let demangled = str_bytes.and_then(|s| try_demangle(s).ok());
let cpp = if demangled.is_none() {
OptionCppSymbol::parse(bytes)
} else {
OptionCppSymbol::none()
};
SymbolName {
bytes: bytes,
demangled: demangled,
cpp_demangled: cpp,
}
}
/// Returns the raw (mangled) symbol name as a `str` if the symbol is valid utf-8.
///
/// Use the `Display` implementation if you want the demangled version.
pub fn as_str(&self) -> Option<&'a str> {
self.demangled
.as_ref()
.map(|s| s.as_str())
.or_else(|| str::from_utf8(self.bytes).ok())
}
/// Returns the raw symbol name as a list of bytes
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
}
fn format_symbol_name(
fmt: fn(&str, &mut fmt::Formatter) -> fmt::Result,
mut bytes: &[u8],
f: &mut fmt::Formatter,
) -> fmt::Result {
while bytes.len() > 0 {
match str::from_utf8(bytes) {
Ok(name) => {
fmt(name, f)?;
break;
}
Err(err) => {
fmt("\u{FFFD}", f)?;
match err.error_len() {
Some(len) => bytes = &bytes[err.valid_up_to() + len..],
None => break,
}
}
}
}
Ok(())
}
cfg_if::cfg_if! {
if #[cfg(feature = "cpp_demangle")] {
impl<'a> fmt::Display for SymbolName<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref s) = self.demangled {
s.fmt(f)
} else if let Some(ref cpp) = self.cpp_demangled.0 {
cpp.fmt(f)
} else {
format_symbol_name(fmt::Display::fmt, self.bytes, f)
}
}
}
} else {
impl<'a> fmt::Display for SymbolName<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref s) = self.demangled {
s.fmt(f)
} else {
format_symbol_name(fmt::Display::fmt, self.bytes, f)
}
}
}
}
}
cfg_if::cfg_if! {
if #[cfg(all(feature = "std", feature = "cpp_demangle"))] {
impl<'a> fmt::Debug for SymbolName<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use std::fmt::Write;
if let Some(ref s) = self.demangled {
return s.fmt(f)
}
// This may to print if the demangled symbol isn't actually
// valid, so handle the error here gracefully by not propagating
// it outwards.
if let Some(ref cpp) = self.cpp_demangled.0 {
let mut s = String::new();
if write!(s, "{}", cpp).is_ok() {
return s.fmt(f)
}
}
format_symbol_name(fmt::Debug::fmt, self.bytes, f)
}
}
} else {
impl<'a> fmt::Debug for SymbolName<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref s) = self.demangled {
s.fmt(f)
} else {
format_symbol_name(fmt::Debug::fmt, self.bytes, f)
}
}
}
}
}
/// Attempt to reclaim that cached memory used to symbolicate addresses.
///
/// This method will attempt to release any global data structures that have
/// otherwise been cached globally or in the thread which typically represent
/// parsed DWARF information or similar.
///
/// # Caveats
///
/// While this function is always available it doesn't actually do anything on
/// most implementations. Libraries like dbghelp or libbacktrace do not provide
/// facilities to deallocate state and manage the allocated memory. For now the
/// `gimli-symbolize` feature of this crate is the only feature where this
/// function has any effect.
#[cfg(feature = "std")]
pub fn clear_symbol_cache() {
let _guard = crate::lock::lock();
unsafe {
clear_symbol_cache_imp();
}
}
mod dladdr;
cfg_if::cfg_if! {
if #[cfg(all(windows, target_env = "msvc", feature = "dbghelp", not(target_vendor = "uwp")))] {
mod dbghelp;
use self::dbghelp::resolve as resolve_imp;
use self::dbghelp::Symbol as SymbolImp;
unsafe fn clear_symbol_cache_imp() {}
} else if #[cfg(all(
feature = "std",
feature = "gimli-symbolize",
any(
target_os = "linux",
target_os = "macos",
windows,
),
))] {
mod gimli;
use self::gimli::resolve as resolve_imp;
use self::gimli::Symbol as SymbolImp;
use self::gimli::clear_symbol_cache as clear_symbol_cache_imp;
// Note that we only enable coresymbolication on iOS when debug assertions
// are enabled because it's helpful in debug mode but it looks like apps get
// rejected from the app store if they use this API, see #92 for more info
} else if #[cfg(all(feature = "coresymbolication",
any(target_os = "macos",
all(target_os = "ios", debug_assertions))))] {
mod coresymbolication;
use self::coresymbolication::resolve as resolve_imp;
use self::coresymbolication::Symbol as SymbolImp;
unsafe fn clear_symbol_cache_imp() {}
} else if #[cfg(all(feature = "libbacktrace",
any(unix, all(windows, not(target_vendor = "uwp"), target_env = "gnu")),
not(target_os = "fuchsia"),
not(target_os = "emscripten"),
not(target_env = "uclibc")))] {
mod libbacktrace;
use self::libbacktrace::resolve as resolve_imp;
use self::libbacktrace::Symbol as SymbolImp;
unsafe fn clear_symbol_cache_imp() {}
} else if #[cfg(all(unix,
not(target_os = "emscripten"),
not(target_os = "fuchsia"),
not(target_env = "uclibc"),
feature = "dladdr"))] {
mod dladdr_resolve;
use self::dladdr_resolve::resolve as resolve_imp;
use self::dladdr_resolve::Symbol as SymbolImp;
unsafe fn clear_symbol_cache_imp() {}
} else {
mod noop;
use self::noop::resolve as resolve_imp;
use self::noop::Symbol as SymbolImp;
#[allow(unused)]
unsafe fn clear_symbol_cache_imp() {}
}
}

View File

@ -1,37 +0,0 @@
//! Empty symbolication strategy used to compile for platforms that have no
//! support.
use crate::symbolize::ResolveWhat;
use crate::types::BytesOrWideString;
use crate::SymbolName;
use core::ffi::c_void;
use core::marker;
pub unsafe fn resolve(_addr: ResolveWhat, _cb: &mut FnMut(&super::Symbol)) {}
pub struct Symbol<'a> {
_marker: marker::PhantomData<&'a i32>,
}
impl Symbol<'_> {
pub fn name(&self) -> Option<SymbolName> {
None
}
pub fn addr(&self) -> Option<*mut c_void> {
None
}
pub fn filename_raw(&self) -> Option<BytesOrWideString> {
None
}
#[cfg(feature = "std")]
pub fn filename(&self) -> Option<&::std::path::Path> {
None
}
pub fn lineno(&self) -> Option<u32> {
None
}
}

View File

@ -1,83 +0,0 @@
//! Platform dependent types.
cfg_if::cfg_if! {
if #[cfg(feature = "std")] {
use std::borrow::Cow;
use std::fmt;
use std::path::PathBuf;
use std::prelude::v1::*;
use std::str;
}
}
/// A platform independent representation of a string. When working with `std`
/// enabled it is recommended to the convenience methods for providing
/// conversions to `std` types.
#[derive(Debug)]
pub enum BytesOrWideString<'a> {
/// A slice, typically provided on Unix platforms.
Bytes(&'a [u8]),
/// Wide strings typically from Windows.
Wide(&'a [u16]),
}
#[cfg(feature = "std")]
impl<'a> BytesOrWideString<'a> {
/// Lossy converts to a `Cow<str>`, will allocate if `Bytes` is not valid
/// UTF-8 or if `BytesOrWideString` is `Wide`.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
pub fn to_str_lossy(&self) -> Cow<'a, str> {
use self::BytesOrWideString::*;
match self {
&Bytes(slice) => String::from_utf8_lossy(slice),
&Wide(wide) => Cow::Owned(String::from_utf16_lossy(wide)),
}
}
/// Provides a `Path` representation of `BytesOrWideString`.
///
/// # Required features
///
/// This function requires the `std` feature of the `backtrace` crate to be
/// enabled, and the `std` feature is enabled by default.
pub fn into_path_buf(self) -> PathBuf {
#[cfg(unix)]
{
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
if let BytesOrWideString::Bytes(slice) = self {
return PathBuf::from(OsStr::from_bytes(slice));
}
}
#[cfg(windows)]
{
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
if let BytesOrWideString::Wide(slice) = self {
return PathBuf::from(OsString::from_wide(slice));
}
}
if let BytesOrWideString::Bytes(b) = self {
if let Ok(s) = str::from_utf8(b) {
return PathBuf::from(s);
}
}
unreachable!()
}
}
#[cfg(feature = "std")]
impl<'a> fmt::Display for BytesOrWideString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.to_str_lossy().fmt(f)
}
}

View File

@ -1,637 +0,0 @@
//! A module to define the FFI definitions we use on Windows for `dbghelp.dll`
//!
//! This module uses a custom macro, `ffi!`, to wrap all definitions to
//! automatically generate tests to assert that our definitions here are the
//! same as `winapi`.
//!
//! This module largely exists to integrate into libstd itself where winapi is
//! not currently available.
#![allow(bad_style, dead_code)]
cfg_if::cfg_if! {
if #[cfg(feature = "verify-winapi")] {
pub use self::winapi::c_void;
pub use self::winapi::HINSTANCE;
pub use self::winapi::FARPROC;
pub use self::winapi::LPSECURITY_ATTRIBUTES;
#[cfg(target_pointer_width = "64")]
pub use self::winapi::PUNWIND_HISTORY_TABLE;
#[cfg(target_pointer_width = "64")]
pub use self::winapi::PRUNTIME_FUNCTION;
mod winapi {
pub use winapi::ctypes::*;
pub use winapi::shared::basetsd::*;
pub use winapi::shared::minwindef::*;
pub use winapi::um::dbghelp::*;
pub use winapi::um::handleapi::*;
pub use winapi::um::libloaderapi::*;
pub use winapi::um::processthreadsapi::*;
pub use winapi::um::winbase::*;
pub use winapi::um::winnt::*;
pub use winapi::um::fileapi::*;
pub use winapi::um::minwinbase::*;
pub use winapi::um::synchapi::*;
}
} else {
pub use core::ffi::c_void;
pub type HINSTANCE = *mut c_void;
pub type FARPROC = *mut c_void;
pub type LPSECURITY_ATTRIBUTES = *mut c_void;
#[cfg(target_pointer_width = "64")]
pub type PRUNTIME_FUNCTION = *mut c_void;
#[cfg(target_pointer_width = "64")]
pub type PUNWIND_HISTORY_TABLE = *mut c_void;
}
}
macro_rules! ffi {
() => ();
(#[repr($($r:tt)*)] pub struct $name:ident { $(pub $field:ident: $ty:ty,)* } $($rest:tt)*) => (
#[repr($($r)*)]
#[cfg(not(feature = "verify-winapi"))]
#[derive(Copy, Clone)]
pub struct $name {
$(pub $field: $ty,)*
}
#[cfg(feature = "verify-winapi")]
pub use self::winapi::$name;
#[test]
#[cfg(feature = "verify-winapi")]
fn $name() {
use core::mem;
#[repr($($r)*)]
pub struct $name {
$(pub $field: $ty,)*
}
assert_eq!(
mem::size_of::<$name>(),
mem::size_of::<winapi::$name>(),
concat!("size of ", stringify!($name), " is wrong"),
);
assert_eq!(
mem::align_of::<$name>(),
mem::align_of::<winapi::$name>(),
concat!("align of ", stringify!($name), " is wrong"),
);
type Winapi = winapi::$name;
fn assert_same<T>(_: T, _: T) {}
unsafe {
let a = &*(mem::align_of::<$name>() as *const $name);
let b = &*(mem::align_of::<Winapi>() as *const Winapi);
$(
ffi!(@test_fields a b $field $ty);
)*
}
}
ffi!($($rest)*);
);
// Handling verification against unions in winapi requires some special care
(@test_fields $a:ident $b:ident FltSave $ty:ty) => (
// Skip this field on x86_64 `CONTEXT` since it's a union and a bit funny
);
(@test_fields $a:ident $b:ident D $ty:ty) => ({
let a = &$a.D;
let b = $b.D();
assert_same(a, b);
assert_eq!(a as *const $ty, b as *const $ty, "misplaced field D");
});
(@test_fields $a:ident $b:ident s $ty:ty) => ({
let a = &$a.s;
let b = $b.s();
assert_same(a, b);
assert_eq!(a as *const $ty, b as *const $ty, "misplaced field s");
});
// Otherwise test all fields normally.
(@test_fields $a:ident $b:ident $field:ident $ty:ty) => ({
let a = &$a.$field;
let b = &$b.$field;
assert_same(a, b);
assert_eq!(a as *const $ty, b as *const $ty,
concat!("misplaced field ", stringify!($field)));
});
(pub type $name:ident = $ty:ty; $($rest:tt)*) => (
pub type $name = $ty;
#[cfg(feature = "verify-winapi")]
#[allow(dead_code)]
const $name: () = {
fn _foo() {
trait SameType {}
impl<T> SameType for (T, T) {}
fn assert_same<T: SameType>() {}
assert_same::<($name, winapi::$name)>();
}
};
ffi!($($rest)*);
);
(pub const $name:ident: $ty:ty = $val:expr; $($rest:tt)*) => (
pub const $name: $ty = $val;
#[cfg(feature = "verify-winapi")]
#[allow(unused_imports)]
mod $name {
use super::*;
#[test]
fn assert_valid() {
let x: $ty = winapi::$name;
assert_eq!(x, $val);
}
}
ffi!($($rest)*);
);
(extern "system" { $(pub fn $name:ident($($args:tt)*) -> $ret:ty;)* } $($rest:tt)*) => (
extern "system" {
$(pub fn $name($($args)*) -> $ret;)*
}
$(
#[cfg(feature = "verify-winapi")]
mod $name {
#[test]
fn assert_same() {
use super::*;
assert_eq!($name as usize, winapi::$name as usize);
let mut x: unsafe extern "system" fn($($args)*) -> $ret;
x = $name;
drop(x);
x = winapi::$name;
drop(x);
}
}
)*
ffi!($($rest)*);
);
(impl $name:ident { $($i:tt)* } $($rest:tt)*) => (
#[cfg(not(feature = "verify-winapi"))]
impl $name {
$($i)*
}
ffi!($($rest)*);
);
}
ffi! {
#[repr(C)]
pub struct STACKFRAME64 {
pub AddrPC: ADDRESS64,
pub AddrReturn: ADDRESS64,
pub AddrFrame: ADDRESS64,
pub AddrStack: ADDRESS64,
pub AddrBStore: ADDRESS64,
pub FuncTableEntry: PVOID,
pub Params: [DWORD64; 4],
pub Far: BOOL,
pub Virtual: BOOL,
pub Reserved: [DWORD64; 3],
pub KdHelp: KDHELP64,
}
pub type LPSTACKFRAME64 = *mut STACKFRAME64;
#[repr(C)]
pub struct STACKFRAME_EX {
pub AddrPC: ADDRESS64,
pub AddrReturn: ADDRESS64,
pub AddrFrame: ADDRESS64,
pub AddrStack: ADDRESS64,
pub AddrBStore: ADDRESS64,
pub FuncTableEntry: PVOID,
pub Params: [DWORD64; 4],
pub Far: BOOL,
pub Virtual: BOOL,
pub Reserved: [DWORD64; 3],
pub KdHelp: KDHELP64,
pub StackFrameSize: DWORD,
pub InlineFrameContext: DWORD,
}
pub type LPSTACKFRAME_EX = *mut STACKFRAME_EX;
#[repr(C)]
pub struct IMAGEHLP_LINEW64 {
pub SizeOfStruct: DWORD,
pub Key: PVOID,
pub LineNumber: DWORD,
pub FileName: PWSTR,
pub Address: DWORD64,
}
pub type PIMAGEHLP_LINEW64 = *mut IMAGEHLP_LINEW64;
#[repr(C)]
pub struct SYMBOL_INFOW {
pub SizeOfStruct: ULONG,
pub TypeIndex: ULONG,
pub Reserved: [ULONG64; 2],
pub Index: ULONG,
pub Size: ULONG,
pub ModBase: ULONG64,
pub Flags: ULONG,
pub Value: ULONG64,
pub Address: ULONG64,
pub Register: ULONG,
pub Scope: ULONG,
pub Tag: ULONG,
pub NameLen: ULONG,
pub MaxNameLen: ULONG,
pub Name: [WCHAR; 1],
}
pub type PSYMBOL_INFOW = *mut SYMBOL_INFOW;
pub type PTRANSLATE_ADDRESS_ROUTINE64 = Option<
unsafe extern "system" fn(hProcess: HANDLE, hThread: HANDLE, lpaddr: LPADDRESS64) -> DWORD64,
>;
pub type PGET_MODULE_BASE_ROUTINE64 =
Option<unsafe extern "system" fn(hProcess: HANDLE, Address: DWORD64) -> DWORD64>;
pub type PFUNCTION_TABLE_ACCESS_ROUTINE64 =
Option<unsafe extern "system" fn(ahProcess: HANDLE, AddrBase: DWORD64) -> PVOID>;
pub type PREAD_PROCESS_MEMORY_ROUTINE64 = Option<
unsafe extern "system" fn(
hProcess: HANDLE,
qwBaseAddress: DWORD64,
lpBuffer: PVOID,
nSize: DWORD,
lpNumberOfBytesRead: LPDWORD,
) -> BOOL,
>;
#[repr(C)]
pub struct ADDRESS64 {
pub Offset: DWORD64,
pub Segment: WORD,
pub Mode: ADDRESS_MODE,
}
pub type LPADDRESS64 = *mut ADDRESS64;
pub type ADDRESS_MODE = u32;
#[repr(C)]
pub struct KDHELP64 {
pub Thread: DWORD64,
pub ThCallbackStack: DWORD,
pub ThCallbackBStore: DWORD,
pub NextCallback: DWORD,
pub FramePointer: DWORD,
pub KiCallUserMode: DWORD64,
pub KeUserCallbackDispatcher: DWORD64,
pub SystemRangeStart: DWORD64,
pub KiUserExceptionDispatcher: DWORD64,
pub StackBase: DWORD64,
pub StackLimit: DWORD64,
pub BuildVersion: DWORD,
pub Reserved0: DWORD,
pub Reserved1: [DWORD64; 4],
}
pub const MAX_SYM_NAME: usize = 2000;
pub const AddrModeFlat: ADDRESS_MODE = 3;
pub const TRUE: BOOL = 1;
pub const FALSE: BOOL = 0;
pub const PROCESS_QUERY_INFORMATION: DWORD = 0x400;
pub const IMAGE_FILE_MACHINE_ARM64: u16 = 43620;
pub const IMAGE_FILE_MACHINE_AMD64: u16 = 34404;
pub const IMAGE_FILE_MACHINE_I386: u16 = 332;
pub const IMAGE_FILE_MACHINE_ARMNT: u16 = 452;
pub const FILE_SHARE_READ: DWORD = 0x1;
pub const FILE_SHARE_WRITE: DWORD = 0x2;
pub const OPEN_EXISTING: DWORD = 0x3;
pub const GENERIC_READ: DWORD = 0x80000000;
pub const INFINITE: DWORD = !0;
pub type DWORD = u32;
pub type PDWORD = *mut u32;
pub type BOOL = i32;
pub type DWORD64 = u64;
pub type PDWORD64 = *mut u64;
pub type HANDLE = *mut c_void;
pub type PVOID = HANDLE;
pub type PCWSTR = *const u16;
pub type LPSTR = *mut i8;
pub type LPCSTR = *const i8;
pub type PWSTR = *mut u16;
pub type WORD = u16;
pub type ULONG = u32;
pub type ULONG64 = u64;
pub type WCHAR = u16;
pub type PCONTEXT = *mut CONTEXT;
pub type LPDWORD = *mut DWORD;
pub type DWORDLONG = u64;
pub type HMODULE = HINSTANCE;
extern "system" {
pub fn GetCurrentProcess() -> HANDLE;
pub fn GetCurrentThread() -> HANDLE;
pub fn RtlCaptureContext(ContextRecord: PCONTEXT) -> ();
pub fn LoadLibraryA(a: *const i8) -> HMODULE;
pub fn GetProcAddress(h: HMODULE, name: *const i8) -> FARPROC;
pub fn GetModuleHandleA(name: *const i8) -> HMODULE;
pub fn OpenProcess(
dwDesiredAccess: DWORD,
bInheitHandle: BOOL,
dwProcessId: DWORD,
) -> HANDLE;
pub fn GetCurrentProcessId() -> DWORD;
pub fn CloseHandle(h: HANDLE) -> BOOL;
pub fn CreateFileA(
lpFileName: LPCSTR,
dwDesiredAccess: DWORD,
dwShareMode: DWORD,
lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
dwCreationDisposition: DWORD,
dwFlagsAndAttributes: DWORD,
hTemplateFile: HANDLE,
) -> HANDLE;
pub fn CreateMutexA(
attrs: LPSECURITY_ATTRIBUTES,
initial: BOOL,
name: LPCSTR,
) -> HANDLE;
pub fn ReleaseMutex(hMutex: HANDLE) -> BOOL;
pub fn WaitForSingleObjectEx(
hHandle: HANDLE,
dwMilliseconds: DWORD,
bAlertable: BOOL,
) -> DWORD;
}
}
#[cfg(target_pointer_width = "64")]
ffi! {
extern "system" {
pub fn RtlLookupFunctionEntry(
ControlPc: DWORD64,
ImageBase: PDWORD64,
HistoryTable: PUNWIND_HISTORY_TABLE,
) -> PRUNTIME_FUNCTION;
}
}
#[cfg(target_arch = "aarch64")]
ffi! {
#[repr(C, align(16))]
pub struct CONTEXT {
pub ContextFlags: DWORD,
pub Cpsr: DWORD,
pub u: CONTEXT_u,
pub Sp: u64,
pub Pc: u64,
pub V: [ARM64_NT_NEON128; 32],
pub Fpcr: DWORD,
pub Fpsr: DWORD,
pub Bcr: [DWORD; ARM64_MAX_BREAKPOINTS],
pub Bvr: [DWORD64; ARM64_MAX_BREAKPOINTS],
pub Wcr: [DWORD; ARM64_MAX_WATCHPOINTS],
pub Wvr: [DWORD64; ARM64_MAX_WATCHPOINTS],
}
#[repr(C)]
pub struct CONTEXT_u {
pub s: CONTEXT_u_s,
}
impl CONTEXT_u {
pub unsafe fn s(&self) -> &CONTEXT_u_s {
&self.s
}
}
#[repr(C)]
pub struct CONTEXT_u_s {
pub X0: u64,
pub X1: u64,
pub X2: u64,
pub X3: u64,
pub X4: u64,
pub X5: u64,
pub X6: u64,
pub X7: u64,
pub X8: u64,
pub X9: u64,
pub X10: u64,
pub X11: u64,
pub X12: u64,
pub X13: u64,
pub X14: u64,
pub X15: u64,
pub X16: u64,
pub X17: u64,
pub X18: u64,
pub X19: u64,
pub X20: u64,
pub X21: u64,
pub X22: u64,
pub X23: u64,
pub X24: u64,
pub X25: u64,
pub X26: u64,
pub X27: u64,
pub X28: u64,
pub Fp: u64,
pub Lr: u64,
}
pub const ARM64_MAX_BREAKPOINTS: usize = 8;
pub const ARM64_MAX_WATCHPOINTS: usize = 2;
#[repr(C)]
pub struct ARM64_NT_NEON128 {
pub D: [f64; 2],
}
}
#[cfg(target_arch = "x86")]
ffi! {
#[repr(C)]
pub struct CONTEXT {
pub ContextFlags: DWORD,
pub Dr0: DWORD,
pub Dr1: DWORD,
pub Dr2: DWORD,
pub Dr3: DWORD,
pub Dr6: DWORD,
pub Dr7: DWORD,
pub FloatSave: FLOATING_SAVE_AREA,
pub SegGs: DWORD,
pub SegFs: DWORD,
pub SegEs: DWORD,
pub SegDs: DWORD,
pub Edi: DWORD,
pub Esi: DWORD,
pub Ebx: DWORD,
pub Edx: DWORD,
pub Ecx: DWORD,
pub Eax: DWORD,
pub Ebp: DWORD,
pub Eip: DWORD,
pub SegCs: DWORD,
pub EFlags: DWORD,
pub Esp: DWORD,
pub SegSs: DWORD,
pub ExtendedRegisters: [u8; 512],
}
#[repr(C)]
pub struct FLOATING_SAVE_AREA {
pub ControlWord: DWORD,
pub StatusWord: DWORD,
pub TagWord: DWORD,
pub ErrorOffset: DWORD,
pub ErrorSelector: DWORD,
pub DataOffset: DWORD,
pub DataSelector: DWORD,
pub RegisterArea: [u8; 80],
pub Spare0: DWORD,
}
}
#[cfg(target_arch = "x86_64")]
ffi! {
#[repr(C, align(8))]
pub struct CONTEXT {
pub P1Home: DWORDLONG,
pub P2Home: DWORDLONG,
pub P3Home: DWORDLONG,
pub P4Home: DWORDLONG,
pub P5Home: DWORDLONG,
pub P6Home: DWORDLONG,
pub ContextFlags: DWORD,
pub MxCsr: DWORD,
pub SegCs: WORD,
pub SegDs: WORD,
pub SegEs: WORD,
pub SegFs: WORD,
pub SegGs: WORD,
pub SegSs: WORD,
pub EFlags: DWORD,
pub Dr0: DWORDLONG,
pub Dr1: DWORDLONG,
pub Dr2: DWORDLONG,
pub Dr3: DWORDLONG,
pub Dr6: DWORDLONG,
pub Dr7: DWORDLONG,
pub Rax: DWORDLONG,
pub Rcx: DWORDLONG,
pub Rdx: DWORDLONG,
pub Rbx: DWORDLONG,
pub Rsp: DWORDLONG,
pub Rbp: DWORDLONG,
pub Rsi: DWORDLONG,
pub Rdi: DWORDLONG,
pub R8: DWORDLONG,
pub R9: DWORDLONG,
pub R10: DWORDLONG,
pub R11: DWORDLONG,
pub R12: DWORDLONG,
pub R13: DWORDLONG,
pub R14: DWORDLONG,
pub R15: DWORDLONG,
pub Rip: DWORDLONG,
pub FltSave: FLOATING_SAVE_AREA,
pub VectorRegister: [M128A; 26],
pub VectorControl: DWORDLONG,
pub DebugControl: DWORDLONG,
pub LastBranchToRip: DWORDLONG,
pub LastBranchFromRip: DWORDLONG,
pub LastExceptionToRip: DWORDLONG,
pub LastExceptionFromRip: DWORDLONG,
}
#[repr(C)]
pub struct M128A {
pub Low: u64,
pub High: i64,
}
}
#[repr(C)]
#[cfg(target_arch = "x86_64")]
#[derive(Copy, Clone)]
pub struct FLOATING_SAVE_AREA {
_Dummy: [u8; 512],
}
#[cfg(target_arch = "arm")]
ffi! {
// #[repr(C)]
// pub struct NEON128 {
// pub Low: ULONG64,
// pub High: LONG64,
// }
// pub type PNEON128 = *mut NEON128;
#[repr(C)]
pub struct CONTEXT_u {
// pub Q: [NEON128; 16],
pub D: [ULONG64; 32],
// pub S: [DWORD; 32],
}
pub const ARM_MAX_BREAKPOINTS: usize = 8;
pub const ARM_MAX_WATCHPOINTS: usize = 1;
#[repr(C)]
pub struct CONTEXT {
pub ContextFlags: DWORD,
pub R0: DWORD,
pub R1: DWORD,
pub R2: DWORD,
pub R3: DWORD,
pub R4: DWORD,
pub R5: DWORD,
pub R6: DWORD,
pub R7: DWORD,
pub R8: DWORD,
pub R9: DWORD,
pub R10: DWORD,
pub R11: DWORD,
pub R12: DWORD,
pub Sp: DWORD,
pub Lr: DWORD,
pub Pc: DWORD,
pub Cpsr: DWORD,
pub Fpsrc: DWORD,
pub Padding: DWORD,
pub u: CONTEXT_u,
pub Bvr: [DWORD; ARM_MAX_BREAKPOINTS],
pub Bcr: [DWORD; ARM_MAX_BREAKPOINTS],
pub Wvr: [DWORD; ARM_MAX_WATCHPOINTS],
pub Wcr: [DWORD; ARM_MAX_WATCHPOINTS],
pub Padding2: [DWORD; 2],
}
} // IFDEF(arm)

View File

@ -1,16 +0,0 @@
#[inline(never)]
pub fn callback<F>(f: F)
where
F: FnOnce((&'static str, u32)),
{
f((file!(), line!()))
}
#[inline(always)]
#[cfg_attr(feature = "coresymbolication", inline(never))]
pub fn callback_inlined<F>(f: F)
where
F: FnOnce((&'static str, u32)),
{
f((file!(), line!()))
}

View File

@ -1,92 +0,0 @@
mod auxiliary;
macro_rules! pos {
() => {
(file!(), line!())
};
}
macro_rules! check {
($($pos:expr),*) => ({
verify(&[$($pos,)* pos!()]);
})
}
type Pos = (&'static str, u32);
#[test]
fn doit() {
outer(pos!());
}
#[inline(never)]
fn outer(main_pos: Pos) {
inner(main_pos, pos!());
inner_inlined(main_pos, pos!());
}
#[inline(never)]
#[rustfmt::skip]
fn inner(main_pos: Pos, outer_pos: Pos) {
check!(main_pos, outer_pos);
check!(main_pos, outer_pos);
let inner_pos = pos!(); auxiliary::callback(|aux_pos| {
check!(main_pos, outer_pos, inner_pos, aux_pos);
});
let inner_pos = pos!(); auxiliary::callback_inlined(|aux_pos| {
check!(main_pos, outer_pos, inner_pos, aux_pos);
});
}
#[inline(always)]
#[cfg_attr(feature = "coresymbolication", inline(never))]
#[rustfmt::skip]
fn inner_inlined(main_pos: Pos, outer_pos: Pos) {
check!(main_pos, outer_pos);
check!(main_pos, outer_pos);
#[inline(always)]
#[cfg_attr(feature = "coresymbolication", inline(never))]
fn inner_further_inlined(main_pos: Pos, outer_pos: Pos, inner_pos: Pos) {
check!(main_pos, outer_pos, inner_pos);
}
inner_further_inlined(main_pos, outer_pos, pos!());
let inner_pos = pos!(); auxiliary::callback(|aux_pos| {
check!(main_pos, outer_pos, inner_pos, aux_pos);
});
let inner_pos = pos!(); auxiliary::callback_inlined(|aux_pos| {
check!(main_pos, outer_pos, inner_pos, aux_pos);
});
// this tests a distinction between two independent calls to the inlined function.
// (un)fortunately, LLVM somehow merges two consecutive such calls into one node.
inner_further_inlined(main_pos, outer_pos, pos!());
}
fn verify(filelines: &[Pos]) {
let trace = backtrace::Backtrace::new();
println!("-----------------------------------");
println!("looking for:");
for (file, line) in filelines.iter().rev() {
println!("\t{}:{}", file, line);
}
println!("found:\n{:?}", trace);
let mut symbols = trace.frames().iter().flat_map(|frame| frame.symbols());
let mut iter = filelines.iter().rev();
while let Some((file, line)) = iter.next() {
loop {
let sym = match symbols.next() {
Some(sym) => sym,
None => panic!("failed to find {}:{}", file, line),
};
if let Some(filename) = sym.filename() {
if let Some(lineno) = sym.lineno() {
if filename.ends_with(file) && lineno == *line {
break;
}
}
}
}
}
}

View File

@ -1,72 +0,0 @@
use std::env;
use std::panic;
use std::process::Command;
use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
use std::sync::Arc;
use std::thread;
const PANICS: usize = 100;
const THREADS: usize = 8;
const VAR: &str = "__THE_TEST_YOU_ARE_LUKE";
fn main() {
// These run in docker containers on CI where they can't re-exec the test,
// so just skip these for CI. No other reason this can't run on those
// platforms though.
if cfg!(unix) && (cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64")) {
println!("test result: ok");
return;
}
if env::var(VAR).is_err() {
parent();
} else {
child();
}
}
fn parent() {
let me = env::current_exe().unwrap();
let result = Command::new(&me)
.env("RUST_BACKTRACE", "1")
.env(VAR, "1")
.output()
.unwrap();
if result.status.success() {
println!("test result: ok");
return;
}
println!("stdout:\n{}", String::from_utf8_lossy(&result.stdout));
println!("stderr:\n{}", String::from_utf8_lossy(&result.stderr));
println!("code: {}", result.status);
panic!();
}
fn child() {
let done = Arc::new(AtomicBool::new(false));
let done2 = done.clone();
let a = thread::spawn(move || {
while !done2.load(SeqCst) {
format!("{:?}", backtrace::Backtrace::new());
}
});
let threads = (0..THREADS)
.map(|_| {
thread::spawn(|| {
for _ in 0..PANICS {
assert!(panic::catch_unwind(|| {
panic!();
})
.is_err());
}
})
})
.collect::<Vec<_>>();
for thread in threads {
thread.join().unwrap();
}
done.store(true, SeqCst);
a.join().unwrap();
}

View File

@ -1,50 +0,0 @@
extern crate backtrace;
use backtrace::Backtrace;
// 50-character module name
mod _234567890_234567890_234567890_234567890_234567890 {
// 50-character struct name
#[allow(non_camel_case_types)]
pub struct _234567890_234567890_234567890_234567890_234567890<T>(T);
impl<T> _234567890_234567890_234567890_234567890_234567890<T> {
#[allow(dead_code)]
pub fn new() -> crate::Backtrace {
crate::Backtrace::new()
}
}
}
// Long function names must be truncated to (MAX_SYM_NAME - 1) characters.
// Only run this test for msvc, since gnu prints "<no info>" for all frames.
#[test]
#[cfg(all(windows, feature = "dbghelp", target_env = "msvc"))]
fn test_long_fn_name() {
use _234567890_234567890_234567890_234567890_234567890::_234567890_234567890_234567890_234567890_234567890 as S;
// 10 repetitions of struct name, so fully qualified function name is
// atleast 10 * (50 + 50) * 2 = 2000 characters long.
// It's actually longer since it also includes `::`, `<>` and the
// name of the current module
let bt = S::<S<S<S<S<S<S<S<S<S<i32>>>>>>>>>>::new();
println!("{:?}", bt);
let mut found_long_name_frame = false;
for frame in bt.frames() {
let symbols = frame.symbols();
if symbols.is_empty() {
continue;
}
if let Some(function_name) = symbols[0].name() {
let function_name = function_name.as_str().unwrap();
if function_name.contains("::_234567890_234567890_234567890_234567890_234567890") {
found_long_name_frame = true;
assert!(function_name.len() > 200);
}
}
}
assert!(found_long_name_frame);
}

View File

@ -1,49 +0,0 @@
extern crate backtrace;
use backtrace::Backtrace;
// This test only works on platforms which have a working `symbol_address`
// function for frames which reports the starting address of a symbol. As a
// result it's only enabled on a few platforms.
const ENABLED: bool = cfg!(all(
// Windows hasn't really been tested, and OSX doesn't support actually
// finding an enclosing frame, so disable this
target_os = "linux",
// This is the only method currently that supports accurate enough
// backtraces for this test to work.
feature = "libunwind",
// On ARM finding the enclosing function is simply returning the ip itself.
not(target_arch = "arm"),
));
#[test]
fn backtrace_new_unresolved_should_start_with_call_site_trace() {
if !ENABLED {
return;
}
let mut b = Backtrace::new_unresolved();
b.resolve();
println!("{:?}", b);
assert!(!b.frames().is_empty());
let this_ip = backtrace_new_unresolved_should_start_with_call_site_trace as usize;
println!("this_ip: {:?}", this_ip as *const usize);
let frame_ip = b.frames().first().unwrap().symbol_address() as usize;
assert_eq!(this_ip, frame_ip);
}
#[test]
fn backtrace_new_should_start_with_call_site_trace() {
if !ENABLED {
return;
}
let b = Backtrace::new();
println!("{:?}", b);
assert!(!b.frames().is_empty());
let this_ip = backtrace_new_should_start_with_call_site_trace as usize;
let frame_ip = b.frames().first().unwrap().symbol_address() as usize;
assert_eq!(this_ip, frame_ip);
}

View File

@ -1,250 +0,0 @@
extern crate backtrace;
use backtrace::Frame;
use std::thread;
static LIBUNWIND: bool = cfg!(all(unix, feature = "libunwind"));
static UNIX_BACKTRACE: bool = cfg!(all(unix, feature = "unix-backtrace"));
static LIBBACKTRACE: bool = cfg!(feature = "libbacktrace") && !cfg!(target_os = "fuchsia");
static CORESYMBOLICATION: bool = cfg!(all(
any(target_os = "macos", target_os = "ios"),
feature = "coresymbolication"
));
static DLADDR: bool = cfg!(all(unix, feature = "dladdr")) && !cfg!(target_os = "fuchsia");
static DBGHELP: bool = cfg!(all(windows, feature = "dbghelp"));
static MSVC: bool = cfg!(target_env = "msvc");
static GIMLI_SYMBOLIZE: bool = cfg!(all(feature = "gimli-symbolize", unix, target_os = "linux"));
#[test]
// FIXME: shouldn't ignore this test on i686-msvc, unsure why it's failing
#[cfg_attr(all(target_arch = "x86", target_env = "msvc"), ignore)]
#[rustfmt::skip] // we care about line numbers here
fn smoke_test_frames() {
frame_1(line!());
#[inline(never)] fn frame_1(start_line: u32) { frame_2(start_line) }
#[inline(never)] fn frame_2(start_line: u32) { frame_3(start_line) }
#[inline(never)] fn frame_3(start_line: u32) { frame_4(start_line) }
#[inline(never)] fn frame_4(start_line: u32) {
let mut v = Vec::new();
backtrace::trace(|cx| {
v.push(cx.clone());
true
});
if v.len() < 5 {
assert!(!LIBUNWIND);
assert!(!UNIX_BACKTRACE);
assert!(!DBGHELP);
return;
}
// Various platforms have various bits of weirdness about their
// backtraces. To find a good starting spot let's search through the
// frames
let target = frame_4 as usize;
let offset = v
.iter()
.map(|frame| frame.symbol_address() as usize)
.enumerate()
.filter_map(|(i, sym)| {
if sym >= target {
Some((sym, i))
} else {
None
}
})
.min()
.unwrap()
.1;
let mut frames = v[offset..].iter();
assert_frame(
frames.next().unwrap(),
frame_4 as usize,
"frame_4",
"tests/smoke.rs",
start_line + 6,
);
assert_frame(
frames.next().unwrap(),
frame_3 as usize,
"frame_3",
"tests/smoke.rs",
start_line + 3,
);
assert_frame(
frames.next().unwrap(),
frame_2 as usize,
"frame_2",
"tests/smoke.rs",
start_line + 2,
);
assert_frame(
frames.next().unwrap(),
frame_1 as usize,
"frame_1",
"tests/smoke.rs",
start_line + 1,
);
assert_frame(
frames.next().unwrap(),
smoke_test_frames as usize,
"smoke_test_frames",
"",
0,
);
}
fn assert_frame(
frame: &Frame,
actual_fn_pointer: usize,
expected_name: &str,
expected_file: &str,
expected_line: u32,
) {
backtrace::resolve_frame(frame, |sym| {
print!("symbol ip:{:?} address:{:?} ", frame.ip(), frame.symbol_address());
if let Some(name) = sym.name() {
print!("name:{} ", name);
}
if let Some(file) = sym.filename() {
print!("file:{} ", file.display());
}
if let Some(lineno) = sym.lineno() {
print!("lineno:{} ", lineno);
}
println!();
});
let ip = frame.ip() as usize;
let sym = frame.symbol_address() as usize;
assert!(ip >= sym);
assert!(
sym >= actual_fn_pointer,
"{:?} < {:?} ({} {}:{})",
sym as *const usize,
actual_fn_pointer as *const usize,
expected_name,
expected_file,
expected_line,
);
// windows dbghelp is *quite* liberal (and wrong) in many of its reports
// right now...
//
// This assertion can also fail for release builds, so skip it there
if !DBGHELP && cfg!(debug_assertions) {
assert!(sym - actual_fn_pointer < 1024);
}
let mut resolved = 0;
let can_resolve = DLADDR || LIBBACKTRACE || CORESYMBOLICATION || DBGHELP || GIMLI_SYMBOLIZE;
let mut name = None;
let mut addr = None;
let mut line = None;
let mut file = None;
backtrace::resolve_frame(frame, |sym| {
resolved += 1;
name = sym.name().map(|v| v.to_string());
addr = sym.addr();
line = sym.lineno();
file = sym.filename().map(|v| v.to_path_buf());
});
// dbghelp doesn't always resolve symbols right now
match resolved {
0 => return assert!(!can_resolve || DBGHELP),
_ => {}
}
// * linux dladdr doesn't work (only consults local symbol table)
// * windows dbghelp isn't great for GNU
if can_resolve && !(cfg!(target_os = "linux") && DLADDR) && !(DBGHELP && !MSVC) {
let name = name.expect("didn't find a name");
// in release mode names get weird as functions can get merged
// together with `mergefunc`, so only assert this in debug mode
if cfg!(debug_assertions) {
assert!(
name.contains(expected_name),
"didn't find `{}` in `{}`",
expected_name,
name
);
}
}
if can_resolve {
addr.expect("didn't find a symbol");
}
if (LIBBACKTRACE || CORESYMBOLICATION || (DBGHELP && MSVC)) && cfg!(debug_assertions) {
let line = line.expect("didn't find a line number");
let file = file.expect("didn't find a line number");
if !expected_file.is_empty() {
assert!(
file.ends_with(expected_file),
"{:?} didn't end with {:?}",
file,
expected_file
);
}
if expected_line != 0 {
assert!(
line == expected_line,
"bad line number on frame for `{}`: {} != {}",
expected_name,
line,
expected_line
);
}
}
}
}
#[test]
fn many_threads() {
let threads = (0..16)
.map(|_| {
thread::spawn(|| {
for _ in 0..16 {
backtrace::trace(|frame| {
backtrace::resolve(frame.ip(), |symbol| {
let _s = symbol.name().map(|s| s.to_string());
});
true
});
}
})
})
.collect::<Vec<_>>();
for t in threads {
t.join().unwrap()
}
}
#[test]
#[cfg(feature = "rustc-serialize")]
fn is_rustc_serialize() {
extern crate rustc_serialize;
fn is_encode<T: rustc_serialize::Encodable>() {}
fn is_decode<T: rustc_serialize::Decodable>() {}
is_encode::<backtrace::Backtrace>();
is_decode::<backtrace::Backtrace>();
}
#[test]
#[cfg(feature = "serde")]
fn is_serde() {
extern crate serde;
fn is_serialize<T: serde::ser::Serialize>() {}
fn is_deserialize<T: serde::de::DeserializeOwned>() {}
is_serialize::<backtrace::Backtrace>();
is_deserialize::<backtrace::Backtrace>();
}

File diff suppressed because one or more lines are too long

View File

@ -1,41 +0,0 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "backtrace-sys"
version = "0.1.34"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
build = "build.rs"
description = "Bindings to the libbacktrace gcc library\n"
homepage = "https://github.com/alexcrichton/backtrace-rs"
documentation = "http://alexcrichton.com/backtrace-rs"
license = "MIT/Apache-2.0"
repository = "https://github.com/alexcrichton/backtrace-rs"
[dependencies.compiler_builtins]
version = "0.1.2"
optional = true
[dependencies.core]
version = "1.0.0"
optional = true
package = "rustc-std-workspace-core"
[dependencies.libc]
version = "0.2"
default-features = false
[build-dependencies.cc]
version = "1.0.37"
[features]
backtrace-sys = []
default = ["backtrace-sys"]
rustc-dep-of-std = ["core", "compiler_builtins"]

View File

@ -1,166 +0,0 @@
extern crate cc;
use std::env;
use std::fs::File;
use std::path::PathBuf;
fn main() {
let target = env::var("TARGET").unwrap();
if !cfg!(feature = "backtrace-sys") || // without this feature, this crate does nothing
target.contains("msvc") || // libbacktrace isn't used on MSVC windows
target.contains("emscripten") || // no way this will ever compile for emscripten
target.contains("cloudabi") ||
target.contains("hermit") ||
target.contains("wasm32") ||
target.contains("fuchsia") ||
target.contains("uclibc")
{
println!("cargo:rustc-cfg=empty");
return;
}
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let mut build = cc::Build::new();
build
.include("src/libbacktrace")
.include(&out_dir)
.warnings(false)
.file("src/libbacktrace/alloc.c")
.file("src/libbacktrace/dwarf.c")
.file("src/libbacktrace/fileline.c")
.file("src/libbacktrace/posix.c")
.file("src/libbacktrace/sort.c")
.file("src/libbacktrace/state.c");
// `mmap` does not exist on Windows, so we use
// the less efficient `read`-based code.
// Using `mmap` on macOS causes weird isseus - see
// https://github.com/rust-lang/rust/pull/45866
if target.contains("windows") || target.contains("darwin") {
build.file("src/libbacktrace/read.c");
} else {
build.file("src/libbacktrace/mmapio.c");
}
// No need to have any symbols reexported form shared objects
build.flag("-fvisibility=hidden");
if target.contains("darwin") {
build.file("src/libbacktrace/macho.c");
} else if target.contains("windows") {
build.file("src/libbacktrace/pecoff.c");
} else {
build.file("src/libbacktrace/elf.c");
let pointer_width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap();
if pointer_width == "64" {
build.define("BACKTRACE_ELF_SIZE", "64");
} else {
build.define("BACKTRACE_ELF_SIZE", "32");
}
}
File::create(out_dir.join("backtrace-supported.h")).unwrap();
build.define("BACKTRACE_SUPPORTED", "1");
build.define("BACKTRACE_USES_MALLOC", "1");
build.define("BACKTRACE_SUPPORTS_THREADS", "0");
build.define("BACKTRACE_SUPPORTS_DATA", "0");
File::create(out_dir.join("config.h")).unwrap();
if target.contains("android") {
maybe_enable_dl_iterate_phdr_android(&mut build);
} else if !target.contains("apple-ios")
&& !target.contains("solaris")
&& !target.contains("redox")
&& !target.contains("haiku")
&& !target.contains("vxworks")
{
build.define("HAVE_DL_ITERATE_PHDR", "1");
}
build.define("_GNU_SOURCE", "1");
build.define("_LARGE_FILES", "1");
// When we're built as part of the Rust compiler, this is used to enable
// debug information in libbacktrace itself.
let any_debug = env::var("RUSTC_DEBUGINFO").unwrap_or_default() == "true"
|| env::var("RUSTC_DEBUGINFO_LINES").unwrap_or_default() == "true";
build.debug(any_debug);
let syms = [
"backtrace_full",
"backtrace_dwarf_add",
"backtrace_initialize",
"backtrace_pcinfo",
"backtrace_syminfo",
"backtrace_get_view",
"backtrace_release_view",
"backtrace_alloc",
"backtrace_free",
"backtrace_vector_finish",
"backtrace_vector_grow",
"backtrace_vector_release",
"backtrace_close",
"backtrace_open",
"backtrace_print",
"backtrace_simple",
"backtrace_qsort",
"backtrace_create_state",
"backtrace_uncompress_zdebug",
// These should be `static` in C, but they aren't...
"macho_get_view",
"macho_symbol_type_relevant",
"macho_get_commands",
"macho_try_dsym",
"macho_try_dwarf",
"macho_get_addr_range",
"macho_get_uuid",
"macho_add",
"macho_add_symtab",
"macho_file_to_host_u64",
"macho_file_to_host_u32",
"macho_file_to_host_u16",
];
let prefix = if cfg!(feature = "rustc-dep-of-std") {
println!("cargo:rustc-cfg=rdos");
"__rdos_"
} else {
println!("cargo:rustc-cfg=rbt");
"__rbt_"
};
for sym in syms.iter() {
build.define(sym, &format!("{}{}", prefix, sym)[..]);
}
build.compile("backtrace");
}
// The `dl_iterate_phdr` API was added in Android API 21+ (according to #227),
// so if we can dynamically detect an appropriately configured C compiler for
// that then let's enable the `dl_iterate_phdr` API, otherwise Android just
// won't have any information.
fn maybe_enable_dl_iterate_phdr_android(build: &mut cc::Build) {
let expansion = cc::Build::new().file("src/android-api.c").expand();
let expansion = match std::str::from_utf8(&expansion) {
Ok(s) => s,
Err(_) => return,
};
println!("expanded android version detection:\n{}", expansion);
let marker = "APIVERSION";
let i = match expansion.find(marker) {
Some(i) => i,
None => return,
};
let version = match expansion[i + marker.len() + 1..].split_whitespace().next() {
Some(s) => s,
None => return,
};
let version = match version.parse::<u32>() {
Ok(n) => n,
Err(_) => return,
};
if version >= 21 {
build.define("HAVE_DL_ITERATE_PHDR", "1");
}
}

View File

@ -1,4 +0,0 @@
// Used from the build script to detect the value of the `__ANDROID_API__`
// builtin #define
APIVERSION __ANDROID_API__

View File

@ -1,58 +0,0 @@
#![allow(bad_style)]
#![no_std]
extern crate libc;
#[cfg(not(empty))]
pub use self::bindings::*;
#[cfg(not(empty))]
mod bindings {
use libc::{c_char, c_int, c_void, uintptr_t};
pub type backtrace_syminfo_callback = extern "C" fn(
data: *mut c_void,
pc: uintptr_t,
symname: *const c_char,
symval: uintptr_t,
symsize: uintptr_t,
);
pub type backtrace_full_callback = extern "C" fn(
data: *mut c_void,
pc: uintptr_t,
filename: *const c_char,
lineno: c_int,
function: *const c_char,
) -> c_int;
pub type backtrace_error_callback =
extern "C" fn(data: *mut c_void, msg: *const c_char, errnum: c_int);
pub enum backtrace_state {}
extern "C" {
#[cfg_attr(rdos, link_name = "__rdos_backtrace_create_state")]
#[cfg_attr(rbt, link_name = "__rbt_backtrace_create_state")]
pub fn backtrace_create_state(
filename: *const c_char,
threaded: c_int,
error: backtrace_error_callback,
data: *mut c_void,
) -> *mut backtrace_state;
#[cfg_attr(rdos, link_name = "__rdos_backtrace_syminfo")]
#[cfg_attr(rbt, link_name = "__rbt_backtrace_syminfo")]
pub fn backtrace_syminfo(
state: *mut backtrace_state,
addr: uintptr_t,
cb: backtrace_syminfo_callback,
error: backtrace_error_callback,
data: *mut c_void,
) -> c_int;
#[cfg_attr(rdos, link_name = "__rdos_backtrace_pcinfo")]
#[cfg_attr(rbt, link_name = "__rbt_backtrace_pcinfo")]
pub fn backtrace_pcinfo(
state: *mut backtrace_state,
addr: uintptr_t,
cb: backtrace_full_callback,
error: backtrace_error_callback,
data: *mut c_void,
) -> c_int;
}
}

View File

@ -1,29 +0,0 @@
# Copyright (C) 2012-2016 Free Software Foundation, Inc.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# (1) Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# (2) Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# (3) The name of the author may not be used to
# endorse or promote products derived from this software without
# specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,206 +0,0 @@
# Makefile.am -- Backtrace Makefile.
# Copyright (C) 2012-2018 Free Software Foundation, Inc.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# (1) Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# (2) Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# (3) The name of the author may not be used to
# endorse or promote products derived from this software without
# specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
ACLOCAL_AMFLAGS = -I config
AM_CFLAGS = $(EXTRA_FLAGS) $(WARN_FLAGS) $(PIC_FLAG)
include_HEADERS = backtrace.h backtrace-supported.h
lib_LTLIBRARIES = libbacktrace.la
libbacktrace_la_SOURCES = \
backtrace.h \
atomic.c \
dwarf.c \
fileline.c \
internal.h \
posix.c \
print.c \
sort.c \
state.c
BACKTRACE_FILES = \
backtrace.c \
simple.c \
nounwind.c
FORMAT_FILES = \
elf.c \
pecoff.c \
unknown.c \
xcoff.c
VIEW_FILES = \
read.c \
mmapio.c
ALLOC_FILES = \
alloc.c \
mmap.c
EXTRA_libbacktrace_la_SOURCES = \
$(BACKTRACE_FILES) \
$(FORMAT_FILES) \
$(VIEW_FILES) \
$(ALLOC_FILES)
libbacktrace_la_LIBADD = \
$(BACKTRACE_FILE) \
$(FORMAT_FILE) \
$(VIEW_FILE) \
$(ALLOC_FILE)
libbacktrace_la_DEPENDENCIES = $(libbacktrace_la_LIBADD)
# Testsuite.
check_PROGRAMS =
CLEANFILES =
TESTS = $(check_PROGRAMS)
if NATIVE
btest_SOURCES = btest.c testlib.c
btest_CFLAGS = $(AM_CFLAGS) -g -O
btest_LDADD = libbacktrace.la
check_PROGRAMS += btest
btest_static_SOURCES = btest.c testlib.c
btest_static_CFLAGS = $(AM_CFLAGS) -g -O
btest_static_LDADD = libbacktrace.la
btest_static_LDFLAGS = -static-libtool-libs
check_PROGRAMS += btest_static
stest_SOURCES = stest.c
stest_LDADD = libbacktrace.la
check_PROGRAMS += stest
ztest_SOURCES = ztest.c testlib.c
ztest_CFLAGS = -DSRCDIR=\"$(srcdir)\"
ztest_LDADD = libbacktrace.la
if HAVE_ZLIB
ztest_LDADD += -lz
endif
ztest_LDADD += $(CLOCK_GETTIME_LINK)
check_PROGRAMS += ztest
edtest_SOURCES = edtest.c edtest2_build.c testlib.c
edtest_LDADD = libbacktrace.la
check_PROGRAMS += edtest
edtest2_build.c: gen_edtest2_build; @true
gen_edtest2_build: $(srcdir)/edtest2.c
cat $(srcdir)/edtest2.c > tmp-edtest2_build.c
$(SHELL) $(srcdir)/move-if-change tmp-edtest2_build.c edtest2_build.c
echo timestamp > $@
CLEANFILES += edtest2_build.c gen_edtest2_build
if HAVE_PTHREAD
check_PROGRAMS += ttest
ttest_SOURCES = ttest.c testlib.c
ttest_CFLAGS = $(AM_CFLAGS) -pthread
ttest_LDADD = libbacktrace.la
endif HAVE_PTHREAD
if HAVE_OBJCOPY_DEBUGLINK
TESTS += dtest
dtest: btest_static
$(OBJCOPY) --only-keep-debug btest_static btest.debug
$(OBJCOPY) --strip-debug --add-gnu-debuglink=btest.debug btest_static dtest
CLEANFILES += dtest btest.debug
endif HAVE_OBJCOPY_DEBUGLINK
if HAVE_COMPRESSED_DEBUG
ctestg_SOURCES = btest.c testlib.c
ctestg_CFLAGS = $(AM_CFLAGS) -g
ctestg_LDFLAGS = -Wl,--compress-debug-sections=zlib-gnu
ctestg_LDADD = libbacktrace.la
ctesta_SOURCES = btest.c testlib.c
ctesta_CFLAGS = $(AM_CFLAGS) -g
ctesta_LDFLAGS = -Wl,--compress-debug-sections=zlib-gabi
ctesta_LDADD = libbacktrace.la
check_PROGRAMS += ctestg ctesta
endif
endif NATIVE
# We can't use automake's automatic dependency tracking, because it
# breaks when using bootstrap-lean. Automatic dependency tracking
# with GCC bootstrap will cause some of the objects to depend on
# header files in prev-gcc/include, e.g., stddef.h and stdarg.h. When
# using bootstrap-lean, prev-gcc is removed after each stage. When
# running "make install", those header files will be gone, causing the
# library to be rebuilt at install time. That may not succeed.
# These manual dependencies do not include dependencies on unwind.h,
# even though that is part of GCC, because where to find it depends on
# whether we are being built as a host library or a target library.
alloc.lo: config.h backtrace.h internal.h
backtrace.lo: config.h backtrace.h internal.h
btest.lo: backtrace.h backtrace-supported.h filenames.h
dwarf.lo: config.h filenames.h backtrace.h internal.h
elf.lo: config.h backtrace.h internal.h
fileline.lo: config.h backtrace.h internal.h
mmap.lo: config.h backtrace.h internal.h
mmapio.lo: config.h backtrace.h internal.h
nounwind.lo: config.h internal.h
pecoff.lo: config.h backtrace.h internal.h
posix.lo: config.h backtrace.h internal.h
print.lo: config.h backtrace.h internal.h
read.lo: config.h backtrace.h internal.h
simple.lo: config.h backtrace.h internal.h
sort.lo: config.h backtrace.h internal.h
stest.lo: config.h backtrace.h internal.h
state.lo: config.h backtrace.h backtrace-supported.h internal.h
unknown.lo: config.h backtrace.h internal.h
xcoff.lo: config.h backtrace.h internal.h

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +0,0 @@
# libbacktrace
A C library that may be linked into a C/C++ program to produce symbolic backtraces
Initially written by Ian Lance Taylor <iant@golang.org>.
This is version 1.0.
It is likely that this will always be version 1.0.
The libbacktrace library may be linked into a program or library and
used to produce symbolic backtraces.
Sample uses would be to print a detailed backtrace when an error
occurs or to gather detailed profiling information.
The libbacktrace library is provided under a BSD license.
See the source files for the exact license text.
The public functions are declared and documented in the header file
backtrace.h, which should be #include'd by a user of the library.
Building libbacktrace will generate a file backtrace-supported.h,
which a user of the library may use to determine whether backtraces
will work.
See the source file backtrace-supported.h.in for the macros that it
defines.
As of January 2018, libbacktrace only supports ELF, PE/COFF, and XCOFF
executables with DWARF debugging information.
The library is written to make it straightforward to add support for
other object file and debugging formats.
The library relies on the C++ unwind API defined at
https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
This API is provided by GCC.

View File

@ -1,72 +0,0 @@
dnl
dnl Check whether _Unwind_GetIPInfo is available without doing a link
dnl test so we can use this with libstdc++-v3 and libjava. Need to
dnl use $target to set defaults because automatic checking is not possible
dnl without a link test (and maybe even with a link test).
dnl
AC_DEFUN([GCC_CHECK_UNWIND_GETIPINFO], [
AC_ARG_WITH(system-libunwind,
[ --with-system-libunwind use installed libunwind])
# If system-libunwind was not specifically set, pick a default setting.
if test x$with_system_libunwind = x; then
case ${target} in
ia64-*-hpux*) with_system_libunwind=yes ;;
*) with_system_libunwind=no ;;
esac
fi
# Based on system-libunwind and target, do we have ipinfo?
if test x$with_system_libunwind = xyes; then
case ${target} in
ia64-*-*) have_unwind_getipinfo=no ;;
*) have_unwind_getipinfo=yes ;;
esac
else
# Darwin before version 9 does not have _Unwind_GetIPInfo.
changequote(,)
case ${target} in
*-*-darwin[3-8]|*-*-darwin[3-8].*) have_unwind_getipinfo=no ;;
*) have_unwind_getipinfo=yes ;;
esac
changequote([,])
fi
if test x$have_unwind_getipinfo = xyes; then
AC_DEFINE(HAVE_GETIPINFO, 1, [Define if _Unwind_GetIPInfo is available.])
fi
])
# ACX_PROG_CC_WARNING_OPTS(WARNINGS, [VARIABLE = WARN_CFLAGS])
# Sets @VARIABLE@ to the subset of the given options which the
# compiler accepts.
AC_DEFUN([ACX_PROG_CC_WARNING_OPTS],
[AC_REQUIRE([AC_PROG_CC])dnl
AC_LANG_PUSH(C)
m4_pushdef([acx_Var], [m4_default([$2], [WARN_CFLAGS])])dnl
AC_SUBST(acx_Var)dnl
m4_expand_once([acx_Var=
],m4_quote(acx_Var=))dnl
save_CFLAGS="$CFLAGS"
for real_option in $1; do
# Do the check with the no- prefix removed since gcc silently
# accepts any -Wno-* option on purpose
case $real_option in
-Wno-*) option=-W`expr x$real_option : 'x-Wno-\(.*\)'` ;;
*) option=$real_option ;;
esac
AS_VAR_PUSHDEF([acx_Woption], [acx_cv_prog_cc_warning_$option])
AC_CACHE_CHECK([whether $CC supports $option], acx_Woption,
[CFLAGS="$option"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],
[AS_VAR_SET(acx_Woption, yes)],
[AS_VAR_SET(acx_Woption, no)])
])
AS_IF([test AS_VAR_GET(acx_Woption) = yes],
[acx_Var="$acx_Var${acx_Var:+ }$real_option"])
AS_VAR_POPDEF([acx_Woption])dnl
done
CFLAGS="$save_CFLAGS"
m4_popdef([acx_Var])dnl
AC_LANG_POP(C)
])# ACX_PROG_CC_WARNING_OPTS

View File

@ -1,767 +0,0 @@
# generated automatically by aclocal 1.11.6 -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation,
# Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.64],,
[m4_warning([this file was generated for autoconf 2.64.
You have another version of autoconf. It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically `autoreconf'.])])
# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008, 2011 Free Software
# Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 1
# AM_AUTOMAKE_VERSION(VERSION)
# ----------------------------
# Automake X.Y traces this macro to ensure aclocal.m4 has been
# generated from the m4 files accompanying Automake X.Y.
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.11'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
m4_if([$1], [1.11.6], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
# _AM_AUTOCONF_VERSION(VERSION)
# -----------------------------
# aclocal traces this macro to find the Autoconf version.
# This is a private macro too. Using m4_define simplifies
# the logic in aclocal, which can simply ignore this definition.
m4_define([_AM_AUTOCONF_VERSION], [])
# AM_SET_CURRENT_AUTOMAKE_VERSION
# -------------------------------
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.11.6])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 1
# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
#
# Of course, Automake must honor this variable whenever it calls a
# tool from the auxiliary directory. The problem is that $srcdir (and
# therefore $ac_aux_dir as well) can be either absolute or relative,
# depending on how configure is run. This is pretty annoying, since
# it makes $ac_aux_dir quite unusable in subdirectories: in the top
# source directory, any form will work fine, but in subdirectories a
# relative path needs to be adjusted first.
#
# $ac_aux_dir/missing
# fails when called from a subdirectory if $ac_aux_dir is relative
# $top_srcdir/$ac_aux_dir/missing
# fails if $ac_aux_dir is absolute,
# fails when called from a subdirectory in a VPATH build with
# a relative $ac_aux_dir
#
# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
# are both prefixed by $srcdir. In an in-source build this is usually
# harmless because $srcdir is `.', but things will broke when you
# start a VPATH build or use an absolute $srcdir.
#
# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
# and then we would define $MISSING as
# MISSING="\${SHELL} $am_aux_dir/missing"
# This will work as long as MISSING is not called from configure, because
# unfortunately $(top_srcdir) has no meaning in configure.
# However there are other variables, like CC, which are often used in
# configure, and could therefore not use this "fixed" $ac_aux_dir.
#
# Another solution, used here, is to always expand $ac_aux_dir to an
# absolute PATH. The drawback is that using absolute paths prevent a
# configured tree to be moved without reconfiguration.
AC_DEFUN([AM_AUX_DIR_EXPAND],
[dnl Rely on autoconf to set up CDPATH properly.
AC_PREREQ([2.50])dnl
# expand $ac_aux_dir to an absolute path
am_aux_dir=`cd $ac_aux_dir && pwd`
])
# AM_CONDITIONAL -*- Autoconf -*-
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 9
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
# -------------------------------------
# Define a conditional.
AC_DEFUN([AM_CONDITIONAL],
[AC_PREREQ(2.52)dnl
ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
AC_SUBST([$1_TRUE])dnl
AC_SUBST([$1_FALSE])dnl
_AM_SUBST_NOTMAKE([$1_TRUE])dnl
_AM_SUBST_NOTMAKE([$1_FALSE])dnl
m4_define([_AM_COND_VALUE_$1], [$2])dnl
if $2; then
$1_TRUE=
$1_FALSE='#'
else
$1_TRUE='#'
$1_FALSE=
fi
AC_CONFIG_COMMANDS_PRE(
[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
AC_MSG_ERROR([[conditional "$1" was never defined.
Usually this means the macro was only invoked conditionally.]])
fi])])
# Do all the work for Automake. -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 16
# This macro actually does too much. Some checks are only needed if
# your package does certain things. But this isn't really a big deal.
# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
# AM_INIT_AUTOMAKE([OPTIONS])
# -----------------------------------------------
# The call with PACKAGE and VERSION arguments is the old style
# call (pre autoconf-2.50), which is being phased out. PACKAGE
# and VERSION should now be passed to AC_INIT and removed from
# the call to AM_INIT_AUTOMAKE.
# We support both call styles for the transition. After
# the next Automake release, Autoconf can make the AC_INIT
# arguments mandatory, and then we can depend on a new Autoconf
# release and drop the old call support.
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_PREREQ([2.62])dnl
dnl Autoconf wants to disallow AM_ names. We explicitly allow
dnl the ones we care about.
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
AC_REQUIRE([AC_PROG_INSTALL])dnl
if test "`cd $srcdir && pwd`" != "`pwd`"; then
# Use -I$(srcdir) only when $(srcdir) != ., so that make's output
# is not polluted with repeated "-I."
AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
# test to see if srcdir already configured
if test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
fi
fi
# test whether we have cygpath
if test -z "$CYGPATH_W"; then
if (cygpath --version) >/dev/null 2>/dev/null; then
CYGPATH_W='cygpath -w'
else
CYGPATH_W=echo
fi
fi
AC_SUBST([CYGPATH_W])
# Define the identity of the package.
dnl Distinguish between old-style and new-style calls.
m4_ifval([$2],
[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
AC_SUBST([PACKAGE], [$1])dnl
AC_SUBST([VERSION], [$2])],
[_AM_SET_OPTIONS([$1])dnl
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
_AM_IF_OPTION([no-define],,
[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
# Some tools Automake needs.
AC_REQUIRE([AM_SANITY_CHECK])dnl
AC_REQUIRE([AC_ARG_PROGRAM])dnl
AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
AM_MISSING_PROG(AUTOCONF, autoconf)
AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
AM_MISSING_PROG(AUTOHEADER, autoheader)
AM_MISSING_PROG(MAKEINFO, makeinfo)
AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
AC_REQUIRE([AM_PROG_MKDIR_P])dnl
# We need awk for the "check" target. The system "awk" is bad on
# some platforms.
AC_REQUIRE([AC_PROG_AWK])dnl
AC_REQUIRE([AC_PROG_MAKE_SET])dnl
AC_REQUIRE([AM_SET_LEADING_DOT])dnl
_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
[_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
[_AM_PROG_TAR([v7])])])
_AM_IF_OPTION([no-dependencies],,
[AC_PROVIDE_IFELSE([AC_PROG_CC],
[_AM_DEPENDENCIES(CC)],
[define([AC_PROG_CC],
defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
AC_PROVIDE_IFELSE([AC_PROG_CXX],
[_AM_DEPENDENCIES(CXX)],
[define([AC_PROG_CXX],
defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJC],
[_AM_DEPENDENCIES(OBJC)],
[define([AC_PROG_OBJC],
defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
])
_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl
dnl The `parallel-tests' driver may need to know about EXEEXT, so add the
dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro
dnl is hooked onto _AC_COMPILER_EXEEXT early, see below.
AC_CONFIG_COMMANDS_PRE(dnl
[m4_provide_if([_AM_COMPILER_EXEEXT],
[AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
])
dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
dnl mangled by Autoconf and run in a shell conditional statement.
m4_define([_AC_COMPILER_EXEEXT],
m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
# When config.status generates a header, we must update the stamp-h file.
# This file resides in the same directory as the config header
# that is generated. The stamp files are numbered to have different names.
# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
# loop where config.status creates the headers, so we can generate
# our stamp files there.
AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
[# Compute $1's index in $config_headers.
_am_arg=$1
_am_stamp_count=1
for _am_header in $config_headers :; do
case $_am_header in
$_am_arg | $_am_arg:* )
break ;;
* )
_am_stamp_count=`expr $_am_stamp_count + 1` ;;
esac
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
# Copyright (C) 2001, 2003, 2005, 2008, 2011 Free Software Foundation,
# Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 1
# AM_PROG_INSTALL_SH
# ------------------
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
if test x"${install_sh}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
*)
install_sh="\${SHELL} $am_aux_dir/install-sh"
esac
fi
AC_SUBST(install_sh)])
# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 2
# Check whether the underlying file-system supports filenames
# with a leading dot. For instance MS-DOS doesn't.
AC_DEFUN([AM_SET_LEADING_DOT],
[rm -rf .tst 2>/dev/null
mkdir .tst 2>/dev/null
if test -d .tst; then
am__leading_dot=.
else
am__leading_dot=_
fi
rmdir .tst 2>/dev/null
AC_SUBST([am__leading_dot])])
# Add --enable-maintainer-mode option to configure. -*- Autoconf -*-
# From Jim Meyering
# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2008,
# 2011 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 5
# AM_MAINTAINER_MODE([DEFAULT-MODE])
# ----------------------------------
# Control maintainer-specific portions of Makefiles.
# Default is to disable them, unless `enable' is passed literally.
# For symmetry, `disable' may be passed as well. Anyway, the user
# can override the default with the --enable/--disable switch.
AC_DEFUN([AM_MAINTAINER_MODE],
[m4_case(m4_default([$1], [disable]),
[enable], [m4_define([am_maintainer_other], [disable])],
[disable], [m4_define([am_maintainer_other], [enable])],
[m4_define([am_maintainer_other], [enable])
m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])])
AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
dnl maintainer-mode's default is 'disable' unless 'enable' is passed
AC_ARG_ENABLE([maintainer-mode],
[ --][am_maintainer_other][-maintainer-mode am_maintainer_other make rules and dependencies not useful
(and sometimes confusing) to the casual installer],
[USE_MAINTAINER_MODE=$enableval],
[USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes]))
AC_MSG_RESULT([$USE_MAINTAINER_MODE])
AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
MAINT=$MAINTAINER_MODE_TRUE
AC_SUBST([MAINT])dnl
]
)
AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE])
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 6
# AM_MISSING_PROG(NAME, PROGRAM)
# ------------------------------
AC_DEFUN([AM_MISSING_PROG],
[AC_REQUIRE([AM_MISSING_HAS_RUN])
$1=${$1-"${am_missing_run}$2"}
AC_SUBST($1)])
# AM_MISSING_HAS_RUN
# ------------------
# Define MISSING if not defined so far and test if it supports --run.
# If it does, set am_missing_run to use it, otherwise, to nothing.
AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([missing])dnl
if test x"${MISSING+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
*)
MISSING="\${SHELL} $am_aux_dir/missing" ;;
esac
fi
# Use eval to expand $SHELL
if eval "$MISSING --run true"; then
am_missing_run="$MISSING --run "
else
am_missing_run=
AC_MSG_WARN([`missing' script is too old or missing])
fi
])
# Copyright (C) 2003, 2004, 2005, 2006, 2011 Free Software Foundation,
# Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 1
# AM_PROG_MKDIR_P
# ---------------
# Check for `mkdir -p'.
AC_DEFUN([AM_PROG_MKDIR_P],
[AC_PREREQ([2.60])dnl
AC_REQUIRE([AC_PROG_MKDIR_P])dnl
dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
dnl while keeping a definition of mkdir_p for backward compatibility.
dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
dnl Makefile.ins that do not define MKDIR_P, so we do our own
dnl adjustment using top_builddir (which is defined more often than
dnl MKDIR_P).
AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
case $mkdir_p in
[[\\/$]]* | ?:[[\\/]]*) ;;
*/*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
esac
])
# Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2012
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 6
# AM_ENABLE_MULTILIB([MAKEFILE], [REL-TO-TOP-SRCDIR])
# ---------------------------------------------------
# Add --enable-multilib to configure.
AC_DEFUN([AM_ENABLE_MULTILIB],
[m4_warn([obsolete], [$0 will be removed from Automake core soon.
Files implementing the "multilib" feature are (and will remain) available
to the 'contrib/' directory in the Automake distribution.])]dnl
[# Default to --enable-multilib
AC_ARG_ENABLE(multilib,
[ --enable-multilib build many library versions (default)],
[case "$enableval" in
yes) multilib=yes ;;
no) multilib=no ;;
*) AC_MSG_ERROR([bad value $enableval for multilib option]) ;;
esac],
[multilib=yes])
# We may get other options which we leave undocumented:
# --with-target-subdir, --with-multisrctop, --with-multisubdir
# See config-ml.in if you want the gory details.
if test "$srcdir" = "."; then
if test "$with_target_subdir" != "."; then
multi_basedir="$srcdir/$with_multisrctop../$2"
else
multi_basedir="$srcdir/$with_multisrctop$2"
fi
else
multi_basedir="$srcdir/$2"
fi
AC_SUBST(multi_basedir)
# Even if the default multilib is not a cross compilation,
# it may be that some of the other multilibs are.
if test $cross_compiling = no && test $multilib = yes \
&& test "x${with_multisubdir}" != x ; then
cross_compiling=maybe
fi
AC_OUTPUT_COMMANDS([
# Only add multilib support code if we just rebuilt the top-level
# Makefile.
case " $CONFIG_FILES " in
*" ]m4_default([$1],Makefile)[ "*)
ac_file=]m4_default([$1],Makefile)[ . ${multi_basedir}/config-ml.in
;;
esac],
[
srcdir="$srcdir"
host="$host"
target="$target"
with_multisubdir="$with_multisubdir"
with_multisrctop="$with_multisrctop"
with_target_subdir="$with_target_subdir"
ac_configure_args="${multilib_arg} ${ac_configure_args}"
multi_basedir="$multi_basedir"
CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
CC="$CC"])])dnl
# Helper functions for option handling. -*- Autoconf -*-
# Copyright (C) 2001, 2002, 2003, 2005, 2008, 2010 Free Software
# Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 5
# _AM_MANGLE_OPTION(NAME)
# -----------------------
AC_DEFUN([_AM_MANGLE_OPTION],
[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
# _AM_SET_OPTION(NAME)
# --------------------
# Set option NAME. Presently that only means defining a flag for this option.
AC_DEFUN([_AM_SET_OPTION],
[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
# _AM_SET_OPTIONS(OPTIONS)
# ------------------------
# OPTIONS is a space-separated list of Automake options.
AC_DEFUN([_AM_SET_OPTIONS],
[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
# -------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
# Check to make sure that the build environment is sane. -*- Autoconf -*-
# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 5
# AM_SANITY_CHECK
# ---------------
AC_DEFUN([AM_SANITY_CHECK],
[AC_MSG_CHECKING([whether build environment is sane])
# Just in case
sleep 1
echo timestamp > conftest.file
# Reject unsafe characters in $srcdir or the absolute working directory
# name. Accept space and tab only in the latter.
am_lf='
'
case `pwd` in
*[[\\\"\#\$\&\'\`$am_lf]]*)
AC_MSG_ERROR([unsafe absolute working directory name]);;
esac
case $srcdir in
*[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);;
esac
# Do `set' in a subshell so we don't clobber the current shell's
# arguments. Must try -L first in case configure is actually a
# symlink; some systems play weird games with the mod time of symlinks
# (eg FreeBSD returns the mod time of the symlink's containing
# directory).
if (
set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
if test "$[*]" = "X"; then
# -L didn't work.
set X `ls -t "$srcdir/configure" conftest.file`
fi
rm -f conftest.file
if test "$[*]" != "X $srcdir/configure conftest.file" \
&& test "$[*]" != "X conftest.file $srcdir/configure"; then
# If neither matched, then we have a broken ls. This can happen
# if, for instance, CONFIG_SHELL is bash and it inherits a
# broken ls alias from the environment. This has actually
# happened. Such a system could not be considered "sane".
AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
alias in your environment])
fi
test "$[2]" = conftest.file
)
then
# Ok.
:
else
AC_MSG_ERROR([newly created file is older than distributed files!
Check your system clock])
fi
AC_MSG_RESULT(yes)])
# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 1
# AM_PROG_INSTALL_STRIP
# ---------------------
# One issue with vendor `install' (even GNU) is that you can't
# specify the program used to strip binaries. This is especially
# annoying in cross-compiling environments, where the build's strip
# is unlikely to handle the host's binaries.
# Fortunately install-sh will honor a STRIPPROG variable, so we
# always use install-sh in `make install-strip', and initialize
# STRIPPROG with the value of the STRIP variable (set by the user).
AC_DEFUN([AM_PROG_INSTALL_STRIP],
[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
# Installed binaries are usually stripped using `strip' when the user
# run `make install-strip'. However `strip' might not be the right
# tool to use in cross-compilation environments, therefore Automake
# will honor the `STRIP' environment variable to overrule this program.
dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
if test "$cross_compiling" != no; then
AC_CHECK_TOOL([STRIP], [strip], :)
fi
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
# Copyright (C) 2006, 2008, 2010 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 3
# _AM_SUBST_NOTMAKE(VARIABLE)
# ---------------------------
# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
# This macro is traced by Automake.
AC_DEFUN([_AM_SUBST_NOTMAKE])
# AM_SUBST_NOTMAKE(VARIABLE)
# --------------------------
# Public sister of _AM_SUBST_NOTMAKE.
AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
# Check how to create a tarball. -*- Autoconf -*-
# Copyright (C) 2004, 2005, 2012 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 2
# _AM_PROG_TAR(FORMAT)
# --------------------
# Check how to create a tarball in format FORMAT.
# FORMAT should be one of `v7', `ustar', or `pax'.
#
# Substitute a variable $(am__tar) that is a command
# writing to stdout a FORMAT-tarball containing the directory
# $tardir.
# tardir=directory && $(am__tar) > result.tar
#
# Substitute a variable $(am__untar) that extract such
# a tarball read from stdin.
# $(am__untar) < result.tar
AC_DEFUN([_AM_PROG_TAR],
[# Always define AMTAR for backward compatibility. Yes, it's still used
# in the wild :-( We should find a proper way to deprecate it ...
AC_SUBST([AMTAR], ['$${TAR-tar}'])
m4_if([$1], [v7],
[am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
[m4_case([$1], [ustar],, [pax],,
[m4_fatal([Unknown tar format])])
AC_MSG_CHECKING([how to create a $1 tar archive])
# Loop over all known methods to create a tar archive until one works.
_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
_am_tools=${am_cv_prog_tar_$1-$_am_tools}
# Do not fold the above two line into one, because Tru64 sh and
# Solaris sh will not grok spaces in the rhs of `-'.
for _am_tool in $_am_tools
do
case $_am_tool in
gnutar)
for _am_tar in tar gnutar gtar;
do
AM_RUN_LOG([$_am_tar --version]) && break
done
am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
am__untar="$_am_tar -xf -"
;;
plaintar)
# Must skip GNU tar: if it does not support --format= it doesn't create
# ustar tarball either.
(tar --version) >/dev/null 2>&1 && continue
am__tar='tar chf - "$$tardir"'
am__tar_='tar chf - "$tardir"'
am__untar='tar xf -'
;;
pax)
am__tar='pax -L -x $1 -w "$$tardir"'
am__tar_='pax -L -x $1 -w "$tardir"'
am__untar='pax -r'
;;
cpio)
am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
am__untar='cpio -i -H $1 -d'
;;
none)
am__tar=false
am__tar_=false
am__untar=false
;;
esac
# If the value was cached, stop now. We just wanted to have am__tar
# and am__untar set.
test -n "${am_cv_prog_tar_$1}" && break
# tar/untar a dummy directory, and stop if the command works
rm -rf conftest.dir
mkdir conftest.dir
echo GrepMe > conftest.dir/file
AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
rm -rf conftest.dir
if test -s conftest.tar; then
AM_RUN_LOG([$am__untar <conftest.tar])
grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
fi
done
rm -rf conftest.dir
AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
AC_MSG_RESULT([$am_cv_prog_tar_$1])])
AC_SUBST([am__tar])
AC_SUBST([am__untar])
]) # _AM_PROG_TAR
m4_include([config/libtool.m4])
m4_include([config/ltoptions.m4])
m4_include([config/ltsugar.m4])
m4_include([config/ltversion.m4])
m4_include([config/lt~obsolete.m4])
m4_include([acinclude.m4])

View File

@ -1,156 +0,0 @@
/* alloc.c -- Memory allocation without mmap.
Copyright (C) 2012-2018 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Google.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
(1) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
#include "config.h"
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include "backtrace.h"
#include "internal.h"
/* Allocation routines to use on systems that do not support anonymous
mmap. This implementation just uses malloc, which means that the
backtrace functions may not be safely invoked from a signal
handler. */
/* Allocate memory like malloc. If ERROR_CALLBACK is NULL, don't
report an error. */
void *
backtrace_alloc (struct backtrace_state *state ATTRIBUTE_UNUSED,
size_t size, backtrace_error_callback error_callback,
void *data)
{
void *ret;
ret = malloc (size);
if (ret == NULL)
{
if (error_callback)
error_callback (data, "malloc", errno);
}
return ret;
}
/* Free memory. */
void
backtrace_free (struct backtrace_state *state ATTRIBUTE_UNUSED,
void *p, size_t size ATTRIBUTE_UNUSED,
backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
free (p);
}
/* Grow VEC by SIZE bytes. */
void *
backtrace_vector_grow (struct backtrace_state *state ATTRIBUTE_UNUSED,
size_t size, backtrace_error_callback error_callback,
void *data, struct backtrace_vector *vec)
{
void *ret;
if (size > vec->alc)
{
size_t alc;
void *base;
if (vec->size == 0)
alc = 32 * size;
else if (vec->size >= 4096)
alc = vec->size + 4096;
else
alc = 2 * vec->size;
if (alc < vec->size + size)
alc = vec->size + size;
base = realloc (vec->base, alc);
if (base == NULL)
{
error_callback (data, "realloc", errno);
return NULL;
}
vec->base = base;
vec->alc = alc - vec->size;
}
ret = (char *) vec->base + vec->size;
vec->size += size;
vec->alc -= size;
return ret;
}
/* Finish the current allocation on VEC. */
void *
backtrace_vector_finish (struct backtrace_state *state,
struct backtrace_vector *vec,
backtrace_error_callback error_callback,
void *data)
{
void *ret;
/* With this allocator we call realloc in backtrace_vector_grow,
which means we can't easily reuse the memory here. So just
release it. */
if (!backtrace_vector_release (state, vec, error_callback, data))
return NULL;
ret = vec->base;
vec->base = NULL;
vec->size = 0;
vec->alc = 0;
return ret;
}
/* Release any extra space allocated for VEC. */
int
backtrace_vector_release (struct backtrace_state *state ATTRIBUTE_UNUSED,
struct backtrace_vector *vec,
backtrace_error_callback error_callback,
void *data)
{
vec->base = realloc (vec->base, vec->size);
if (vec->base == NULL)
{
error_callback (data, "realloc", errno);
return 0;
}
vec->alc = 0;
return 1;
}

View File

@ -1,113 +0,0 @@
/* atomic.c -- Support for atomic functions if not present.
Copyright (C) 2013-2018 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Google.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
(1) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
#include "config.h"
#include <sys/types.h>
#include "backtrace.h"
#include "backtrace-supported.h"
#include "internal.h"
/* This file holds implementations of the atomic functions that are
used if the host compiler has the sync functions but not the atomic
functions, as is true of versions of GCC before 4.7. */
#if !defined (HAVE_ATOMIC_FUNCTIONS) && defined (HAVE_SYNC_FUNCTIONS)
/* Do an atomic load of a pointer. */
void *
backtrace_atomic_load_pointer (void *arg)
{
void **pp;
void *p;
pp = (void **) arg;
p = *pp;
while (!__sync_bool_compare_and_swap (pp, p, p))
p = *pp;
return p;
}
/* Do an atomic load of an int. */
int
backtrace_atomic_load_int (int *p)
{
int i;
i = *p;
while (!__sync_bool_compare_and_swap (p, i, i))
i = *p;
return i;
}
/* Do an atomic store of a pointer. */
void
backtrace_atomic_store_pointer (void *arg, void *p)
{
void **pp;
void *old;
pp = (void **) arg;
old = *pp;
while (!__sync_bool_compare_and_swap (pp, old, p))
old = *pp;
}
/* Do an atomic store of a size_t value. */
void
backtrace_atomic_store_size_t (size_t *p, size_t v)
{
size_t old;
old = *p;
while (!__sync_bool_compare_and_swap (p, old, v))
old = *p;
}
/* Do an atomic store of a int value. */
void
backtrace_atomic_store_int (int *p, int v)
{
size_t old;
old = *p;
while (!__sync_bool_compare_and_swap (p, old, v))
old = *p;
}
#endif

View File

@ -1,66 +0,0 @@
/* backtrace-supported.h.in -- Whether stack backtrace is supported.
Copyright (C) 2012-2016 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Google.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
(1) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
/* The file backtrace-supported.h.in is used by configure to generate
the file backtrace-supported.h. The file backtrace-supported.h may
be #include'd to see whether the backtrace library will be able to
get a backtrace and produce symbolic information. */
/* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library
should work, 0 if it will not. Libraries may #include this to make
other arrangements. */
#define BACKTRACE_SUPPORTED @BACKTRACE_SUPPORTED@
/* BACKTRACE_USES_MALLOC will be #define'd as 1 if the backtrace
library will call malloc as it works, 0 if it will call mmap
instead. This may be used to determine whether it is safe to call
the backtrace functions from a signal handler. In general this
only applies to calls like backtrace and backtrace_pcinfo. It does
not apply to backtrace_simple, which never calls malloc. It does
not apply to backtrace_print, which always calls fprintf and
therefore malloc. */
#define BACKTRACE_USES_MALLOC @BACKTRACE_USES_MALLOC@
/* BACKTRACE_SUPPORTS_THREADS will be #define'd as 1 if the backtrace
library is configured with threading support, 0 if not. If this is
0, the threaded parameter to backtrace_create_state must be passed
as 0. */
#define BACKTRACE_SUPPORTS_THREADS @BACKTRACE_SUPPORTS_THREADS@
/* BACKTRACE_SUPPORTS_DATA will be #defined'd as 1 if the backtrace_syminfo
will work for variables. It will always work for functions. */
#define BACKTRACE_SUPPORTS_DATA @BACKTRACE_SUPPORTS_DATA@

View File

@ -1,129 +0,0 @@
/* backtrace.c -- Entry point for stack backtrace library.
Copyright (C) 2012-2018 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Google.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
(1) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
#include "config.h"
#include <sys/types.h>
#include "unwind.h"
#include "backtrace.h"
#include "internal.h"
/* The main backtrace_full routine. */
/* Data passed through _Unwind_Backtrace. */
struct backtrace_data
{
/* Number of frames to skip. */
int skip;
/* Library state. */
struct backtrace_state *state;
/* Callback routine. */
backtrace_full_callback callback;
/* Error callback routine. */
backtrace_error_callback error_callback;
/* Data to pass to callback routines. */
void *data;
/* Value to return from backtrace_full. */
int ret;
/* Whether there is any memory available. */
int can_alloc;
};
/* Unwind library callback routine. This is passed to
_Unwind_Backtrace. */
static _Unwind_Reason_Code
unwind (struct _Unwind_Context *context, void *vdata)
{
struct backtrace_data *bdata = (struct backtrace_data *) vdata;
uintptr_t pc;
int ip_before_insn = 0;
#ifdef HAVE_GETIPINFO
pc = _Unwind_GetIPInfo (context, &ip_before_insn);
#else
pc = _Unwind_GetIP (context);
#endif
if (bdata->skip > 0)
{
--bdata->skip;
return _URC_NO_REASON;
}
if (!ip_before_insn)
--pc;
if (!bdata->can_alloc)
bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, NULL);
else
bdata->ret = backtrace_pcinfo (bdata->state, pc, bdata->callback,
bdata->error_callback, bdata->data);
if (bdata->ret != 0)
return _URC_END_OF_STACK;
return _URC_NO_REASON;
}
/* Get a stack backtrace. */
int
backtrace_full (struct backtrace_state *state, int skip,
backtrace_full_callback callback,
backtrace_error_callback error_callback, void *data)
{
struct backtrace_data bdata;
void *p;
bdata.skip = skip + 1;
bdata.state = state;
bdata.callback = callback;
bdata.error_callback = error_callback;
bdata.data = data;
bdata.ret = 0;
/* If we can't allocate any memory at all, don't try to produce
file/line information. */
p = backtrace_alloc (state, 4096, NULL, NULL);
if (p == NULL)
bdata.can_alloc = 0;
else
{
backtrace_free (state, p, 4096, NULL, NULL);
bdata.can_alloc = 1;
}
_Unwind_Backtrace (unwind, &bdata);
return bdata.ret;
}

View File

@ -1,182 +0,0 @@
/* backtrace.h -- Public header file for stack backtrace library.
Copyright (C) 2012-2018 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Google.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
(1) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
#ifndef BACKTRACE_H
#define BACKTRACE_H
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/* The backtrace state. This struct is intentionally not defined in
the public interface. */
struct backtrace_state;
/* The type of the error callback argument to backtrace functions.
This function, if not NULL, will be called for certain error cases.
The DATA argument is passed to the function that calls this one.
The MSG argument is an error message. The ERRNUM argument, if
greater than 0, holds an errno value. The MSG buffer may become
invalid after this function returns.
As a special case, the ERRNUM argument will be passed as -1 if no
debug info can be found for the executable, but the function
requires debug info (e.g., backtrace_full, backtrace_pcinfo). The
MSG in this case will be something along the lines of "no debug
info". Similarly, ERRNUM will be passed as -1 if there is no
symbol table, but the function requires a symbol table (e.g.,
backtrace_syminfo). This may be used as a signal that some other
approach should be tried. */
typedef void (*backtrace_error_callback) (void *data, const char *msg,
int errnum);
/* Create state information for the backtrace routines. This must be
called before any of the other routines, and its return value must
be passed to all of the other routines. FILENAME is the path name
of the executable file; if it is NULL the library will try
system-specific path names. If not NULL, FILENAME must point to a
permanent buffer. If THREADED is non-zero the state may be
accessed by multiple threads simultaneously, and the library will
use appropriate atomic operations. If THREADED is zero the state
may only be accessed by one thread at a time. This returns a state
pointer on success, NULL on error. If an error occurs, this will
call the ERROR_CALLBACK routine. */
extern struct backtrace_state *backtrace_create_state (
const char *filename, int threaded,
backtrace_error_callback error_callback, void *data);
/* The type of the callback argument to the backtrace_full function.
DATA is the argument passed to backtrace_full. PC is the program
counter. FILENAME is the name of the file containing PC, or NULL
if not available. LINENO is the line number in FILENAME containing
PC, or 0 if not available. FUNCTION is the name of the function
containing PC, or NULL if not available. This should return 0 to
continuing tracing. The FILENAME and FUNCTION buffers may become
invalid after this function returns. */
typedef int (*backtrace_full_callback) (void *data, uintptr_t pc,
const char *filename, int lineno,
const char *function);
/* Get a full stack backtrace. SKIP is the number of frames to skip;
passing 0 will start the trace with the function calling
backtrace_full. DATA is passed to the callback routine. If any
call to CALLBACK returns a non-zero value, the stack backtrace
stops, and backtrace returns that value; this may be used to limit
the number of stack frames desired. If all calls to CALLBACK
return 0, backtrace returns 0. The backtrace_full function will
make at least one call to either CALLBACK or ERROR_CALLBACK. This
function requires debug info for the executable. */
extern int backtrace_full (struct backtrace_state *state, int skip,
backtrace_full_callback callback,
backtrace_error_callback error_callback,
void *data);
/* The type of the callback argument to the backtrace_simple function.
DATA is the argument passed to simple_backtrace. PC is the program
counter. This should return 0 to continue tracing. */
typedef int (*backtrace_simple_callback) (void *data, uintptr_t pc);
/* Get a simple backtrace. SKIP is the number of frames to skip, as
in backtrace. DATA is passed to the callback routine. If any call
to CALLBACK returns a non-zero value, the stack backtrace stops,
and backtrace_simple returns that value. Otherwise
backtrace_simple returns 0. The backtrace_simple function will
make at least one call to either CALLBACK or ERROR_CALLBACK. This
function does not require any debug info for the executable. */
extern int backtrace_simple (struct backtrace_state *state, int skip,
backtrace_simple_callback callback,
backtrace_error_callback error_callback,
void *data);
/* Print the current backtrace in a user readable format to a FILE.
SKIP is the number of frames to skip, as in backtrace_full. Any
error messages are printed to stderr. This function requires debug
info for the executable. */
extern void backtrace_print (struct backtrace_state *state, int skip, FILE *);
/* Given PC, a program counter in the current program, call the
callback function with filename, line number, and function name
information. This will normally call the callback function exactly
once. However, if the PC happens to describe an inlined call, and
the debugging information contains the necessary information, then
this may call the callback function multiple times. This will make
at least one call to either CALLBACK or ERROR_CALLBACK. This
returns the first non-zero value returned by CALLBACK, or 0. */
extern int backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
backtrace_full_callback callback,
backtrace_error_callback error_callback,
void *data);
/* The type of the callback argument to backtrace_syminfo. DATA and
PC are the arguments passed to backtrace_syminfo. SYMNAME is the
name of the symbol for the corresponding code. SYMVAL is the
value and SYMSIZE is the size of the symbol. SYMNAME will be NULL
if no error occurred but the symbol could not be found. */
typedef void (*backtrace_syminfo_callback) (void *data, uintptr_t pc,
const char *symname,
uintptr_t symval,
uintptr_t symsize);
/* Given ADDR, an address or program counter in the current program,
call the callback information with the symbol name and value
describing the function or variable in which ADDR may be found.
This will call either CALLBACK or ERROR_CALLBACK exactly once.
This returns 1 on success, 0 on failure. This function requires
the symbol table but does not require the debug info. Note that if
the symbol table is present but ADDR could not be found in the
table, CALLBACK will be called with a NULL SYMNAME argument.
Returns 1 on success, 0 on error. */
extern int backtrace_syminfo (struct backtrace_state *state, uintptr_t addr,
backtrace_syminfo_callback callback,
backtrace_error_callback error_callback,
void *data);
#ifdef __cplusplus
} /* End extern "C". */
#endif
#endif

View File

@ -1,500 +0,0 @@
/* btest.c -- Test for libbacktrace library
Copyright (C) 2012-2018 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Google.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
(1) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
/* This program tests the externally visible interfaces of the
libbacktrace library. */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "filenames.h"
#include "backtrace.h"
#include "backtrace-supported.h"
#include "testlib.h"
/* Test the backtrace function with non-inlined functions. */
static int test1 (void) __attribute__ ((noinline, unused));
static int f2 (int) __attribute__ ((noinline));
static int f3 (int, int) __attribute__ ((noinline));
static int
test1 (void)
{
/* Returning a value here and elsewhere avoids a tailcall which
would mess up the backtrace. */
return f2 (__LINE__) + 1;
}
static int
f2 (int f1line)
{
return f3 (f1line, __LINE__) + 2;
}
static int
f3 (int f1line, int f2line)
{
struct info all[20];
struct bdata data;
int f3line;
int i;
data.all = &all[0];
data.index = 0;
data.max = 20;
data.failed = 0;
f3line = __LINE__ + 1;
i = backtrace_full (state, 0, callback_one, error_callback_one, &data);
if (i != 0)
{
fprintf (stderr, "test1: unexpected return value %d\n", i);
data.failed = 1;
}
if (data.index < 3)
{
fprintf (stderr,
"test1: not enough frames; got %zu, expected at least 3\n",
data.index);
data.failed = 1;
}
check ("test1", 0, all, f3line, "f3", "btest.c", &data.failed);
check ("test1", 1, all, f2line, "f2", "btest.c", &data.failed);
check ("test1", 2, all, f1line, "test1", "btest.c", &data.failed);
printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS");
if (data.failed)
++failures;
return failures;
}
/* Test the backtrace function with inlined functions. */
static inline int test2 (void) __attribute__ ((always_inline, unused));
static inline int f12 (int) __attribute__ ((always_inline));
static inline int f13 (int, int) __attribute__ ((always_inline));
static inline int
test2 (void)
{
return f12 (__LINE__) + 1;
}
static inline int
f12 (int f1line)
{
return f13 (f1line, __LINE__) + 2;
}
static inline int
f13 (int f1line, int f2line)
{
struct info all[20];
struct bdata data;
int f3line;
int i;
data.all = &all[0];
data.index = 0;
data.max = 20;
data.failed = 0;
f3line = __LINE__ + 1;
i = backtrace_full (state, 0, callback_one, error_callback_one, &data);
if (i != 0)
{
fprintf (stderr, "test2: unexpected return value %d\n", i);
data.failed = 1;
}
check ("test2", 0, all, f3line, "f13", "btest.c", &data.failed);
check ("test2", 1, all, f2line, "f12", "btest.c", &data.failed);
check ("test2", 2, all, f1line, "test2", "btest.c", &data.failed);
printf ("%s: backtrace_full inline\n", data.failed ? "FAIL" : "PASS");
if (data.failed)
++failures;
return failures;
}
/* Test the backtrace_simple function with non-inlined functions. */
static int test3 (void) __attribute__ ((noinline, unused));
static int f22 (int) __attribute__ ((noinline));
static int f23 (int, int) __attribute__ ((noinline));
static int
test3 (void)
{
return f22 (__LINE__) + 1;
}
static int
f22 (int f1line)
{
return f23 (f1line, __LINE__) + 2;
}
static int
f23 (int f1line, int f2line)
{
uintptr_t addrs[20];
struct sdata data;
int f3line;
int i;
data.addrs = &addrs[0];
data.index = 0;
data.max = 20;
data.failed = 0;
f3line = __LINE__ + 1;
i = backtrace_simple (state, 0, callback_two, error_callback_two, &data);
if (i != 0)
{
fprintf (stderr, "test3: unexpected return value %d\n", i);
data.failed = 1;
}
if (!data.failed)
{
struct info all[20];
struct bdata bdata;
int j;
bdata.all = &all[0];
bdata.index = 0;
bdata.max = 20;
bdata.failed = 0;
for (j = 0; j < 3; ++j)
{
i = backtrace_pcinfo (state, addrs[j], callback_one,
error_callback_one, &bdata);
if (i != 0)
{
fprintf (stderr,
("test3: unexpected return value "
"from backtrace_pcinfo %d\n"),
i);
bdata.failed = 1;
}
if (!bdata.failed && bdata.index != (size_t) (j + 1))
{
fprintf (stderr,
("wrong number of calls from backtrace_pcinfo "
"got %u expected %d\n"),
(unsigned int) bdata.index, j + 1);
bdata.failed = 1;
}
}
check ("test3", 0, all, f3line, "f23", "btest.c", &bdata.failed);
check ("test3", 1, all, f2line, "f22", "btest.c", &bdata.failed);
check ("test3", 2, all, f1line, "test3", "btest.c", &bdata.failed);
if (bdata.failed)
data.failed = 1;
for (j = 0; j < 3; ++j)
{
struct symdata symdata;
symdata.name = NULL;
symdata.val = 0;
symdata.size = 0;
symdata.failed = 0;
i = backtrace_syminfo (state, addrs[j], callback_three,
error_callback_three, &symdata);
if (i == 0)
{
fprintf (stderr,
("test3: [%d]: unexpected return value "
"from backtrace_syminfo %d\n"),
j, i);
symdata.failed = 1;
}
if (!symdata.failed)
{
const char *expected;
switch (j)
{
case 0:
expected = "f23";
break;
case 1:
expected = "f22";
break;
case 2:
expected = "test3";
break;
default:
assert (0);
}
if (symdata.name == NULL)
{
fprintf (stderr, "test3: [%d]: NULL syminfo name\n", j);
symdata.failed = 1;
}
/* Use strncmp, not strcmp, because GCC might create a
clone. */
else if (strncmp (symdata.name, expected, strlen (expected))
!= 0)
{
fprintf (stderr,
("test3: [%d]: unexpected syminfo name "
"got %s expected %s\n"),
j, symdata.name, expected);
symdata.failed = 1;
}
}
if (symdata.failed)
data.failed = 1;
}
}
printf ("%s: backtrace_simple noinline\n", data.failed ? "FAIL" : "PASS");
if (data.failed)
++failures;
return failures;
}
/* Test the backtrace_simple function with inlined functions. */
static inline int test4 (void) __attribute__ ((always_inline, unused));
static inline int f32 (int) __attribute__ ((always_inline));
static inline int f33 (int, int) __attribute__ ((always_inline));
static inline int
test4 (void)
{
return f32 (__LINE__) + 1;
}
static inline int
f32 (int f1line)
{
return f33 (f1line, __LINE__) + 2;
}
static inline int
f33 (int f1line, int f2line)
{
uintptr_t addrs[20];
struct sdata data;
int f3line;
int i;
data.addrs = &addrs[0];
data.index = 0;
data.max = 20;
data.failed = 0;
f3line = __LINE__ + 1;
i = backtrace_simple (state, 0, callback_two, error_callback_two, &data);
if (i != 0)
{
fprintf (stderr, "test3: unexpected return value %d\n", i);
data.failed = 1;
}
if (!data.failed)
{
struct info all[20];
struct bdata bdata;
bdata.all = &all[0];
bdata.index = 0;
bdata.max = 20;
bdata.failed = 0;
i = backtrace_pcinfo (state, addrs[0], callback_one, error_callback_one,
&bdata);
if (i != 0)
{
fprintf (stderr,
("test4: unexpected return value "
"from backtrace_pcinfo %d\n"),
i);
bdata.failed = 1;
}
check ("test4", 0, all, f3line, "f33", "btest.c", &bdata.failed);
check ("test4", 1, all, f2line, "f32", "btest.c", &bdata.failed);
check ("test4", 2, all, f1line, "test4", "btest.c", &bdata.failed);
if (bdata.failed)
data.failed = 1;
}
printf ("%s: backtrace_simple inline\n", data.failed ? "FAIL" : "PASS");
if (data.failed)
++failures;
return failures;
}
static int test5 (void) __attribute__ ((unused));
int global = 1;
static int
test5 (void)
{
struct symdata symdata;
int i;
uintptr_t addr = (uintptr_t) &global;
if (sizeof (global) > 1)
addr += 1;
symdata.name = NULL;
symdata.val = 0;
symdata.size = 0;
symdata.failed = 0;
i = backtrace_syminfo (state, addr, callback_three,
error_callback_three, &symdata);
if (i == 0)
{
fprintf (stderr,
"test5: unexpected return value from backtrace_syminfo %d\n",
i);
symdata.failed = 1;
}
if (!symdata.failed)
{
if (symdata.name == NULL)
{
fprintf (stderr, "test5: NULL syminfo name\n");
symdata.failed = 1;
}
else if (strcmp (symdata.name, "global") != 0)
{
fprintf (stderr,
"test5: unexpected syminfo name got %s expected %s\n",
symdata.name, "global");
symdata.failed = 1;
}
else if (symdata.val != (uintptr_t) &global)
{
fprintf (stderr,
"test5: unexpected syminfo value got %lx expected %lx\n",
(unsigned long) symdata.val,
(unsigned long) (uintptr_t) &global);
symdata.failed = 1;
}
else if (symdata.size != sizeof (global))
{
fprintf (stderr,
"test5: unexpected syminfo size got %lx expected %lx\n",
(unsigned long) symdata.size,
(unsigned long) sizeof (global));
symdata.failed = 1;
}
}
printf ("%s: backtrace_syminfo variable\n",
symdata.failed ? "FAIL" : "PASS");
if (symdata.failed)
++failures;
return failures;
}
/* Check that are no files left open. */
static void
check_open_files (void)
{
int i;
for (i = 3; i < 10; i++)
{
if (close (i) == 0)
{
fprintf (stderr,
"ERROR: descriptor %d still open after tests complete\n",
i);
++failures;
}
}
}
/* Run all the tests. */
int
main (int argc ATTRIBUTE_UNUSED, char **argv)
{
state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS,
error_callback_create, NULL);
#if BACKTRACE_SUPPORTED
test1 ();
test2 ();
test3 ();
test4 ();
#if BACKTRACE_SUPPORTS_DATA
test5 ();
#endif
#endif
check_open_files ();
exit (failures ? EXIT_FAILURE : EXIT_SUCCESS);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,149 +0,0 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* ELF size: 32 or 64 */
#undef BACKTRACE_ELF_SIZE
/* XCOFF size: 32 or 64 */
#undef BACKTRACE_XCOFF_SIZE
/* Define to 1 if you have the __atomic functions */
#undef HAVE_ATOMIC_FUNCTIONS
/* Define to 1 if you have the `clock_gettime' function. */
#undef HAVE_CLOCK_GETTIME
/* Define to 1 if you have the declaration of `strnlen', and to 0 if you
don't. */
#undef HAVE_DECL_STRNLEN
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define if dl_iterate_phdr is available. */
#undef HAVE_DL_ITERATE_PHDR
/* Define to 1 if you have the fcntl function */
#undef HAVE_FCNTL
/* Define if getexecname is available. */
#undef HAVE_GETEXECNAME
/* Define if _Unwind_GetIPInfo is available. */
#undef HAVE_GETIPINFO
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `z' library (-lz). */
#undef HAVE_LIBZ
/* Define to 1 if you have the <link.h> header file. */
#undef HAVE_LINK_H
/* Define if AIX loadquery is available. */
#undef HAVE_LOADQUERY
/* Define to 1 if you have the `lstat' function. */
#undef HAVE_LSTAT
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `readlink' function. */
#undef HAVE_READLINK
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the __sync functions */
#undef HAVE_SYNC_FUNCTIONS
/* Define to 1 if you have the <sys/ldr.h> header file. */
#undef HAVE_SYS_LDR_H
/* Define to 1 if you have the <sys/mman.h> header file. */
#undef HAVE_SYS_MMAN_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define if -lz is available. */
#undef HAVE_ZLIB
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define for large files, on AIX-style hosts. */
#undef _LARGE_FILES
/* Define to 1 if on MINIX. */
#undef _MINIX
/* Define to 2 if the system does not provide POSIX.1 features except with
this defined. */
#undef _POSIX_1_SOURCE
/* Define to 1 if you need to in order for `stat' and other things to work. */
#undef _POSIX_SOURCE

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More