Skip to content

Commit 2e8db89

Browse files
committed
c++のスタックトレースを出せるようにした
1 parent 7bfea0d commit 2e8db89

File tree

2 files changed

+87
-11
lines changed

2 files changed

+87
-11
lines changed

app/terminal/repl.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { useSectionCode } from "../[docs_id]/section";
1616
import { Terminal } from "@xterm/xterm";
1717

1818
export interface ReplOutput {
19-
type: "stdout" | "stderr" | "error" | "return" | "system"; // 出力の種類
19+
type: "stdout" | "stderr" | "error" | "return" | "trace" | "system"; // 出力の種類
2020
message: string; // 出力メッセージ
2121
}
2222
export interface ReplCommand {
@@ -41,6 +41,9 @@ export function writeOutput(
4141
case "error":
4242
term.write(chalk.red(message));
4343
break;
44+
case "trace":
45+
term.write(chalk.blue.italic(message));
46+
break;
4447
case "system":
4548
term.write(systemMessageColor(message));
4649
break;

app/terminal/wandbox/wandbox.tsx

Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ interface WandboxOptions {
2626
compilerName: string;
2727
compilerOptions: string[];
2828
commandline: string;
29+
compilerOptionsRaw: string[];
30+
additionalFiles: Record<string, string>;
31+
additionalSources: string[];
2932
}
3033

3134
const WandboxContext = createContext<IWandboxContext>(null!);
@@ -111,6 +114,36 @@ interface CompileResult {
111114
url: string;
112115
}
113116

117+
const CPP_STACKTRACE_HANDLER = `
118+
#define BOOST_STACKTRACE_USE_ADDR2LINE
119+
#include <boost/stacktrace.hpp>
120+
#include <iostream>
121+
#include <signal.h>
122+
void signal_handler(int signum) {
123+
signal(signum, SIG_DFL);
124+
switch(signum) {
125+
case SIGABRT:
126+
std::cerr << "Aborted" << std::endl;
127+
break;
128+
case SIGSEGV:
129+
std::cerr << "Segmentation fault" << std::endl;
130+
break;
131+
default:
132+
std::cerr << "Signal " << signum << " received" << std::endl;
133+
break;
134+
}
135+
std::cerr << "Stack trace:" << std::endl;
136+
std::cerr << boost::stacktrace::stacktrace();
137+
raise(signum);
138+
}
139+
struct _init_signal_handler {
140+
_init_signal_handler() {
141+
signal(SIGABRT, signal_handler);
142+
signal(SIGSEGV, signal_handler);
143+
}
144+
} _init_signal_handler_instance;
145+
`;
146+
114147
const compilerInfoFetcher: Fetcher<CompilerInfo[]> = () =>
115148
fetch(new URL("/api/list.json", WANDBOX)).then(
116149
(res) => res.json() as Promise<CompilerInfo[]>
@@ -158,16 +191,17 @@ export function WandboxProvider({ children }: { children: ReactNode }) {
158191
for (const switchSelect of selectedCompiler.switches.filter(
159192
(s) => s.type === "select"
160193
)) {
161-
// boostはnothing、stdは最新を選ぶ ほかはデフォルト
194+
// boost最新、stdは最新を選ぶ ほかはデフォルト
162195
if (switchSelect.name.includes("boost")) {
163-
const boostNothingOption = switchSelect.options.find((o) =>
164-
o.name.includes("nothing")
165-
);
166-
if (boostNothingOption) {
167-
compilerOptions.push(boostNothingOption.name);
168-
commandlineArgs.push(boostNothingOption["display-flags"]);
196+
const boostLatestOption = switchSelect.options
197+
.filter((o) => !o.name.includes("nothing"))
198+
.sort()
199+
.reverse()[0];
200+
if (boostLatestOption) {
201+
compilerOptions.push(boostLatestOption.name);
202+
// commandlineArgs.push(boostLatestOption["display-flags"]);
169203
} else {
170-
console.warn("boost nothing option not found");
204+
console.warn("boost option not found");
171205
}
172206
} else if (switchSelect.name.includes("std")) {
173207
const stdLatestOption = switchSelect.options
@@ -189,10 +223,18 @@ export function WandboxProvider({ children }: { children: ReactNode }) {
189223
}
190224
}
191225

226+
// その他オプション
227+
// commandlineArgs.push("-g");
228+
192229
return {
193230
compilerName,
194231
compilerOptions,
195232
commandline: commandlineArgs.join(" "),
233+
compilerOptionsRaw: ["-g"],
234+
additionalFiles: {
235+
"_stacktrace.cpp": CPP_STACKTRACE_HANDLER,
236+
},
237+
additionalSources: ["_stacktrace.cpp"],
196238
} satisfies WandboxOptions;
197239
}, [compilerList]);
198240

@@ -240,10 +282,22 @@ export function WandboxProvider({ children }: { children: ReactNode }) {
240282
file: name,
241283
code: files[name] || "",
242284
}))
285+
)
286+
.concat(
287+
Object.entries(options.additionalFiles).map(([file, code]) => ({
288+
file,
289+
code,
290+
}))
243291
),
244292
options: options.compilerOptions.join(","),
245293
stdin: "",
246-
"compiler-option-raw": namesSource.join("\n"),
294+
"compiler-option-raw": [
295+
options.compilerOptionsRaw,
296+
namesSource,
297+
options.additionalSources,
298+
]
299+
.flat()
300+
.join("\n"),
247301
"runtime-option-raw": "",
248302
save: false,
249303
is_private: true,
@@ -277,12 +331,31 @@ export function WandboxProvider({ children }: { children: ReactNode }) {
277331
);
278332
}
279333
if (result.program_error) {
334+
const errorBeforeTrace =
335+
result.program_error.split("Stack trace:\n")[0];
280336
outputs = outputs.concat(
281-
result.program_error
337+
errorBeforeTrace
282338
.trim()
283339
.split("\n")
284340
.map((line) => ({ type: "error" as const, message: line }))
285341
);
342+
if (result.program_error.includes("Stack trace:")) {
343+
// CPP_STACKTRACE_HANDLER のコードで出力されるスタックトレースを、js側でパースしていい感じに表示する
344+
const stackTrace = result.program_error.split("Stack trace:\n")[1];
345+
outputs = outputs.concat({
346+
type: "trace" as const,
347+
message: "Stack trace (filtered):",
348+
});
349+
for (const line of stackTrace.trim().split("\n")) {
350+
// ユーザーのソースコードだけを対象にする
351+
if (line.includes("/home/wandbox")) {
352+
outputs = outputs.concat({
353+
type: "trace" as const,
354+
message: line.replace("/home/wandbox/", ""),
355+
});
356+
}
357+
}
358+
}
286359
}
287360
if (result.status !== "0") {
288361
outputs.push({

0 commit comments

Comments
 (0)