Skip to content

Commit 228ead0

Browse files
committed
capi: Add blaze_symbolization_reason enum
This change introduces the blaze_symbolization_reason enumeration to blazesym-c and adjusts the blaze_sym type to carry such a reason. We also introduce the blaze_symbolization_reason_str() helper function that can be used to retrieve a textual representation of a reason for a symbolization failure. Signed-off-by: Daniel Müller <deso@posteo.net>
1 parent 8943f02 commit 228ead0

File tree

4 files changed

+212
-16
lines changed

4 files changed

+212
-16
lines changed

capi/CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ Unreleased
1010
- Introduced `blaze_normalize_opts` and added
1111
`blaze_normalize_user_addrs_opts` to use it
1212
- Removed `blaze_normalize_user_addrs_sorted` function
13-
- Introduced `blaze_normalize_reason` type and extended
13+
- Introduced `blaze_normalize_reason` type
1414
- Added `reason` attribute to `blaze_user_meta_unknown`
1515
- Added `blaze_normalize_reason_str` to retrieve textual representation
16+
- Introduced `blaze_symbolize_reason` type
17+
- Added `reason` attribute to `blaze_sym`
18+
- Added `blaze_symbolize_reason_str` to retrieve textual representation
1619
- Added `blaze_symbolize_elf_file_offsets` function for symbolization of
1720
file offsets
1821
- Added support for transparently working with input data not in accordance with

capi/include/blazesym.h

+60-1
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,54 @@ enum blaze_sym_type
143143
typedef uint8_t blaze_sym_type;
144144
#endif // __cplusplus
145145

146+
/**
147+
* The reason why symbolization failed.
148+
*
149+
* The reason is generally only meant as a hint. Reasons reported may
150+
* change over time and, hence, should not be relied upon for the
151+
* correctness of the application.
152+
*/
153+
enum blaze_symbolize_reason
154+
#ifdef __cplusplus
155+
: uint8_t
156+
#endif // __cplusplus
157+
{
158+
/**
159+
* Symbolization was successful.
160+
*/
161+
BLAZE_SYMBOLIZE_REASON_SUCCESS = 0,
162+
/**
163+
* The absolute address was not found in the corresponding process'
164+
* virtual memory map.
165+
*/
166+
BLAZE_SYMBOLIZE_REASON_UNMAPPED,
167+
/**
168+
* The file offset does not map to a valid piece of code/data.
169+
*/
170+
BLAZE_SYMBOLIZE_REASON_INVALID_FILE_OFFSET,
171+
/**
172+
* The `/proc/<pid>/maps` entry corresponding to the address does
173+
* not have a component (file system path, object, ...) associated
174+
* with it.
175+
*/
176+
BLAZE_SYMBOLIZE_REASON_MISSING_COMPONENT,
177+
/**
178+
* The symbolization source has no or no relevant symbols.
179+
*/
180+
BLAZE_SYMBOLIZE_REASON_MISSING_SYMS,
181+
/**
182+
* The address could not be found in the symbolization source.
183+
*/
184+
BLAZE_SYMBOLIZE_REASON_UNKNOWN_ADDR,
185+
/**
186+
* The address belonged to an entity that is currently unsupported.
187+
*/
188+
BLAZE_SYMBOLIZE_REASON_UNSUPPORTED,
189+
};
190+
#ifndef __cplusplus
191+
typedef uint8_t blaze_symbolize_reason;
192+
#endif // __cplusplus
193+
146194
/**
147195
* The valid variant kind in [`blaze_user_meta`].
148196
*/
@@ -601,10 +649,15 @@ typedef struct blaze_sym {
601649
* An array of `inlined_cnt` symbolized inlined function calls.
602650
*/
603651
const struct blaze_symbolize_inlined_fn *inlined;
652+
/**
653+
* On error (i.e., if `name` is NULL), a reason trying to explain
654+
* why symbolization failed.
655+
*/
656+
blaze_symbolize_reason reason;
604657
/**
605658
* Unused member available for future expansion.
606659
*/
607-
uint8_t reserved[8];
660+
uint8_t reserved[7];
608661
} blaze_sym;
609662

610663
/**
@@ -978,6 +1031,12 @@ struct blaze_normalized_user_output *blaze_normalize_user_addrs_opts(const blaze
9781031
*/
9791032
void blaze_user_output_free(struct blaze_normalized_user_output *output);
9801033

1034+
/**
1035+
* Retrieve a textual representation of the reason of a symbolization
1036+
* failure.
1037+
*/
1038+
const char *blaze_symbolize_reason_str(blaze_symbolize_reason err);
1039+
9811040
/**
9821041
* Create an instance of a symbolizer.
9831042
*

capi/src/symbolize.rs

+128-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use blazesym::symbolize::InlinedFn;
2020
use blazesym::symbolize::Input;
2121
use blazesym::symbolize::Kernel;
2222
use blazesym::symbolize::Process;
23+
use blazesym::symbolize::Reason;
2324
use blazesym::symbolize::Source;
2425
use blazesym::symbolize::Sym;
2526
use blazesym::symbolize::Symbolized;
@@ -315,6 +316,81 @@ impl From<blaze_symbolize_src_gsym_file> for GsymFile {
315316
pub type blaze_symbolizer = Symbolizer;
316317

317318

319+
/// The reason why symbolization failed.
320+
///
321+
/// The reason is generally only meant as a hint. Reasons reported may
322+
/// change over time and, hence, should not be relied upon for the
323+
/// correctness of the application.
324+
#[repr(u8)]
325+
#[derive(Copy, Clone, Debug, PartialEq)]
326+
pub enum blaze_symbolize_reason {
327+
/// Symbolization was successful.
328+
BLAZE_SYMBOLIZE_REASON_SUCCESS = 0,
329+
/// The absolute address was not found in the corresponding process'
330+
/// virtual memory map.
331+
BLAZE_SYMBOLIZE_REASON_UNMAPPED,
332+
/// The file offset does not map to a valid piece of code/data.
333+
BLAZE_SYMBOLIZE_REASON_INVALID_FILE_OFFSET,
334+
/// The `/proc/<pid>/maps` entry corresponding to the address does
335+
/// not have a component (file system path, object, ...) associated
336+
/// with it.
337+
BLAZE_SYMBOLIZE_REASON_MISSING_COMPONENT,
338+
/// The symbolization source has no or no relevant symbols.
339+
BLAZE_SYMBOLIZE_REASON_MISSING_SYMS,
340+
/// The address could not be found in the symbolization source.
341+
BLAZE_SYMBOLIZE_REASON_UNKNOWN_ADDR,
342+
/// The address belonged to an entity that is currently unsupported.
343+
BLAZE_SYMBOLIZE_REASON_UNSUPPORTED,
344+
}
345+
346+
impl From<Reason> for blaze_symbolize_reason {
347+
fn from(reason: Reason) -> Self {
348+
use blaze_symbolize_reason::*;
349+
350+
match reason {
351+
Reason::Unmapped => BLAZE_SYMBOLIZE_REASON_UNMAPPED,
352+
Reason::InvalidFileOffset => BLAZE_SYMBOLIZE_REASON_INVALID_FILE_OFFSET,
353+
Reason::MissingComponent => BLAZE_SYMBOLIZE_REASON_MISSING_COMPONENT,
354+
Reason::MissingSyms => BLAZE_SYMBOLIZE_REASON_MISSING_SYMS,
355+
Reason::Unsupported => BLAZE_SYMBOLIZE_REASON_UNSUPPORTED,
356+
Reason::UnknownAddr => BLAZE_SYMBOLIZE_REASON_UNKNOWN_ADDR,
357+
_ => unreachable!(),
358+
}
359+
}
360+
}
361+
362+
363+
/// Retrieve a textual representation of the reason of a symbolization
364+
/// failure.
365+
#[no_mangle]
366+
pub extern "C" fn blaze_symbolize_reason_str(err: blaze_symbolize_reason) -> *const c_char {
367+
use blaze_symbolize_reason::*;
368+
369+
match err as i32 {
370+
e if e == BLAZE_SYMBOLIZE_REASON_SUCCESS as i32 => b"success\0".as_ptr().cast(),
371+
e if e == BLAZE_SYMBOLIZE_REASON_UNMAPPED as i32 => {
372+
Reason::Unmapped.as_bytes().as_ptr().cast()
373+
}
374+
e if e == BLAZE_SYMBOLIZE_REASON_INVALID_FILE_OFFSET as i32 => {
375+
Reason::InvalidFileOffset.as_bytes().as_ptr().cast()
376+
}
377+
e if e == BLAZE_SYMBOLIZE_REASON_MISSING_COMPONENT as i32 => {
378+
Reason::MissingComponent.as_bytes().as_ptr().cast()
379+
}
380+
e if e == BLAZE_SYMBOLIZE_REASON_MISSING_SYMS as i32 => {
381+
Reason::MissingSyms.as_bytes().as_ptr().cast()
382+
}
383+
e if e == BLAZE_SYMBOLIZE_REASON_UNKNOWN_ADDR as i32 => {
384+
Reason::UnknownAddr.as_bytes().as_ptr().cast()
385+
}
386+
e if e == BLAZE_SYMBOLIZE_REASON_UNSUPPORTED as i32 => {
387+
Reason::Unsupported.as_bytes().as_ptr().cast()
388+
}
389+
_ => b"unknown reason\0".as_ptr().cast(),
390+
}
391+
}
392+
393+
318394
/// Source code location information for a symbol or inlined function.
319395
#[repr(C)]
320396
#[derive(Debug)]
@@ -384,8 +460,11 @@ pub struct blaze_sym {
384460
pub inlined_cnt: usize,
385461
/// An array of `inlined_cnt` symbolized inlined function calls.
386462
pub inlined: *const blaze_symbolize_inlined_fn,
463+
/// On error (i.e., if `name` is NULL), a reason trying to explain
464+
/// why symbolization failed.
465+
pub reason: blaze_symbolize_reason,
387466
/// Unused member available for future expansion.
388-
pub reserved: [u8; 8],
467+
pub reserved: [u8; 7],
389468
}
390469

391470
/// `blaze_result` is the result of symbolization for C API.
@@ -683,6 +762,7 @@ fn convert_symbolizedresults_to_c(results: Vec<Symbolized>) -> *const blaze_resu
683762
convert_code_info(&sym.code_info, &mut sym_ref.code_info, &mut make_cstr);
684763
sym_ref.inlined_cnt = sym.inlined.len();
685764
sym_ref.inlined = inlined_last;
765+
sym_ref.reason = blaze_symbolize_reason::BLAZE_SYMBOLIZE_REASON_SUCCESS;
686766

687767
for inlined in sym.inlined.iter() {
688768
let inlined_ref = unsafe { &mut *inlined_last };
@@ -698,12 +778,14 @@ fn convert_symbolizedresults_to_c(results: Vec<Symbolized>) -> *const blaze_resu
698778
inlined_last = unsafe { inlined_last.add(1) };
699779
}
700780
}
701-
Symbolized::Unknown(..) => {
781+
Symbolized::Unknown(reason) => {
702782
// Unknown symbols/addresses are just represented with all
703-
// fields set to zero.
783+
// fields set to zero (except for reason).
704784
// SAFETY: `syms_last` is pointing to a writable and properly
705785
// aligned `blaze_sym` object.
706786
let () = unsafe { syms_last.write_bytes(0, 1) };
787+
let sym_ref = unsafe { &mut *syms_last };
788+
sym_ref.reason = reason.into();
707789
}
708790
}
709791

@@ -1099,11 +1181,12 @@ mod tests {
10991181
},
11001182
inlined_cnt: 0,
11011183
inlined: ptr::null(),
1102-
reserved: [0u8; 8],
1184+
reason: blaze_symbolize_reason::BLAZE_SYMBOLIZE_REASON_UNSUPPORTED,
1185+
reserved: [0u8; 7],
11031186
};
11041187
assert_eq!(
11051188
format!("{sym:?}"),
1106-
"blaze_sym { name: 0x0, addr: 4919, offset: 24, code_info: blaze_symbolize_code_info { dir: 0x0, file: 0x0, line: 42, column: 1, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, inlined_cnt: 0, inlined: 0x0, reserved: [0, 0, 0, 0, 0, 0, 0, 0] }"
1189+
"blaze_sym { name: 0x0, addr: 4919, offset: 24, code_info: blaze_symbolize_code_info { dir: 0x0, file: 0x0, line: 42, column: 1, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, inlined_cnt: 0, inlined: 0x0, reason: BLAZE_SYMBOLIZE_REASON_UNSUPPORTED, reserved: [0, 0, 0, 0, 0, 0, 0] }"
11071190
);
11081191

11091192
let inlined = blaze_symbolize_inlined_fn {
@@ -1136,6 +1219,36 @@ mod tests {
11361219
);
11371220
}
11381221

1222+
/// Make sure that we can stringify symbolization reasons as expected.
1223+
#[tag(miri)]
1224+
#[test]
1225+
fn reason_stringification() {
1226+
use blaze_symbolize_reason::*;
1227+
1228+
let data = [
1229+
(Reason::Unmapped, BLAZE_SYMBOLIZE_REASON_UNMAPPED),
1230+
(
1231+
Reason::InvalidFileOffset,
1232+
BLAZE_SYMBOLIZE_REASON_INVALID_FILE_OFFSET,
1233+
),
1234+
(
1235+
Reason::MissingComponent,
1236+
BLAZE_SYMBOLIZE_REASON_MISSING_COMPONENT,
1237+
),
1238+
(Reason::MissingSyms, BLAZE_SYMBOLIZE_REASON_MISSING_SYMS),
1239+
(Reason::Unsupported, BLAZE_SYMBOLIZE_REASON_UNSUPPORTED),
1240+
(Reason::UnknownAddr, BLAZE_SYMBOLIZE_REASON_UNKNOWN_ADDR),
1241+
];
1242+
1243+
for (reason, expected) in data {
1244+
assert_eq!(blaze_symbolize_reason::from(reason), expected);
1245+
let cstr = unsafe { CStr::from_ptr(blaze_symbolize_reason_str(expected)) };
1246+
let expected = CStr::from_bytes_with_nul(reason.as_bytes()).unwrap();
1247+
assert_eq!(cstr, expected);
1248+
}
1249+
}
1250+
1251+
11391252
/// Check that we can convert a [`blaze_symbolize_src_kernel`]
11401253
/// reference into a [`Kernel`].
11411254
#[tag(miri)]
@@ -1201,6 +1314,7 @@ mod tests {
12011314
code_info,
12021315
inlined_cnt,
12031316
inlined,
1317+
reason,
12041318
reserved: _,
12051319
} = sym;
12061320

@@ -1219,6 +1333,7 @@ mod tests {
12191333
let () = touch_cstr(*name);
12201334
let () = touch_code_info(code_info);
12211335
}
1336+
let () = touch(reason);
12221337
}
12231338
}
12241339

@@ -1327,6 +1442,10 @@ mod tests {
13271442
unsafe { CStr::from_ptr(sym.name) },
13281443
CStr::from_bytes_with_nul(b"factorial\0").unwrap()
13291444
);
1445+
assert_eq!(
1446+
sym.reason,
1447+
blaze_symbolize_reason::BLAZE_SYMBOLIZE_REASON_SUCCESS
1448+
);
13301449
assert_eq!(sym.addr, 0x2000100);
13311450
assert_eq!(sym.offset, 0);
13321451

@@ -1697,6 +1816,10 @@ mod tests {
16971816
// Shouldn't have symbolized because the debug link target cannot be
16981817
// found.
16991818
assert_eq!(sym.name, ptr::null());
1819+
assert_eq!(
1820+
sym.reason,
1821+
blaze_symbolize_reason::BLAZE_SYMBOLIZE_REASON_MISSING_SYMS
1822+
);
17001823

17011824
let () = unsafe { blaze_result_free(result) };
17021825
let () = unsafe { blaze_symbolizer_free(symbolizer) };

src/symbolize/mod.rs

+20-9
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ use std::fmt::Display;
102102
use std::fmt::Formatter;
103103
use std::fmt::Result as FmtResult;
104104
use std::path::Path;
105+
use std::str;
105106

106107
cfg_apk! {
107108
pub use source::Apk;
@@ -388,7 +389,7 @@ pub struct Sym<'src> {
388389
/// The reason is generally only meant as a hint. Reasons reported may change
389390
/// over time and, hence, should not be relied upon for the correctness of the
390391
/// application.
391-
#[derive(Clone, Debug, PartialEq)]
392+
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
392393
#[non_exhaustive]
393394
pub enum Reason {
394395
/// The absolute address was not found in the corresponding process' virtual
@@ -410,16 +411,26 @@ pub enum Reason {
410411
UnknownAddr,
411412
}
412413

414+
impl Reason {
415+
#[doc(hidden)]
416+
#[inline]
417+
pub fn as_bytes(&self) -> &'static [u8] {
418+
match self {
419+
Self::Unmapped => b"absolute address not found in virtual memory map of process\0",
420+
Self::InvalidFileOffset => b"file offset does not map to a valid piece of code/data\0",
421+
Self::MissingComponent => b"proc maps entry has no component\0",
422+
Self::MissingSyms => b"symbolization source has no or no relevant symbols\0",
423+
Self::Unsupported => b"address belongs to unsupported entity\0",
424+
Self::UnknownAddr => b"address not found in symbolization source\0",
425+
}
426+
}
427+
}
428+
413429
impl Display for Reason {
414430
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
415-
let s = match self {
416-
Self::Unmapped => "absolute address not found in virtual memory map of process",
417-
Self::InvalidFileOffset => "file offset does not map to a valid piece of code/data",
418-
Self::MissingComponent => "proc maps entry has no component",
419-
Self::MissingSyms => "symbolization source has no or no relevant symbols",
420-
Self::Unsupported => "address belongs to unsupported entity",
421-
Self::UnknownAddr => "address not found in symbolization source",
422-
};
431+
let cstr = self.as_bytes();
432+
// SAFETY: `as_bytes` always returns a valid string.
433+
let s = unsafe { str::from_utf8_unchecked(&cstr[..cstr.len() - 1]) };
423434

424435
f.write_str(s)
425436
}

0 commit comments

Comments
 (0)