mstange / samply

Command-line sampling profiler for macOS and Linux
Apache License 2.0
1.96k stars 48 forks source link

Fix symbols for perf.data_jit_app_cache frames #248

Closed mstange closed 4 weeks ago

mstange commented 4 weeks ago

We weren't applying the right offsets.

Simpleperf creates synthetic mappings for JIT code with synthetic images with paths like "/data/local/tmp/perf.data_jit_app_cache:1039560-1040440".

The FILE2 symbols for these images have the following shape:

SimpleperfFileRecord {
    path: "/data/local/tmp/perf.data_jit_app_cache:1080272-1090264",
    r#type: 2,
    min_vaddr: 0,
    symbol: [
        SimpleperfSymbol {
            vaddr: 1489045024,
            len: 1088,
            name: "mozilla.components.browser.state.state.TabSessionState.hashCode",
        },
        SimpleperfSymbol {
            vaddr: 1489046144,
            len: 2928,
            name: "kotlinx.coroutines.JobSupport.invokeOnCompletionInternal$kotlinx_coroutines_core",
        },
        // ...
        SimpleperfSymbol {
            vaddr: 1489103728,
            len: 428,
            name: "mozilla.components.lib.state.ext.StoreExtensionsKt$flow$1$subscription$1$1.invokeSuspend",
        },
    ],
    type_specific_msg: Some(
        ElfFile(
            SimpleperfElfFileInfo {
                file_offset_of_min_vaddr: 0,
            },
        ),
    ),
}

The vaddr addresses here are the absolute address in process virtual memory where this function is located. The 1080272 here is the file offset at which this mapping starts within the synthetic file.

What seems to work best is to interpret the vaddr values as "relative addresses", i.e. adjust min_vaddr and file_offset_of_min_vaddr so that when we compute the library-relative address for a stack frame inside such a mapping, we end up with the frame's absolute address.

We still treat each "jit range path" at a separate library, e.g. "/data/local/tmp/perf.data_jit_app_cache:1080272-1090264" will be a different library than "/data/local/tmp/perf.data_jit_app_cache:1067808-1068688". In other words, we don't try to combine multiple different ranges into a single perf.data_jit_app_cache library. Keeping the ranges separate means that we don't run into trouble if multiple processes have different JIT functions at the same in-process address; this would be trouble because the combined library might have different symbols at the same relative address.