mirror of
https://github.com/morluto/rea
synced 2026-10-25 14:01:13 -04:00
fix(apple): keep the recorded image name and frame symbol offset of IPS reports (#7059)
* fix(apple): keep the recorded image name and frame symbol offset of IPS reports Apple's IPS format documents usedImages[].name and frames[].symbolLocation, and docs/apple-ips.md says documented body fields are reproduced, but the parser shapes did not list either member. They were syntax-checked and discarded without an unlisted_keys entry, so a symbolicated frame lost its +offset and every image its binary name. Both are now kept: images[].name and frames[].symbol_location as a recorded integer, with type mismatches recorded like other fields. * fix(apple): count frame symbol offsets in the integer text check
This commit is contained in:
+3
-1
@@ -29,7 +29,9 @@ body is parsed only when that string is `"309"`. Other types, including `"288"`
|
||||
stackshots, return no crash result.
|
||||
|
||||
`process`, `exception`, `termination`, `faulting_thread`, `threads`, and
|
||||
`images` reproduce documented body fields when present. Integers keep their
|
||||
`images` reproduce documented body fields when present, including each image's
|
||||
`name` and each frame's `symbolLocation` (as `symbol_location`, the byte offset
|
||||
from the start of `recorded_symbol`). Integers keep their
|
||||
decimal literals and a hexadecimal form, including arm64e registers above
|
||||
2^53. Frame roles follow the recorded frame index: row zero of the resolved
|
||||
faulting thread is `faulting_instruction`, row zero of other threads is
|
||||
|
||||
@@ -273,3 +273,46 @@ it.each([
|
||||
if (!result.ok) expect(result.error._tag).toBe("AnalysisOutputError");
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
[21, false],
|
||||
[22, true],
|
||||
] as const)(
|
||||
"counts a frame symbol offset against %s UTF-16 integer text bytes",
|
||||
async (maximum, accepted) => {
|
||||
const withOffset: AppleIps = {
|
||||
...report,
|
||||
threads: [
|
||||
{
|
||||
index: 0,
|
||||
id: null,
|
||||
triggered: null,
|
||||
name: null,
|
||||
queue: null,
|
||||
frames: [
|
||||
{
|
||||
thread_index: 0,
|
||||
frame_index: 0,
|
||||
image_index: null,
|
||||
image_offset: null,
|
||||
runtime_address: null,
|
||||
role: "unknown",
|
||||
inline: null,
|
||||
recorded_symbol: "main",
|
||||
symbol_location: { decimal: "12345", hex: "0x3039" },
|
||||
},
|
||||
],
|
||||
state_flavor: null,
|
||||
registers: [],
|
||||
},
|
||||
],
|
||||
coverage: { ...report.coverage, threads: 1, frames: 1 },
|
||||
};
|
||||
const result = await new AppleIpsService({
|
||||
identity,
|
||||
inspect: () => Promise.resolve(ok(withOffset)),
|
||||
}).inspect({ path: report.artifact.path, max_integer_text_bytes: maximum });
|
||||
expect(result.ok).toBe(accepted);
|
||||
if (!result.ok) expect(result.error._tag).toBe("AnalysisOutputError");
|
||||
},
|
||||
);
|
||||
|
||||
@@ -170,6 +170,11 @@ const frameSchema = z.strictObject({
|
||||
]),
|
||||
inline: z.boolean().nullable(),
|
||||
recorded_symbol: z.string().nullable(),
|
||||
symbol_location: recordedIntegerSchema
|
||||
.nullable()
|
||||
.describe(
|
||||
"Recorded byte offset of the address from the start of recorded_symbol.",
|
||||
),
|
||||
});
|
||||
|
||||
const threadSchema = z.strictObject({
|
||||
@@ -192,6 +197,7 @@ const imageSchema = z.strictObject({
|
||||
size: recordedIntegerSchema.nullable(),
|
||||
uuid: uuidSchema.nullable(),
|
||||
uuid_unknown: z.boolean(),
|
||||
name: z.string().nullable(),
|
||||
path: z.string().nullable(),
|
||||
});
|
||||
|
||||
@@ -321,7 +327,8 @@ export const appleIpsReportIntegerTextBytes = (report: AppleIps): number => {
|
||||
for (const frame of thread.frames)
|
||||
bytes +=
|
||||
appleIpsIntegerTextBytes(frame.image_offset) +
|
||||
appleIpsIntegerTextBytes(frame.runtime_address);
|
||||
appleIpsIntegerTextBytes(frame.runtime_address) +
|
||||
appleIpsIntegerTextBytes(frame.symbol_location);
|
||||
}
|
||||
for (const mismatch of report.coverage.type_mismatches)
|
||||
bytes += 2 * (mismatch.recorded_integer?.length ?? 0);
|
||||
|
||||
@@ -86,6 +86,7 @@ const threadShape = (
|
||||
"imageIndex",
|
||||
"imageOffset",
|
||||
"symbol",
|
||||
"symbolLocation",
|
||||
"inline",
|
||||
),
|
||||
},
|
||||
@@ -180,6 +181,7 @@ export const ipsBodyShape = (input: InspectAppleIpsInput): JsonShape => {
|
||||
"base",
|
||||
"size",
|
||||
"uuid",
|
||||
"name",
|
||||
"path",
|
||||
),
|
||||
},
|
||||
|
||||
@@ -402,6 +402,39 @@ it("keeps ReportCrash-shaped producer facts and reports complete coverage", () =
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps the recorded image name and symbol offset", () => {
|
||||
const parsed = report({});
|
||||
expect(parsed.images.map((image) => image.name)).toEqual([
|
||||
"rea-crash-fixture",
|
||||
null,
|
||||
]);
|
||||
expect(parsed.threads[0]?.frames[0]).toMatchObject({
|
||||
recorded_symbol: "rea_fixture_fault",
|
||||
symbol_location: { decimal: "12", hex: "0xc" },
|
||||
});
|
||||
expect(parsed.threads[1]?.frames[0]?.symbol_location).toEqual({
|
||||
decimal: "0",
|
||||
hex: "0x0",
|
||||
});
|
||||
expect(parsed.coverage.status).toBe("complete");
|
||||
const wrong = parse(
|
||||
Buffer.from(
|
||||
appleIpsReport().replace(
|
||||
'"symbolLocation": 12',
|
||||
'"symbolLocation": "12"',
|
||||
),
|
||||
"utf8",
|
||||
),
|
||||
);
|
||||
expect(wrong.threads[0]?.frames[0]?.symbol_location).toBeNull();
|
||||
expect(wrong.coverage.type_mismatches).toContainEqual(
|
||||
expect.objectContaining({
|
||||
path: "body.threads[0].frames[0].symbolLocation",
|
||||
expected: "integer",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("assigns frame roles by recorded row without inventing inline stack addresses", () => {
|
||||
const roles = (parsed: ReturnType<typeof parse>) =>
|
||||
parsed.threads.map((thread) => thread.frames.map((frame) => frame.role));
|
||||
|
||||
@@ -222,6 +222,7 @@ const imagesOf = (
|
||||
size: read.integer(members.size, `${path}.size`),
|
||||
uuid: uuid.uuid,
|
||||
uuid_unknown: uuid.unknown || arch === null || arch === "",
|
||||
name: read.text(members.name, `${path}.name`),
|
||||
path: read.text(members.path, `${path}.path`),
|
||||
};
|
||||
});
|
||||
@@ -283,6 +284,10 @@ const threadsOf = (
|
||||
role,
|
||||
inline,
|
||||
recorded_symbol: read.text(frameMembers.symbol, `${framePath}.symbol`),
|
||||
symbol_location: read.integer(
|
||||
frameMembers.symbolLocation,
|
||||
`${framePath}.symbolLocation`,
|
||||
),
|
||||
};
|
||||
});
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user