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 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 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
|
use linux_perf_data::jitdump::{JitDumpReader, JitDumpRecord};
use yaxpeax_arch::{Arch, DecodeError, Reader, U8Reader};
fn main() {
let file = std::fs::File::open(
std::env::args()
.nth(1)
.unwrap_or("/Users/mstange/Downloads/jit-34147.dump".into()),
)
.unwrap();
let mut reader = JitDumpReader::new(file).unwrap();
let em_arch = reader.header().elf_machine_arch as u16;
while let Ok(Some(record)) = reader.next_record() {
let timestamp = record.timestamp;
match record.parse().unwrap() {
JitDumpRecord::CodeLoad(record) => {
println!(
"{timestamp:016} LOAD {} (pid: {}, tid: {})",
record.code_index, record.pid, record.tid
);
println!(
" address: {:#x}, size: {:#x}, name: {}",
record.code_addr,
record.code_bytes.len(),
std::str::from_utf8(&record.function_name.as_slice()).unwrap()
);
println!();
let _ = decode_arch(&record.code_bytes.as_slice(), em_arch);
}
JitDumpRecord::CodeMove(record) => {
println!(
"{timestamp:016} MOVE {} (pid: {}, tid: {})",
record.code_index, record.pid, record.tid
);
println!(
" address: {:#x} -> {:#x}, size: {:#x}",
record.old_code_addr, record.new_code_addr, record.code_size
);
println!();
}
JitDumpRecord::CodeDebugInfo(record) => {
println!("{timestamp:016} DEBUG INFO");
println!(" address: {:#x}", record.code_addr);
for entry in &record.entries {
println!(
" {:#8x} {}:{}:{}",
entry.code_addr,
std::str::from_utf8(&entry.file_path.as_slice()).unwrap(),
entry.line,
entry.column
);
}
println!();
}
JitDumpRecord::CodeClose => {
println!("{timestamp:016} CLOSE");
println!();
}
JitDumpRecord::CodeUnwindingInfo(_record) => {
println!("{timestamp:016} UNWINDING INFO");
println!();
}
JitDumpRecord::Other(record) => {
println!("{timestamp:016} <unknown type {}>", record.record_type.0);
println!();
}
}
}
}
/// ARM
const EM_ARM: u16 = 40;
/// ARM AARCH64
const EM_AARCH64: u16 = 183;
/// Intel 80386
const EM_386: u16 = 3;
/// AMD x86-64 architecture
const EM_X86_64: u16 = 62;
fn decode_arch(bytes: &[u8], elf_machine_arch: u16) -> Result<(), String> {
match elf_machine_arch {
EM_386 => decode::<yaxpeax_x86::protected_mode::Arch>(bytes),
EM_X86_64 => decode::<yaxpeax_x86::amd64::Arch>(bytes),
EM_AARCH64 => decode::<yaxpeax_arm::armv8::a64::ARMv8>(bytes),
EM_ARM => decode::<yaxpeax_arm::armv7::ARMv7>(bytes),
_ => {
return Err(format!(
"Unrecognized ELF machine architecture {elf_machine_arch}"
));
}
}
Ok(())
}
trait InstructionDecoding: Arch {
const ADJUST_BY_AFTER_ERROR: usize;
type InstructionDisplay<'a>: std::fmt::Display;
fn make_decoder() -> Self::Decoder;
fn inst_display(inst: &Self::Instruction) -> Self::InstructionDisplay<'_>;
}
impl InstructionDecoding for yaxpeax_x86::amd64::Arch {
const ADJUST_BY_AFTER_ERROR: usize = 1;
type InstructionDisplay<'a> = yaxpeax_x86::amd64::InstructionDisplayer<'a>;
fn make_decoder() -> Self::Decoder {
yaxpeax_x86::amd64::InstDecoder::default()
}
fn inst_display(inst: &Self::Instruction) -> Self::InstructionDisplay<'_> {
inst.display_with(yaxpeax_x86::amd64::DisplayStyle::Intel)
}
}
impl InstructionDecoding for yaxpeax_x86::protected_mode::Arch {
const ADJUST_BY_AFTER_ERROR: usize = 1;
type InstructionDisplay<'a> = &'a Self::Instruction;
fn make_decoder() -> Self::Decoder {
yaxpeax_x86::protected_mode::InstDecoder::default()
}
fn inst_display(inst: &Self::Instruction) -> Self::InstructionDisplay<'_> {
inst
}
}
impl InstructionDecoding for yaxpeax_arm::armv8::a64::ARMv8 {
const ADJUST_BY_AFTER_ERROR: usize = 4;
type InstructionDisplay<'a> = &'a Self::Instruction;
fn make_decoder() -> Self::Decoder {
yaxpeax_arm::armv8::a64::InstDecoder::default()
}
fn inst_display(inst: &Self::Instruction) -> Self::InstructionDisplay<'_> {
inst
}
}
impl InstructionDecoding for yaxpeax_arm::armv7::ARMv7 {
const ADJUST_BY_AFTER_ERROR: usize = 2;
type InstructionDisplay<'a> = &'a Self::Instruction;
fn make_decoder() -> Self::Decoder {
// Assume thumb. The Jitdump format doesn't seem to have a way of indicating
// ARM or thumb mode for 32 bit arm functions.
yaxpeax_arm::armv7::InstDecoder::default_thumb()
}
fn inst_display(inst: &Self::Instruction) -> Self::InstructionDisplay<'_> {
inst
}
}
fn decode<'a, A: InstructionDecoding>(bytes: &'a [u8])
where
u64: From<A::Address>,
U8Reader<'a>: yaxpeax_arch::Reader<A::Address, A::Word>,
{
use yaxpeax_arch::Decoder;
let mut reader = yaxpeax_arch::U8Reader::new(bytes);
let decoder = A::make_decoder();
let mut offset = 0;
loop {
let before = u64::from(reader.total_offset()) as u32;
match decoder.decode(&mut reader) {
Ok(inst) => {
println!("{offset:6x} {}", A::inst_display(&inst));
let after = u64::from(reader.total_offset()) as u32;
offset += after - before;
}
Err(e) => {
if e.data_exhausted() {
break;
}
let remaining_bytes = &bytes[offset as usize..];
let s = remaining_bytes
.iter()
.take(A::ADJUST_BY_AFTER_ERROR)
.map(|b| format!("{b:#02x}"))
.collect::<Vec<_>>()
.join(", ");
let s2 = remaining_bytes
.iter()
.take(A::ADJUST_BY_AFTER_ERROR)
.map(|b| format!("{b:02X}"))
.collect::<Vec<_>>()
.join(" ");
println!(
"{offset:6x} .byte {s:width$} # Invalid instruction {s2}: {e}",
width = A::ADJUST_BY_AFTER_ERROR * 6
);
offset += A::ADJUST_BY_AFTER_ERROR as u32;
let Some(reader_bytes) = bytes.get(offset as usize..) else {
break;
};
reader = U8Reader::new(reader_bytes);
}
}
}
println!();
}
|