diff --git a/README.md b/README.md index 362b8c5..f8cab2a 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # WebDBG A React Static Web App (SWA) frontend with a containerized Javascript API backend that injests `.dmp` files and returns analyzed text. +## Analysis +By default `!analyze -v` is executed against each dump and the results are returned. Advanced post-processing can be configured in `/api/post-process.js` in `bugcheckCommands`. + +Specify any Bugcheck you want to act up and define the comamnd that should be run against that dump. This app is limited to one additional command per dump, additional commands would require additional logic. Please open an issue if this is required. + ## Public Site This project is hosted publicly on a best effort basis at https://webdbg.rtech.support as a service by the [r/Techsupport Discord Server](https://rtech.support/discord). diff --git a/api/analyze.js b/api/analyze.js index b2b14af..4e80ea9 100644 --- a/api/analyze.js +++ b/api/analyze.js @@ -2,6 +2,7 @@ import fs from 'fs'; import path from 'path'; import { exec } from 'child_process'; import winston from 'winston'; +import postProcessResults from './post-process.js'; // Corrected import path // Configure Winston logger const logger = winston.createLogger({ @@ -16,11 +17,13 @@ const logger = winston.createLogger({ ] }); +// Define the parser +const parser = 'cdb.exe'; + // Run the debugger over the dmp file and report errors should failure occur const processDmpObject = (dmp) => { return new Promise((resolve) => { logger.info(`Analysis started on ${dmp}`) - const parser = 'cdb.exe'; const command = `-z ${dmp} -c "k; !analyze -v ; q"`; exec(`${parser} ${command}`, (error, stdout, stderr) => { @@ -37,7 +40,7 @@ const processDmpObject = (dmp) => { }; // Split the raw content provided by processDmpObject -const processResult = (rawContent) => { +const processResult = (dmp, rawContent) => { // Splitting the content let splits = rawContent.split('------------------'); splits = splits.flatMap(split => split.split('STACK_TEXT:')); @@ -63,11 +66,11 @@ const processResult = (rawContent) => { const argMatches = analysis.match(/Arg\d: ([0-9a-fA-Fx]+)/g); const args = argMatches ? argMatches.map(arg => arg.split(': ')[1]) : []; - logger.info(`Bugcheck: ${bugcheck}`) - logger.info(`Args: ${args}`) + logger.info(`Bugcheck: ${bugcheck}, Args: ${args}`); // Output object creation const output = { + dmp: dmp, // Include the dmp file path dmpInfo: dmpInfo, analysis: analysis, bugcheck: bugcheck, @@ -86,20 +89,23 @@ const Analyze = async (target) => { if (!statPath.isDirectory()) { const dmp = path.resolve(target); const result = await processDmpObject(dmp); - const processedResult = processResult(result); + const processedResult = processResult(dmp, result); dmpArray.push(processedResult); } else { // Run a job for every dmp file found, this drastically reduces processing time const files = fs.readdirSync(target).filter(file => file.endsWith('.dmp')); const promises = files.map(async (file) => { const dmp = path.resolve(target, file); const result = await processDmpObject(dmp); - return processResult(result); + return processResult(dmp, result); }); const results = await Promise.all(promises); dmpArray.push(...results); } - return JSON.stringify(dmpArray); + // Call the postProcessResults function with the parser + const postProcessedResults = await postProcessResults(dmpArray, parser); + + return JSON.stringify(postProcessedResults); }; export default Analyze; \ No newline at end of file diff --git a/api/post-process.js b/api/post-process.js new file mode 100644 index 0000000..83109a7 --- /dev/null +++ b/api/post-process.js @@ -0,0 +1,65 @@ +import { exec } from 'child_process'; +import winston from 'winston'; + +// Configure Winston logger +const logger = winston.createLogger({ + level: 'info', + format: winston.format.combine( + winston.format.timestamp(), + winston.format.json() + ), + transports: [ + new winston.transports.Console(), + new winston.transports.File({ filename: 'post-process.log' }) + ] +}); + +// Configuration object for bugcheck commands +const bugcheckCommands = { + '9f': (parser, dmp, args) => `${parser} -z ${dmp} -c "k; !devstack ${args[1]} ; q"`, + // Add more bugcheck commands here as needed + // '': (dmp, args) => `${parser} -z ${dmp} -c "k; ; q"`, + // Args can be used in a command ${args[#]} + // Arg counts start at 0 so "Arg1" is ${args[0]} +}; + +// Function to execute a command and return a promise +const executeCommand = (command) => { + return new Promise((resolve, reject) => { + exec(command, (error, stdout, stderr) => { + if (error) { + reject(error); + } else if (stderr) { + resolve(`Warnings: ${stderr}`); + } else { + resolve(stdout); + } + }); + }); +}; + +// Function to perform additional operations on the analysis results +const postProcessResults = async (results, parser) => { + for (const result of results) { + const commandGenerator = bugcheckCommands[result.bugcheck]; + if (commandGenerator) { + const command = commandGenerator(parser, result.dmp, result.args); + logger.info(`Executing command: ${command}`); + try { + const output = await executeCommand(command); + result.post = output; + logger.info('Post-process completed'); + } catch (error) { + result.post = error; + logger.error(`An error occured while post-processing the file: ${error}`); + } + } else { + result.post = "No post processing configured for this bugcheck" // Add a null post key if no command is run + logger.info(`No command for bugcheck: ${result.bugcheck}`); + } + } + + return results; +}; + +export default postProcessResults; \ No newline at end of file diff --git a/swa/src/App.js b/swa/src/App.js index b0ce480..ae1c730 100644 --- a/swa/src/App.js +++ b/swa/src/App.js @@ -116,6 +116,7 @@ const FileUpload = () => { const order = [ "dmpInfo", "analysis", + "post", "rawContent", ]; const specialKeys = ["rawContent"]; diff --git a/swa/src/style.css b/swa/src/style.css index 84de22b..51ec8af 100644 --- a/swa/src/style.css +++ b/swa/src/style.css @@ -56,6 +56,14 @@ body { .result-header.analysis { visibility: hidden; } + +.result-header.post::before { + content: "Post Processing"; + visibility: visible; +} +.result-header.post { + visibility: hidden; +} .result-header.dmpInfo::before { content: "Dump Info"; visibility: visible;