Skip to content

Commit fe51d7b

Browse files
committed
Use framehop 0.3.0 for better x86 unwinding.
Framehop 0.3.0 comes with x86_64 instruction analysis for the leaf frame, more accurate stubs/stub_helper unwinding, supports one global unwinder cache, and is less confused by unwind info for the text_env section.
1 parent 6631a48 commit fe51d7b

File tree

5 files changed

+73
-39
lines changed

5 files changed

+73
-39
lines changed

perfrecord/Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

perfrecord/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ crossbeam-channel = "0.5.1"
2525
signal-hook = "0.3.9"
2626
serde_json = "1.0.53"
2727
gecko_profile = { version = "0.2.0", path = "../gecko_profile" }
28-
framehop = "0.2.0"
28+
framehop = "0.3.0"
2929

3030
[profile.release]
3131
debug = true

perfrecord/src/proc_maps.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub struct DyldInfo {
5252
pub file: String,
5353
pub address: u64,
5454
pub vmsize: u64,
55-
pub sections: framehop::ModuleSectionAddresses,
55+
pub sections: framehop::ModuleSectionAddressRanges,
5656
pub uuid: Option<Uuid>,
5757
pub arch: Option<&'static str>,
5858
pub unwind_sections: UnwindSectionInfo,
@@ -65,7 +65,7 @@ pub struct UnwindSectionInfo {
6565
/// (address, size)
6666
pub eh_frame_section: Option<(u64, u64)>,
6767
/// (address, size)
68-
pub text_section: Option<(u64, u64)>,
68+
pub text_segment: Option<(u64, u64)>,
6969
}
7070

7171
pub struct DyldInfoManager {
@@ -282,7 +282,11 @@ fn get_dyld_image_info(
282282
let mut unwind_info_section = None;
283283
let mut eh_frame_section = None;
284284
let mut text_section = None;
285-
let mut got_section_addr = 0;
285+
let mut text_env_section = None;
286+
let mut stubs_section = None;
287+
let mut stub_helper_section = None;
288+
let mut got_section = None;
289+
let mut text_segment = None;
286290
let mut offset = 0;
287291
for _ in 0..header.ncmds {
288292
unsafe {
@@ -294,6 +298,7 @@ fn get_dyld_image_info(
294298
if (*segcmd).segname[0..7] == [95, 95, 84, 69, 88, 84, 0] {
295299
// This is the __TEXT segment.
296300
vmsize = (*segcmd).vmsize;
301+
text_segment = Some((image_load_address, vmsize));
297302
let textvmaddr = (*segcmd).vmaddr;
298303

299304
let mut section_offset = offset + mem::size_of::<segment_command_64>();
@@ -310,9 +315,26 @@ fn get_dyld_image_info(
310315
if (*section).sectname[0..7] == [95, 95, 116, 101, 120, 116, 0] {
311316
// This is the __text section.
312317
text_section = Some((addr, size));
318+
} else if (*section).sectname[0..9]
319+
== [116, 101, 120, 116, 95, 101, 110, 118, 0]
320+
{
321+
// This is the text_env section.
322+
text_env_section = Some((addr, size));
323+
} else if (*section).sectname[0..8]
324+
== [95, 95, 115, 116, 117, 98, 115, 0]
325+
{
326+
// This is the __stubs section.
327+
stubs_section = Some((addr, size));
328+
} else if (*section).sectname[0..14]
329+
== [
330+
95, 95, 115, 116, 117, 98, 95, 104, 101, 108, 112, 101, 114, 0,
331+
]
332+
{
333+
// This is the __stub_helper section.
334+
stub_helper_section = Some((addr, size));
313335
} else if (*section).sectname[0..6] == [95, 95, 103, 111, 116, 0] {
314336
// This is the __got section.
315-
got_section_addr = addr;
337+
got_section = Some((addr, size));
316338
} else if (*section).sectname[0..14]
317339
== [
318340
95, 95, 117, 110, 119, 105, 110, 100, 95, 105, 110, 102, 111, 0,
@@ -345,19 +367,22 @@ fn get_dyld_image_info(
345367
file: filename,
346368
address: image_load_address,
347369
vmsize,
348-
sections: framehop::ModuleSectionAddresses {
349-
text: text_section.map(|(addr, _)| addr).unwrap_or(0),
350-
eh_frame: eh_frame_section.map(|(addr, _)| addr).unwrap_or(0),
351-
eh_frame_hdr: 0,
352-
got: got_section_addr,
370+
sections: framehop::ModuleSectionAddressRanges {
371+
text: text_section.map(|(addr, size)| addr..addr + size),
372+
text_env: text_env_section.map(|(addr, size)| addr..addr + size),
373+
stubs: stubs_section.map(|(addr, size)| addr..addr + size),
374+
stub_helper: stub_helper_section.map(|(addr, size)| addr..addr + size),
375+
eh_frame: eh_frame_section.map(|(addr, size)| addr..addr + size),
376+
eh_frame_hdr: None,
377+
got: got_section.map(|(addr, size)| addr..addr + size),
353378
},
354379
uuid,
355380
arch: get_arch_string(header.cputype as u32, header.cpusubtype as u32),
356381
is_executable: header.filetype == MH_EXECUTE,
357382
unwind_sections: UnwindSectionInfo {
358383
unwind_info_section,
359384
eh_frame_section,
360-
text_section,
385+
text_segment,
361386
},
362387
})
363388
}

perfrecord/src/sampler.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::error::SamplingError;
22
use crate::task_profiler::TaskProfiler;
3+
use crate::task_profiler::UnwinderCache;
34
use crossbeam_channel::Receiver;
45
use gecko_profile::ProfileBuilder;
56
use std::mem;
@@ -15,6 +16,7 @@ pub struct Sampler {
1516
live_other_tasks: Vec<TaskProfiler>,
1617
dead_root_task: Option<TaskProfiler>,
1718
dead_other_tasks: Vec<TaskProfiler>,
19+
unwinder_cache: UnwinderCache,
1820
}
1921

2022
impl Sampler {
@@ -32,6 +34,7 @@ impl Sampler {
3234
live_other_tasks: Vec::new(),
3335
dead_root_task: None,
3436
dead_other_tasks: Vec::new(),
37+
unwinder_cache: Default::default(),
3538
}
3639
}
3740

@@ -62,7 +65,7 @@ impl Sampler {
6265
}
6366

6467
if let Some(task) = &mut self.live_root_task {
65-
let still_alive = task.sample(sample_timestamp)?;
68+
let still_alive = task.sample(sample_timestamp, &mut self.unwinder_cache)?;
6669
if !still_alive {
6770
task.notify_dead(sample_timestamp);
6871
self.dead_root_task = self.live_root_task.take();
@@ -72,7 +75,7 @@ impl Sampler {
7275
let mut other_tasks = Vec::with_capacity(self.live_other_tasks.capacity());
7376
mem::swap(&mut self.live_other_tasks, &mut other_tasks);
7477
for mut task in other_tasks.into_iter() {
75-
let still_alive = task.sample(sample_timestamp)?;
78+
let still_alive = task.sample(sample_timestamp, &mut self.unwinder_cache)?;
7679
if still_alive {
7780
self.live_other_tasks.push(task);
7881
} else {

perfrecord/src/task_profiler.rs

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ use crate::error::SamplingError;
22
use crate::kernel_error::{IntoResult, KernelError};
33
use crate::proc_maps::{DyldInfo, DyldInfoManager, Modification, StackwalkerRef, VmSubData};
44
use crate::thread_profiler::ThreadProfiler;
5+
use framehop::{
6+
CacheNative, MayAllocateDuringUnwind, Module, ModuleUnwindData, TextByteData, Unwinder,
7+
UnwinderNative,
8+
};
59
use gecko_profile::debugid::DebugId;
610
use mach::mach_types::thread_act_port_array_t;
711
use mach::mach_types::thread_act_t;
@@ -18,9 +22,10 @@ use std::path::Path;
1822
use std::sync::Arc;
1923
use std::time::{Duration, Instant, SystemTime};
2024

21-
use framehop::Unwinder;
2225
use gecko_profile::ProfileBuilder;
2326

27+
pub type UnwinderCache = CacheNative<VmSubData, MayAllocateDuringUnwind>;
28+
2429
pub struct TaskProfiler {
2530
task: mach_port_t,
2631
pid: u32,
@@ -35,8 +40,7 @@ pub struct TaskProfiler {
3540
executable_lib: Option<DyldInfo>,
3641
command_name: String,
3742
ignored_errors: Vec<SamplingError>,
38-
unwinder: framehop::UnwinderNative<VmSubData, framehop::MayAllocateDuringUnwind>,
39-
unwinder_cache: framehop::CacheNative<VmSubData, framehop::MayAllocateDuringUnwind>,
43+
unwinder: UnwinderNative<VmSubData, MayAllocateDuringUnwind>,
4044
}
4145

4246
impl TaskProfiler {
@@ -71,13 +75,16 @@ impl TaskProfiler {
7175
command_name: command_name.to_owned(),
7276
executable_lib: None,
7377
ignored_errors: Vec::new(),
74-
unwinder: framehop::UnwinderNative::new(),
75-
unwinder_cache: framehop::CacheNative::new(),
78+
unwinder: UnwinderNative::new(),
7679
})
7780
}
7881

79-
pub fn sample(&mut self, now: Instant) -> Result<bool, SamplingError> {
80-
let result = self.sample_impl(now);
82+
pub fn sample(
83+
&mut self,
84+
now: Instant,
85+
unwinder_cache: &mut UnwinderCache,
86+
) -> Result<bool, SamplingError> {
87+
let result = self.sample_impl(now, unwinder_cache);
8188
match result {
8289
Ok(()) => Ok(true),
8390
Err(SamplingError::ProcessTerminated(_, _)) => Ok(false),
@@ -99,7 +106,11 @@ impl TaskProfiler {
99106
}
100107
}
101108

102-
fn sample_impl(&mut self, now: Instant) -> Result<(), SamplingError> {
109+
fn sample_impl(
110+
&mut self,
111+
now: Instant,
112+
unwinder_cache: &mut UnwinderCache,
113+
) -> Result<(), SamplingError> {
103114
// First, check for any newly-loaded libraries.
104115
let changes = self
105116
.lib_info_manager
@@ -141,7 +152,7 @@ impl TaskProfiler {
141152
}
142153
};
143154
// Grab a sample from the thread.
144-
let stackwalker = StackwalkerRef::new(&self.unwinder, &mut self.unwinder_cache);
155+
let stackwalker = StackwalkerRef::new(&self.unwinder, unwinder_cache);
145156
let still_alive = thread.sample(stackwalker, now)?;
146157
if still_alive {
147158
now_live_threads.insert(thread_act);
@@ -165,26 +176,21 @@ impl TaskProfiler {
165176
.unwind_sections
166177
.eh_frame_section
167178
.and_then(|(addr, size)| VmSubData::map_from_task(self.task, addr, size).ok());
168-
let text_data = lib
169-
.unwind_sections
170-
.text_section
171-
.and_then(|(addr, size)| VmSubData::map_from_task(self.task, addr, size).ok());
179+
let text_data = lib.unwind_sections.text_segment.and_then(|(addr, size)| {
180+
VmSubData::map_from_task(self.task, addr, size)
181+
.ok()
182+
.map(|data| TextByteData::new(data, addr..addr + size))
183+
});
172184

173185
let unwind_data = match (unwind_info_data, eh_frame_data) {
174-
(Some(unwind_info), Some(eh_frame)) => {
175-
framehop::ModuleUnwindData::CompactUnwindInfoAndEhFrame(
176-
unwind_info,
177-
Some(Arc::new(eh_frame)),
178-
)
179-
}
180-
(Some(unwind_info), None) => {
181-
framehop::ModuleUnwindData::CompactUnwindInfoAndEhFrame(unwind_info, None)
186+
(Some(unwind_info), eh_frame) => {
187+
ModuleUnwindData::CompactUnwindInfoAndEhFrame(unwind_info, eh_frame.map(Arc::new))
182188
}
183-
(None, Some(eh_frame)) => framehop::ModuleUnwindData::EhFrame(Arc::new(eh_frame)),
184-
(None, None) => framehop::ModuleUnwindData::None,
189+
(None, Some(eh_frame)) => ModuleUnwindData::EhFrame(Arc::new(eh_frame)),
190+
(None, None) => ModuleUnwindData::None,
185191
};
186192

187-
let module = framehop::Module::new(
193+
let module = Module::new(
188194
lib.file.clone(),
189195
lib.address..(lib.address + lib.vmsize),
190196
lib.address,

0 commit comments

Comments
 (0)