Skip to content

Instantly share code, notes, and snippets.

@dudeuter
Last active January 19, 2024 20:42
Show Gist options
  • Save dudeuter/632fc9018b05b2b3c1d4dbd3a2498f54 to your computer and use it in GitHub Desktop.
Save dudeuter/632fc9018b05b2b3c1d4dbd3a2498f54 to your computer and use it in GitHub Desktop.
symbolicate tombstone threads using ndk-stack
const fs = require('fs/promises');
const process = require('process');
const { exec } = require('child_process');
const [SYMBOLS_DIR, TOMBSTONE_FILE_NAME] = process.argv.slice(2);
const FILE_OPTIONS = { encoding: 'utf-8' };
const TOMBSTONE_THREAD_DELIMITER = '--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n';
const NDK_STACK_DELIMITER = '*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n';
function run(command) {
return new Promise((resolve, reject) => {
exec(command, (error, stdout) => {
if (error) {
reject(error);
return;
}
resolve(stdout);
})
})
}
async function resolveNdkStack() {
try {
return await run(`which ndk-stack`);
} catch (e) {
return (await run(`find ~/Library/Android -type f -name ndk-stack | head -n1`)).trim();
}
}
void async function () { // MAIN
const ndkStackCommand = await resolveNdkStack();
const tombstone = await fs.readFile(TOMBSTONE_FILE_NAME, FILE_OPTIONS);
const threads = tombstone.split(TOMBSTONE_THREAD_DELIMITER).slice(1); // first index is the main stacktrace and memory table
for (const thread of threads) {
const threadInfo = Object.fromEntries(
thread
.slice(0, thread.indexOf('>>>'))
.trim()
.split(', ')
.map(e => e.split(': '))
);
const filename = `name-${threadInfo.name}_pid-${threadInfo.pid}_tid-${threadInfo.tid}`;
await fs.writeFile(`${filename}.log`, NDK_STACK_DELIMITER + thread, FILE_OPTIONS);
await run(`${ndkStackCommand} --sym ${SYMBOLS_DIR} --dump ${filename.replace(' ', '\\ ')}.log > ${filename.replace(' ', '\\ ')}.symbolized.log`);
}
}() // END MAIN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment