-
Notifications
You must be signed in to change notification settings - Fork 412
/
Copy pathfetch-assembly.js
124 lines (115 loc) · 4.15 KB
/
fetch-assembly.js
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
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @flow
import { assertExhaustiveCheck } from './flow';
import type {
ApiQueryError,
DecodedInstruction,
NativeSymbolInfo,
Lib,
} from 'firefox-profiler/types';
import { queryApiWithFallback } from './query-api';
import type { ExternalCommunicationDelegate } from './query-api';
import { isLocalURL } from './url';
export type FetchAssemblyResult =
| { type: 'SUCCESS', instructions: DecodedInstruction[] }
| { type: 'ERROR', errors: ApiQueryError[] };
/**
* Fetch a native function's assembly instructions, using the symbolication
* API /asm/v1, targeting either the browser symbolication API or a symbol server.
*/
export async function fetchAssembly(
nativeSymbolInfo: NativeSymbolInfo,
lib: Lib,
symbolServerUrl: string,
delegate: ExternalCommunicationDelegate
): Promise<FetchAssemblyResult> {
// Make a request to /asm/v1. The API format for this endpoint is documented
// at https://github.com/mstange/samply/blob/main/API.md#asmv1
const symbolServerUrlForFallback = _serverMightSupportAssembly(
symbolServerUrl
)
? symbolServerUrl
: null;
const { debugName, breakpadId, name, codeId } = lib;
const { address } = nativeSymbolInfo;
const queryResult = await queryApiWithFallback(
'/asm/v1',
JSON.stringify({
debugName,
debugId: breakpadId,
name,
codeId,
startAddress: `0x${address.toString(16)}`,
size: `0x${nativeSymbolInfo.functionSize.toString(16)}`,
continueUntilFunctionEnd: !nativeSymbolInfo.functionSizeIsKnown,
}),
symbolServerUrlForFallback,
delegate,
convertJsonInstructions
);
switch (queryResult.type) {
case 'SUCCESS':
return {
type: 'SUCCESS',
instructions: queryResult.convertedResponse,
};
case 'ERROR': {
return { type: 'ERROR', errors: queryResult.errors };
}
default:
throw assertExhaustiveCheck(queryResult.type);
}
}
// At the moment, the official Mozilla symbolication server does not have an
// endpoint for requesting assembly code. The /asm/v1 URL is only supported by
// local symbol servers. Check the symbol server URL to avoid hammering the
// official Mozilla symbolication server with requests it can't handle.
// This check can be removed once it adds support for /asm/v1.
function _serverMightSupportAssembly(symbolServerUrl: string): boolean {
return isLocalURL(symbolServerUrl);
}
// Convert the response from the JSON format into our own DecodedInstruction
// format. The JSON format uses relative offsets to the start address for each
// instruction, and DecodedInstruction has the resolved address.
//
// We currently discard a fair amount of information. We'll consume the rest of
// the information once we have UI which uses it.
function convertJsonInstructions(
responseJSON: MixedObject
): DecodedInstruction[] {
if (!responseJSON.startAddress) {
throw new Error('Missing startAddress field in asm response');
}
if (!responseJSON.instructions) {
throw new Error('Missing instructions field in asm response');
}
if (!Array.isArray(responseJSON.instructions)) {
throw new Error('The instructions field in asm response is not an array');
}
const { startAddress, instructions } = responseJSON;
const startAddressNum = parseInt(startAddress, 16);
if (isNaN(startAddressNum)) {
throw new Error('Invalid startAddress value in asm response');
}
return instructions.map((instructionData) => {
if (!Array.isArray(instructionData)) {
throw new Error('Invalid instruction data (not an array)');
}
if (instructionData.length < 2) {
throw new Error('Invalid instruction data (< 2 elements)');
}
const [offset, decodedString] = instructionData;
if (typeof offset !== 'number') {
throw new Error('Invalid instruction offset');
}
if (typeof decodedString !== 'string') {
throw new Error('Invalid instruction decodedString value');
}
return {
address: startAddressNum + offset,
decodedString,
};
});
}