Skip to content

Commit cdadd8e

Browse files
committed
Merge branch 'seanmcm/localizationJune29_2' of https://github.com/microsoft/vscode-cpptools into seanmcm/localizationJune29_2
2 parents bfa5913 + 9e3c097 commit cdadd8e

2 files changed

Lines changed: 51 additions & 40 deletions

File tree

Extension/src/LanguageServer/extension.ts

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,13 +1165,10 @@ export function watchForCrashes(crashDirectory: string): void {
11651165
let previousCrashData: string;
11661166
let previousCrashCount: number = 0;
11671167

1168-
function logCrashTelemetry(data: string, type: string, offsetData?: string, crashLog?: string): void {
1168+
function logCrashTelemetry(data: string, type: string, crashLog?: string): void {
11691169
const crashObject: Record<string, string> = {};
11701170
const crashCountObject: Record<string, number> = {};
11711171
crashObject.CrashingThreadCallStack = data;
1172-
if (offsetData !== undefined) {
1173-
crashObject.CrashingThreadCallStackOffsets = offsetData;
1174-
}
11751172
if (crashLog !== undefined) {
11761173
crashObject.CrashLog = crashLog;
11771174
}
@@ -1185,8 +1182,8 @@ function logMacCrashTelemetry(data: string): void {
11851182
logCrashTelemetry(data, "MacCrash");
11861183
}
11871184

1188-
function logCppCrashTelemetry(data: string, offsetData?: string, crashLog?: string): void {
1189-
logCrashTelemetry(data, "CppCrash", offsetData, crashLog);
1185+
function logCppCrashTelemetry(data: string, crashLog?: string): void {
1186+
logCrashTelemetry(data, "CppCrash", crashLog);
11901187
}
11911188

11921189
function handleMacCrashFileRead(err: NodeJS.ErrnoException | undefined | null, data: string): void {
@@ -1292,6 +1289,39 @@ function containsFilteredTelemetryData(str: string): boolean {
12921289
return regex.test(str);
12931290
}
12941291

1292+
// Non-null fault addresses are randomized by ASLR (and use-after-free/wild pointers vary run to
1293+
// run), so embedding the raw value in CrashingThreadCallStack would fragment crash buckets and
1294+
// make CrashCount meaningless. Preserve near-null addresses (typical null-pointer dereferences,
1295+
// which are stable and useful for bucketing), but replace arbitrary addresses with a stable
1296+
// placeholder so identical crashes still de-duplicate.
1297+
function bucketSignalAddress(address: string): string {
1298+
let value: bigint;
1299+
try {
1300+
value = BigInt(address.trim());
1301+
} catch {
1302+
return address; // Not a parseable address; leave it untouched.
1303+
}
1304+
// 0x10000 (64 KB) covers null plus small member/array offsets off a null pointer.
1305+
return value < 0x10000n ? address : "<non-null>";
1306+
}
1307+
1308+
// An unsymbolized frame is reported as a raw runtime address. Addresses in the fixed-base main
1309+
// executable (non-PIE on Linux) stay constant across runs and are useful for bucketing, but
1310+
// addresses in the ASLR-randomized shared-library/mmap region (Linux 0x7f..., and on macOS the
1311+
// PIE main image and dyld shared cache) shift every launch and would fragment crash buckets. Keep
1312+
// the low, fixed addresses but replace high (relocated) ones with a stable placeholder. 4 GB is a
1313+
// safe cut: a non-PIE executable's own code loads well below it, while the relocated region is far
1314+
// above it.
1315+
function bucketFrameAddress(address: string): string {
1316+
let value: bigint;
1317+
try {
1318+
value = BigInt(address.trim());
1319+
} catch {
1320+
return address; // Not a parseable address; leave it untouched.
1321+
}
1322+
return value < 0x100000000n ? address : "<relocated>";
1323+
}
1324+
12951325
async function handleCrashFileRead(crashDirectory: string, crashFile: string, crashDate: Date, err: NodeJS.ErrnoException | undefined | null, data: string): Promise<void> {
12961326
if (err) {
12971327
if (err.code === "ENOENT") {
@@ -1301,16 +1331,15 @@ async function handleCrashFileRead(crashDirectory: string, crashFile: string, cr
13011331
}
13021332

13031333
const lines: string[] = data.split("\n");
1304-
let addressData: string;
1305-
const isCppToolsSrv2: boolean = crashFile.startsWith("cpptools-srv2");
1306-
const isCppToolsSrv: boolean = crashFile.startsWith("cpptools-srv");
1307-
const telemetryHeader: string = (isCppToolsSrv2 ? "cpptools-srv2.txt" : isCppToolsSrv ? "cpptools-srv.txt" : crashFile) + "\n";
1334+
let signalInfo: string;
1335+
const processName: string = (crashFile.startsWith("cpptools-srv2") ? "cpptools-srv2 process" :
1336+
crashFile.startsWith("cpptools-srv") ? "cpptools-srv process" :
1337+
crashFile.startsWith("cpptools-wordexp") ? "cpptools-wordexp process" : "cpptools process") + "\n";
13081338
const filtPath: string | null = which.sync("c++filt", { nothrow: true });
13091339
const isMac: boolean = process.platform === "darwin";
13101340
const startStr: string = isMac ? " _" : "<";
13111341
const offsetStr: string = isMac ? " + " : "+";
13121342
const endOffsetStr: string = isMac ? " " : " <";
1313-
const dotStr: string = "…\n";
13141343
let signalType: string;
13151344
let crashLog: string = "";
13161345
let crashStackStartLine: number = 0;
@@ -1333,40 +1362,38 @@ async function handleCrashFileRead(crashDirectory: string, crashFile: string, cr
13331362
}
13341363
if (lines[crashStackStartLine].startsWith("SIG")) {
13351364
signalType = `${lines[crashStackStartLine]}\n`;
1336-
addressData = `${lines[crashStackStartLine + 1]}:${lines[crashStackStartLine + 2]}\n`; // signalCode:signalAddr
1365+
signalInfo = `si_code=${lines[crashStackStartLine + 1]}, si_addr=${bucketSignalAddress(lines[crashStackStartLine + 2])}\n`;
13371366
crashStackStartLine += 3;
13381367
} else {
13391368
// The signal type may fail to be written.
13401369
// Intentionally different from SIGUNKNOWN from cpptools,
13411370
// and not SIG-? to avoid matching the regex in containsFilteredTelemetryData.
13421371
signalType = "SIGMISSING\n";
1343-
addressData = ".\n";
1372+
signalInfo = "";
13441373
}
1345-
data = telemetryHeader + signalType;
1374+
data = processName + signalType + signalInfo;
13461375
let crashCallStack: string = "";
13471376
let validFrameFound: boolean = false;
13481377
for (let lineNum: number = crashStackStartLine; lineNum < lines.length - 3; ++lineNum) { // skip last lines
13491378
const line: string = lines[lineNum];
13501379
const startPos: number = line.indexOf(startStr);
13511380
let pendingCallStack: string = "";
13521381
if (startPos === -1 || line[startPos + (isMac ? 1 : 4)] === "+") {
1353-
pendingCallStack = dotStr;
13541382
const startAddressPos: number = line.indexOf("0x");
13551383
const endAddressPos: number = line.indexOf(endOffsetStr, startAddressPos + 2);
13561384
if (startAddressPos === -1 || endAddressPos === -1 || startAddressPos >= endAddressPos) {
1357-
addressData += "Unexpected offset\n";
1385+
pendingCallStack = "Unexpected offset\n";
13581386
} else {
1359-
let pendingAddressData: string = line.substring(startAddressPos, endAddressPos) + "\n";
1387+
let pendingAddressData: string = bucketFrameAddress(line.substring(startAddressPos, endAddressPos)) + "\n";
13601388
if (containsFilteredTelemetryData(pendingAddressData)) {
13611389
pendingAddressData = "?\n";
13621390
}
1363-
addressData += pendingAddressData;
1391+
pendingCallStack = pendingAddressData;
13641392
}
13651393
} else {
13661394
const offsetPos: number = line.indexOf(offsetStr, startPos + startStr.length);
13671395
if (offsetPos === -1) {
13681396
pendingCallStack = "Missing offsetStr\n";
1369-
addressData += "\n";
13701397
} else {
13711398
const startPos2: number = startPos + 1;
13721399
let funcStr: string = line.substring(startPos2, offsetPos);
@@ -1397,27 +1424,13 @@ async function handleCrashFileRead(crashDirectory: string, crashFile: string, cr
13971424
// Compute pendingOffset.
13981425
if (isMac) {
13991426
pendingOffset += line.substring(offsetPos2);
1400-
const startAddressPos: number = line.indexOf("0x");
1401-
if (startAddressPos === -1 || startAddressPos >= startPos) {
1402-
// unexpected
1403-
pendingOffset += "<Missing 0x>";
1404-
addressData += "\n";
1405-
} else {
1406-
let pendingAddressData: string = line.substring(startAddressPos, startPos) + "\n";
1407-
if (containsFilteredTelemetryData(pendingAddressData)) {
1408-
pendingAddressData = "?\n";
1409-
}
1410-
addressData += pendingAddressData;
1411-
}
14121427
} else {
14131428
const endPos: number = line.indexOf(">", offsetPos2);
14141429
if (endPos === -1) {
14151430
pendingOffset += "<Missing > >"; // unexpected
14161431
} else {
14171432
pendingOffset += line.substring(offsetPos2, endPos);
14181433
}
1419-
addressData += "\n";
1420-
// TODO: It seems like addressData should be obtained on Linux in case the function is filtered.
14211434
}
14221435
pendingOffset += "\n";
14231436
pendingCallStack = funcStr + pendingOffset;
@@ -1447,19 +1460,18 @@ async function handleCrashFileRead(crashDirectory: string, crashFile: string, cr
14471460
}
14481461

14491462
crashCallStack = crashCallStack.trimEnd();
1450-
addressData = addressData.trimEnd();
14511463

14521464
if (crashCallStack !== prevCppCrashCallStackData) {
14531465
prevCppCrashCallStackData = crashCallStack;
14541466

14551467
if (lines.length >= 6 && util.getLoggingLevel() >= 1) {
1456-
getCrashCallStacksChannel().appendLine(`\n${isCppToolsSrv2 ? "cpptools-srv2" : isCppToolsSrv ? "cpptools-srv" : "cpptools"}\n${crashDate.toLocaleString()}\n${signalType}${crashCallStack}${crashLog.length > 0 ? "\n\n" + crashLog : ""}`);
1468+
getCrashCallStacksChannel().appendLine(`\n${processName}${crashDate.toLocaleString()}\n${signalType}${signalInfo}${crashCallStack}${crashLog.length > 0 ? "\n\n" + crashLog : ""}`);
14571469
}
14581470
}
14591471

14601472
data += crashCallStack;
14611473

1462-
logCppCrashTelemetry(data, addressData, crashLog);
1474+
logCppCrashTelemetry(data, crashLog);
14631475

14641476
await util.deleteFile(path.resolve(crashDirectory, crashFile)).catch(logAndReturn.undefined);
14651477
if (crashFile === "cpptools.txt") {

Extension/src/LanguageServer/settings.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44
* ------------------------------------------------------------------------------------------ */
55
'use strict';
66

7-
import { execSync } from 'child_process';
7+
import { execFileSync } from 'child_process';
88
import * as os from 'os';
99
import * as path from 'path';
1010
import * as semver from 'semver';
11-
import { quote } from 'shell-quote';
1211
import * as vscode from 'vscode';
1312
import * as nls from 'vscode-nls';
1413
import * as which from 'which';
@@ -297,7 +296,7 @@ export class CppSettings extends Settings {
297296
let bundledVersion: string;
298297
try {
299298
const bundledPath: string = getExtensionFilePath(`./LLVM/bin/${clangName}`);
300-
const output: string = execSync(quote([bundledPath, '--version'])).toString();
299+
const output: string = execFileSync(bundledPath, ['--version']).toString();
301300
bundledVersion = output.match(/(\d+\.\d+\.\d+)/)?.[1] ?? "";
302301
if (!semver.valid(bundledVersion)) {
303302
return path;
@@ -309,7 +308,7 @@ export class CppSettings extends Settings {
309308

310309
// Invoke the version on the system to compare versions. Use ours if it's more recent.
311310
try {
312-
const output: string = execSync(`"${path}" --version`).toString();
311+
const output: string = execFileSync(path, ['--version']).toString();
313312
const userVersion = output.match(/(\d+\.\d+\.\d+)/)?.[1] ?? "";
314313
if (semver.ltr(userVersion, bundledVersion)) {
315314
path = "";

0 commit comments

Comments
 (0)