Skip to content

Commit 75ef3fc

Browse files
committed
cli: print crash diagnostics
1 parent 1b9d84b commit 75ef3fc

File tree

3 files changed

+210
-8
lines changed

3 files changed

+210
-8
lines changed

cli/release-staging/index.js

Lines changed: 110 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,40 @@ const tar = require('tar')
1313

1414
const packageName = 'codecane'
1515

16+
/**
17+
* Terminal escape sequences to reset terminal state after the child process exits.
18+
* When the binary is SIGKILL'd, it can't clean up its own terminal state.
19+
* The wrapper (this process) survives and must reset these modes.
20+
*
21+
* Keep in sync with TERMINAL_RESET_SEQUENCES in cli/src/utils/renderer-cleanup.ts
22+
*/
23+
const TERMINAL_RESET_SEQUENCES =
24+
'\x1b[?1049l' + // Exit alternate screen buffer
25+
'\x1b[?1000l' + // Disable X10 mouse mode
26+
'\x1b[?1002l' + // Disable button event mouse mode
27+
'\x1b[?1003l' + // Disable any-event mouse mode (all motion)
28+
'\x1b[?1006l' + // Disable SGR extended mouse mode
29+
'\x1b[?1004l' + // Disable focus reporting
30+
'\x1b[?2004l' + // Disable bracketed paste mode
31+
'\x1b[?25h' // Show cursor
32+
33+
function resetTerminal() {
34+
try {
35+
if (process.stdin.isTTY && process.stdin.setRawMode) {
36+
process.stdin.setRawMode(false)
37+
}
38+
} catch {
39+
// stdin may be closed
40+
}
41+
try {
42+
if (process.stdout.isTTY) {
43+
process.stdout.write(TERMINAL_RESET_SEQUENCES)
44+
}
45+
} catch {
46+
// stdout may be closed
47+
}
48+
}
49+
1650
function createConfig(packageName) {
1751
const homeDir = os.homedir()
1852
const configDir = path.join(homeDir, '.config', 'manicode')
@@ -527,18 +561,24 @@ async function checkForUpdates(runningProcess, exitListener) {
527561
term.clearLine()
528562

529563
runningProcess.removeListener('exit', exitListener)
530-
runningProcess.kill('SIGTERM')
531564

532565
await new Promise((resolve) => {
533-
runningProcess.on('exit', resolve)
566+
let exited = false
567+
runningProcess.once('exit', () => {
568+
exited = true
569+
resolve()
570+
})
571+
runningProcess.kill('SIGTERM')
534572
setTimeout(() => {
535-
if (!runningProcess.killed) {
573+
if (!exited) {
536574
runningProcess.kill('SIGKILL')
575+
// Safety: resolve after giving SIGKILL time to take effect
576+
setTimeout(() => resolve(), 1000)
537577
}
538-
resolve()
539578
}, 5000)
540579
})
541580

581+
resetTerminal()
542582
console.log(`Update available: ${currentVersion}${latestVersion}`)
543583

544584
await downloadBinary(latestVersion)
@@ -548,8 +588,15 @@ async function checkForUpdates(runningProcess, exitListener) {
548588
detached: false,
549589
})
550590

551-
newChild.on('exit', (code) => {
552-
process.exit(code || 0)
591+
newChild.on('exit', (code, signal) => {
592+
resetTerminal()
593+
printCrashDiagnostics(code, signal)
594+
process.exit(signal ? 1 : (code || 0))
595+
})
596+
597+
newChild.on('error', (err) => {
598+
console.error('Failed to start codecane:', err.message)
599+
process.exit(1)
553600
})
554601

555602
return new Promise(() => {})
@@ -559,6 +606,54 @@ async function checkForUpdates(runningProcess, exitListener) {
559606
}
560607
}
561608

609+
function printCrashDiagnostics(code, signal) {
610+
// Windows NTSTATUS codes (unsigned DWORD)
611+
const unsignedCode = code != null && code < 0 ? (code >>> 0) : code
612+
const isIllegalInstruction =
613+
signal === 'SIGILL' ||
614+
(process.platform === 'win32' && unsignedCode === 0xC000001D)
615+
const isAccessViolation =
616+
signal === 'SIGSEGV' ||
617+
(process.platform === 'win32' && unsignedCode === 0xC0000005)
618+
const isBusError = signal === 'SIGBUS'
619+
const isAbort =
620+
signal === 'SIGABRT' ||
621+
(process.platform === 'win32' && unsignedCode === 0xC0000409)
622+
623+
if (!isIllegalInstruction && !isAccessViolation && !isBusError && !isAbort) return
624+
625+
const exitInfo = signal ? `signal ${signal}` : `code ${code}`
626+
console.error('')
627+
console.error(`❌ ${packageName} exited immediately (${exitInfo})`)
628+
console.error('')
629+
630+
if (isIllegalInstruction) {
631+
console.error('Your CPU may not support the required instruction set (AVX2).')
632+
console.error('This typically affects CPUs from before 2013.')
633+
console.error('Unfortunately, this binary is not compatible with your system.')
634+
console.error('')
635+
} else if (isAccessViolation) {
636+
console.error('The binary crashed with an access violation.')
637+
console.error('')
638+
} else if (isBusError) {
639+
console.error('The binary crashed with a bus error.')
640+
console.error('This may indicate a platform compatibility issue.')
641+
console.error('')
642+
} else if (isAbort) {
643+
console.error('The binary crashed with an abort signal.')
644+
console.error('')
645+
}
646+
647+
console.error('System info:')
648+
console.error(` Platform: ${process.platform} ${process.arch}`)
649+
console.error(` Node: ${process.version}`)
650+
console.error(` Binary: ${CONFIG.binaryPath}`)
651+
console.error('')
652+
console.error('Please report this issue at:')
653+
console.error(' https://github.com/CodebuffAI/codebuff/issues')
654+
console.error('')
655+
}
656+
562657
async function main() {
563658
console.log('\x1b[1m\x1b[91m' + '='.repeat(60) + '\x1b[0m')
564659
console.log('\x1b[1m\x1b[93m❄️ CODECANE STAGING ENVIRONMENT ❄️\x1b[0m')
@@ -574,12 +669,19 @@ async function main() {
574669
stdio: 'inherit',
575670
})
576671

577-
const exitListener = (code) => {
578-
process.exit(code || 0)
672+
const exitListener = (code, signal) => {
673+
resetTerminal()
674+
printCrashDiagnostics(code, signal)
675+
process.exit(signal ? 1 : (code || 0))
579676
}
580677

581678
child.on('exit', exitListener)
582679

680+
child.on('error', (err) => {
681+
console.error('Failed to start codecane:', err.message)
682+
process.exit(1)
683+
})
684+
583685
setTimeout(() => {
584686
checkForUpdates(child, exitListener)
585687
}, 100)

cli/release/index.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,7 @@ async function checkForUpdates(runningProcess, exitListener) {
589589

590590
newChild.on('exit', (code, signal) => {
591591
resetTerminal()
592+
printCrashDiagnostics(code, signal)
592593
process.exit(signal ? 1 : (code || 0))
593594
})
594595

@@ -604,6 +605,54 @@ async function checkForUpdates(runningProcess, exitListener) {
604605
}
605606
}
606607

608+
function printCrashDiagnostics(code, signal) {
609+
// Windows NTSTATUS codes (unsigned DWORD)
610+
const unsignedCode = code != null && code < 0 ? (code >>> 0) : code
611+
const isIllegalInstruction =
612+
signal === 'SIGILL' ||
613+
(process.platform === 'win32' && unsignedCode === 0xC000001D)
614+
const isAccessViolation =
615+
signal === 'SIGSEGV' ||
616+
(process.platform === 'win32' && unsignedCode === 0xC0000005)
617+
const isBusError = signal === 'SIGBUS'
618+
const isAbort =
619+
signal === 'SIGABRT' ||
620+
(process.platform === 'win32' && unsignedCode === 0xC0000409)
621+
622+
if (!isIllegalInstruction && !isAccessViolation && !isBusError && !isAbort) return
623+
624+
const exitInfo = signal ? `signal ${signal}` : `code ${code}`
625+
console.error('')
626+
console.error(`❌ ${packageName} exited immediately (${exitInfo})`)
627+
console.error('')
628+
629+
if (isIllegalInstruction) {
630+
console.error('Your CPU may not support the required instruction set (AVX2).')
631+
console.error('This typically affects CPUs from before 2013.')
632+
console.error('Unfortunately, this binary is not compatible with your system.')
633+
console.error('')
634+
} else if (isAccessViolation) {
635+
console.error('The binary crashed with an access violation.')
636+
console.error('')
637+
} else if (isBusError) {
638+
console.error('The binary crashed with a bus error.')
639+
console.error('This may indicate a platform compatibility issue.')
640+
console.error('')
641+
} else if (isAbort) {
642+
console.error('The binary crashed with an abort signal.')
643+
console.error('')
644+
}
645+
646+
console.error('System info:')
647+
console.error(` Platform: ${process.platform} ${process.arch}`)
648+
console.error(` Node: ${process.version}`)
649+
console.error(` Binary: ${CONFIG.binaryPath}`)
650+
console.error('')
651+
console.error('Please report this issue at:')
652+
console.error(' https://github.com/CodebuffAI/codebuff/issues')
653+
console.error('')
654+
}
655+
607656
async function main() {
608657
await ensureBinaryExists()
609658

@@ -613,6 +662,7 @@ async function main() {
613662

614663
const exitListener = (code, signal) => {
615664
resetTerminal()
665+
printCrashDiagnostics(code, signal)
616666
process.exit(signal ? 1 : (code || 0))
617667
}
618668

freebuff/cli/release/index.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ async function checkForUpdates(runningProcess, exitListener) {
576576

577577
newChild.on('exit', (code, signal) => {
578578
resetTerminal()
579+
printCrashDiagnostics(code, signal)
579580
process.exit(signal ? 1 : (code || 0))
580581
})
581582

@@ -591,6 +592,54 @@ async function checkForUpdates(runningProcess, exitListener) {
591592
}
592593
}
593594

595+
function printCrashDiagnostics(code, signal) {
596+
// Windows NTSTATUS codes (unsigned DWORD)
597+
const unsignedCode = code != null && code < 0 ? (code >>> 0) : code
598+
const isIllegalInstruction =
599+
signal === 'SIGILL' ||
600+
(process.platform === 'win32' && unsignedCode === 0xC000001D)
601+
const isAccessViolation =
602+
signal === 'SIGSEGV' ||
603+
(process.platform === 'win32' && unsignedCode === 0xC0000005)
604+
const isBusError = signal === 'SIGBUS'
605+
const isAbort =
606+
signal === 'SIGABRT' ||
607+
(process.platform === 'win32' && unsignedCode === 0xC0000409)
608+
609+
if (!isIllegalInstruction && !isAccessViolation && !isBusError && !isAbort) return
610+
611+
const exitInfo = signal ? `signal ${signal}` : `code ${code}`
612+
console.error('')
613+
console.error(`❌ ${packageName} exited immediately (${exitInfo})`)
614+
console.error('')
615+
616+
if (isIllegalInstruction) {
617+
console.error('Your CPU may not support the required instruction set (AVX2).')
618+
console.error('This typically affects CPUs from before 2013.')
619+
console.error('Unfortunately, this binary is not compatible with your system.')
620+
console.error('')
621+
} else if (isAccessViolation) {
622+
console.error('The binary crashed with an access violation.')
623+
console.error('')
624+
} else if (isBusError) {
625+
console.error('The binary crashed with a bus error.')
626+
console.error('This may indicate a platform compatibility issue.')
627+
console.error('')
628+
} else if (isAbort) {
629+
console.error('The binary crashed with an abort signal.')
630+
console.error('')
631+
}
632+
633+
console.error('System info:')
634+
console.error(` Platform: ${process.platform} ${process.arch}`)
635+
console.error(` Node: ${process.version}`)
636+
console.error(` Binary: ${CONFIG.binaryPath}`)
637+
console.error('')
638+
console.error('Please report this issue at:')
639+
console.error(' https://github.com/CodebuffAI/codebuff/issues')
640+
console.error('')
641+
}
642+
594643
async function main() {
595644
await ensureBinaryExists()
596645

@@ -600,6 +649,7 @@ async function main() {
600649

601650
const exitListener = (code, signal) => {
602651
resetTerminal()
652+
printCrashDiagnostics(code, signal)
603653
process.exit(signal ? 1 : (code || 0))
604654
}
605655

0 commit comments

Comments
 (0)