fix(apple): keep the nested Rosetta registers of translated crash reports
A translated process records Rosetta's registers in a nested threadState.rosetta object. The parser read it as one register, so every valid report from a Rosetta process came back partial with a threadState.rosetta.value mismatch, and the nested registers were dropped. Each nested member is now reported as rosetta.<name> and counts toward the register budget.
This commit is contained in:
1 parent
3ce311757d
commit
1889ac1532
4 files changed
+68
-4
No files matched your search
@@ -38,6 +38,9 @@ address uses that row's own image base and offset. The recorded `inline` flag
|
|||||||
does not supply a missing offset or authorize borrowing another row's address.
|
does not supply a missing offset or authorize borrowing another row's address.
|
||||||
When `faultingThread` is missing or does not resolve to a recorded thread
|
When `faultingThread` is missing or does not resolve to a recorded thread
|
||||||
object, row-zero roles are `unknown`; the recorded lookup remains available.
|
object, row-zero roles are `unknown`; the recorded lookup remains available.
|
||||||
|
A translated process records Rosetta's registers in a nested
|
||||||
|
`threadState.rosetta` object; each one is reported as `rosetta.<name>` and
|
||||||
|
counts toward the register budget.
|
||||||
`process.os_version` keeps the producer's dictionary (`train`, `build`,
|
`process.os_version` keeps the producer's dictionary (`train`, `build`,
|
||||||
`release_type`, `is_embedded`) or the older string form. Image UUIDs are uppercase 8-4-4-4-12; an all-zero UUID
|
`release_type`, `is_embedded`) or the older string form. Image UUIDs are uppercase 8-4-4-4-12; an all-zero UUID
|
||||||
or a missing `arch` is `uuid_unknown`. Image `source` letters keep their
|
or a missing `arch` is `uuid_unknown`. Image `source` letters keep their
|
||||||
|
|||||||
@@ -96,6 +96,13 @@ const threadShape = (
|
|||||||
members: {
|
members: {
|
||||||
flavor: SCALAR,
|
flavor: SCALAR,
|
||||||
x: { kind: "array", items: register, beforeItem: take.registers },
|
x: { kind: "array", items: register, beforeItem: take.registers },
|
||||||
|
// Rosetta-translated processes nest their translator registers.
|
||||||
|
rosetta: {
|
||||||
|
kind: "object",
|
||||||
|
members: {},
|
||||||
|
rest: register,
|
||||||
|
beforeRest: take.registers,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
rest: register,
|
rest: register,
|
||||||
beforeRest: take.registers,
|
beforeRest: take.registers,
|
||||||
|
|||||||
@@ -500,6 +500,46 @@ it("reports wrong-type collections as partial instead of empty and complete", ()
|
|||||||
expect(empty.coverage.status).toBe("complete");
|
expect(empty.coverage.status).toBe("complete");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps the nested Rosetta registers of a translated process", () => {
|
||||||
|
const parsed = parse(
|
||||||
|
withBody(`{
|
||||||
|
"translated": true,
|
||||||
|
"threads": [
|
||||||
|
{
|
||||||
|
"threadState": {
|
||||||
|
"flavor": "x86_THREAD_STATE",
|
||||||
|
"rip": { "value": 4294983680 },
|
||||||
|
"rosetta": { "tmp0": { "value": 7 }, "tmp1": 8 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"usedImages": []
|
||||||
|
}`),
|
||||||
|
);
|
||||||
|
expect(parsed.threads[0]?.registers).toEqual([
|
||||||
|
{
|
||||||
|
name: "rip",
|
||||||
|
value: { decimal: "4294983680", hex: "0x100004000" },
|
||||||
|
description: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rosetta.tmp0",
|
||||||
|
value: { decimal: "7", hex: "0x7" },
|
||||||
|
description: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rosetta.tmp1",
|
||||||
|
value: { decimal: "8", hex: "0x8" },
|
||||||
|
description: null,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(parsed.coverage).toMatchObject({
|
||||||
|
status: "complete",
|
||||||
|
registers: 3,
|
||||||
|
type_mismatches: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("records wrong-type rows, scalars and registers with their source path", () => {
|
it("records wrong-type rows, scalars and registers with their source path", () => {
|
||||||
const parsed = parse(
|
const parsed = parse(
|
||||||
withBody(`{
|
withBody(`{
|
||||||
@@ -511,7 +551,7 @@ it("records wrong-type rows, scalars and registers with their source path", () =
|
|||||||
7,
|
7,
|
||||||
{
|
{
|
||||||
"frames": [{ "imageIndex": 99999999999, "imageOffset": "8" }, []],
|
"frames": [{ "imageIndex": 99999999999, "imageOffset": "8" }, []],
|
||||||
"threadState": { "flavor": 1, "x": [1, "two", { "value": null }, {}], "rosetta": { "tmp0": 1 } }
|
"threadState": { "flavor": 1, "x": [1, "two", { "value": null }, {}], "rosetta": { "tmp0": "one" } }
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"usedImages": [null, { "base": 1.0 }]
|
"usedImages": [null, { "base": 1.0 }]
|
||||||
@@ -567,9 +607,9 @@ it("records wrong-type rows, scalars and registers with their source path", () =
|
|||||||
observed: "absent",
|
observed: "absent",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "body.threads[1].threadState.rosetta.value",
|
path: "body.threads[1].threadState.rosetta.tmp0",
|
||||||
expected: "integer",
|
expected: "integer or object with integer value",
|
||||||
observed: "absent",
|
observed: "string",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "body.threads[1].threadState.flavor",
|
path: "body.threads[1].threadState.flavor",
|
||||||
|
|||||||
@@ -175,6 +175,20 @@ const threadStateOf = (
|
|||||||
});
|
});
|
||||||
for (const [key, entry] of Object.entries(members)) {
|
for (const [key, entry] of Object.entries(members)) {
|
||||||
if (key === "x" || key === "flavor") continue;
|
if (key === "x" || key === "flavor") continue;
|
||||||
|
if (key === "rosetta") {
|
||||||
|
// A translated process records Rosetta's registers as a nested state.
|
||||||
|
const rosetta = read.object(entry, `${path}.rosetta`);
|
||||||
|
for (const [name, nested] of Object.entries(rosetta?.members ?? {})) {
|
||||||
|
const register = registerValue(
|
||||||
|
`rosetta.${name}`,
|
||||||
|
nested,
|
||||||
|
`${path}.rosetta.${name}`,
|
||||||
|
read,
|
||||||
|
);
|
||||||
|
if (register !== null) registers.push(register);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const register = registerValue(key, entry, `${path}.${key}`, read);
|
const register = registerValue(key, entry, `${path}.${key}`, read);
|
||||||
if (register !== null) registers.push(register);
|
if (register !== null) registers.push(register);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user