Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Fix import for some Instruments profiles #216

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions sample/profiles/Instruments/8.3.3/integer-uniquer-index.grammar
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<ufwb version="1.17">
<grammar name="INDEX grammar" start="id:10" author="Jamie Wong" fileextension="index">
<grammar name="INDEX grammar" start="id:43" author="Jamie Wong" fileextension="index">
<description>Grammar for INDEX files</description>
<structure name="integeruniquer.index file" id="10" encoding="ISO_8859-1:1987" endian="big" signed="no">
<binary name="Header" id="12" length="32"/>
<structure name="Entry" id="16" length="8" repeatmax="-1">
<number name="ByteOffset" id="15" type="integer" length="4" endian="little"/>
<number name="MegabyteOffset" id="18" type="integer" length="4" endian="little"/>
<structure name="integeruniquer.index file" id="43" encoding="ISO_8859-1:1987" endian="big" signed="no">
<binary name="Header" id="44" length="32"/>
<structure name="Entry" id="45" length="8" repeatmax="-1">
<number name="ByteOffset" id="46" fillcolor="FFB598" type="integer" length="4" endian="little"/>
<number name="MegabyteOffset" id="47" fillcolor="A0FF7B" type="integer" length="4" endian="little"/>
</structure>
</structure>
</grammar>
Expand Down
29 changes: 15 additions & 14 deletions sample/profiles/Instruments/8.3.3/time-sample-bulkstore.grammar
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<ufwb version="1.17">
<grammar name="time-sample bulkstore" start="id:1" author="Jamie Wong" fileextension="bin" uti="com.apple.macbinary-archive">
<grammar name="time-sample bulkstore" start="id:17" author="Jamie Wong" fileextension="bin" uti="com.apple.macbinary-archive">
<description>Grammar for BIN files</description>
<structure name="time-sample bulkstore" id="1" length="0" encoding="ISO_8859-1:1987" endian="little" signed="no">
<structure name="Header" id="2" length="Size" endian="little">
<binary name="???0" id="3" length="4"/>
<number name="???1" id="4" type="integer" length="4"/>
<number name="???2" id="5" type="integer" length="4"/>
<number name="Size" id="6" type="integer" length="4"/>
<number name="BytesPerEntry" id="7" type="integer" length="2"/>
<number name="???4" id="8" type="integer" length="4"/>
<number name="???5" id="9" type="integer" length="4"/>
<structure name="time-sample bulkstore" id="17" length="0" encoding="ISO_8859-1:1987" endian="little" signed="no">
<structure name="Header" id="18" length="Size" endian="little">
<binary name="???0" id="19" length="4"/>
<number name="???1" id="20" type="integer" length="4"/>
<number name="???2" id="21" type="integer" length="4"/>
<number name="Size" id="22" type="integer" length="4"/>
<number name="BytesPerEntry" id="23" type="integer" length="2"/>
<number name="???4" id="24" type="integer" length="4"/>
<number name="???5" id="25" type="integer" length="4"/>
</structure>
<structure name="Data" id="11" length="BytesPerEntry" repeatmax="-1">
<number name="Timestamp" id="12" fillcolor="33FF70" type="integer" length="6" endian="little"/>
<binary name="&lt;Binary Fill Bytes&gt;" id="13" unused="yes" length="23"/>
<number name="Backtrace ID" id="14" fillcolor="FF8284" type="integer" length="4" endian="little"/>
<structure name="Data" id="27" length="BytesPerEntry" repeatmax="-1">
<number name="Timestamp" id="28" fillcolor="33FF70" type="integer" length="6" endian="little"/>
<number name="Thread ID" id="33" fillcolor="65A0FF" type="integer" length="4"/>
<binary name="&lt;Binary Fill Bytes&gt;" id="29" unused="yes" fillcolor="C0C0C0" length="19"/>
<number name="Backtrace ID" id="30" fillcolor="FF8284" type="integer" length="4" endian="little"/>
</structure>
</structure>
</grammar>
Expand Down
52 changes: 41 additions & 11 deletions src/import/instruments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,21 @@ async function getRawSampleList(core: TraceDirectoryTree): Promise<Sample[]> {
while (true) {
// Schema as of Instruments 8.3.3 is a 6 byte timestamp, followed by a bunch
// of stuff we don't care about, followed by a 4 byte backtrace ID
const timestampBytes = 48 / 8
const timestamp = bulkstore.readUint48()
if (timestamp === 0) break

const threadIDBytes = 32 / 8
const threadID = bulkstore.readUint32()

bulkstore.skip(bytesPerEntry - 6 - 4 - 4)
// Skip the stuff we don't care about. We can do this because we know how
// many bytes there shuold be per entry from the header of the file, and
// we know how many bytes we're reading for each of the fields we do care
// about.
const backtraceIDBytes = 32 / 8
bulkstore.skip(bytesPerEntry - timestampBytes - threadIDBytes - backtraceIDBytes)
const backtraceID = bulkstore.readUint32()

samples.push({timestamp, threadID, backtraceID})
}
return samples
Expand Down Expand Up @@ -508,22 +516,44 @@ export function importThreadFromInstrumentsTrace(args: {
const profile = new StackListProfileBuilder(lastOf(samples)!.timestamp)
profile.setName(`${fileName} - thread ${threadID}`)

// Each sample's stack is identified by a single number. This number might be
// a an address, or an index into the integer array list. If it's an index
// into the integer arrays list, then the integer array it corresponds to
// might itself contain either addresses or indices into the integer array.
function appendRecursive(k: number, stack: FrameInfo[]) {
const frame = addressToFrameMap.get(k)
// First try: let's see if the number is an address and we have
// an associated stack frame for the address.
const address = k
const frame = addressToFrameMap.get(address)
if (frame) {
stack.push(frame)
} else if (k in arrays) {
for (let addr of arrays[k]) {
return
}

// Second try: let's see if the number is an index into the integer array
// list. We'll re-interpret the index as a 32 bit integer here by ignoring
// the upper 32 bits of the integer.
//
// TODO(jlfwong): This seems pretty dubious. Is there a more correct wait to
// differentiate between an address and a number? I wonder if there's either
// some metadata somewhere that we need or if there's something in the byte
// sequence that indicates what kind of number it is.
const arrayIndex = k & 0xffffffff
if (arrayIndex in arrays) {
for (let addr of arrays[arrayIndex]) {
appendRecursive(addr, stack)
}
} else {
const rawAddressFrame: FrameInfo = {
key: k,
name: `0x${zeroPad(k.toString(16), 16)}`,
}
addressToFrameMap.set(k, rawAddressFrame)
stack.push(rawAddressFrame)
return
}

// Fallback case: we'll say we can't find the address, and just
// display the address instead of frame information.
const rawAddressFrame: FrameInfo = {
key: k,
name: `0x${zeroPad(k.toString(16), 16)}`,
}
addressToFrameMap.set(k, rawAddressFrame)
stack.push(rawAddressFrame)
}

let lastTimestamp: null | number = null
Expand Down