diff --git a/src/actions/enhancedmultiFileRunner.ts b/src/actions/enhancedmultiFileRunner.ts index a3dbe12..5a98caa 100644 --- a/src/actions/enhancedmultiFileRunner.ts +++ b/src/actions/enhancedmultiFileRunner.ts @@ -10,157 +10,177 @@ import { performance } from 'perf_hooks'; const docker = new Docker(); const createFilesTar = async (files: { name: string; content: string }[]) => { - const pack = tar.pack(); + const pack = tar.pack(); - files.forEach(file => { - if (file.name.includes('..') || path.isAbsolute(file.name)) { - throw new Error(`非法文件路径: ${file.name}`); - } - if (!/\.(c|java|py|txt)$/i.test(file.name)) { - throw new Error(`禁止的文件类型: ${file.name}`); - } - pack.entry({ name: file.name }, file.content); - }); + files.forEach(file => { + if (file.name.includes('..') || path.isAbsolute(file.name)) { + throw new Error(`非法文件路径: ${file.name}`); + } + if (!/\.(c|java|py|txt)$/i.test(file.name)) { + throw new Error(`禁止的文件类型: ${file.name}`); + } + pack.entry({ name: file.name }, file.content); + }); - pack.finalize(); - return Readable.from(pack) as unknown as NodeJS.ReadableStream; + pack.finalize(); + return Readable.from(pack) as unknown as NodeJS.ReadableStream; }; const readContainerFile = async (container: Docker.Container, filePath: string) => { - try { - const archive = await container.getArchive({ path: filePath }); - const extract = tar.extract(); - let fileContent = ''; + try { + const archive = await container.getArchive({ path: filePath }); + const extract = tar.extract(); + let fileContent = ''; - extract.on('entry', (header, stream, next) => { - stream.on('data', (chunk) => { - fileContent += chunk.toString(); - }); - stream.on('end', next); - stream.resume(); - }); + extract.on('entry', (header, stream, next) => { + stream.on('data', (chunk) => { + fileContent += chunk.toString(); + }); + stream.on('end', next); + stream.resume(); + }); - return new Promise((resolve, reject) => { - extract.on('finish', () => resolve(fileContent)); - extract.on('error', reject); - archive.pipe(extract); - }); - } catch (error) { - return null; - } + return new Promise((resolve, reject) => { + extract.on('finish', () => resolve(fileContent)); + extract.on('error', reject); + archive.pipe(extract); + }); + } catch (error) { + return null; + } }; export const runMultiFileCodeWithOptionalTestData = async (params: { - files: { name: string; content: string }[]; - language: string; - testDataFiles?: string[]; - expectedAnswerFiles?: string[]; + files: { name: string; content: string }[]; + language: string; + testDataFiles?: string[]; + expectedAnswerFiles?: string[]; }) => { - const totalStart = performance.now(); - let stageStart: number; - const { files, language } = params; - let container: Docker.Container | null = null; + const totalStart = performance.now(); + let stageStart: number; + const { files, language } = params; + let container: Docker.Container | null = null; - try { - // 阶段1: 参数校验 - stageStart = performance.now(); - if (!files?.length || !language) { - throw new Error('必须提供代码文件和语言类型'); + try { + // 阶段1: 参数校验 + stageStart = performance.now(); + if (!files?.length || !language) { + throw new Error('必须提供代码文件和语言类型'); + } + const supportedLanguages = ['c', 'java', 'python']; + if (!supportedLanguages.includes(language)) { + throw new Error(`不支持的语言类型: ${language}`); + } + console.log(`[PERF] 参数校验耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); + + // 阶段2: 准备编译命令 + stageStart = performance.now(); + const compileAndRunCmd = { + c: `cd ${dockerConfig.WorkingDir} && + start_time=$(date +%s%N) && + gcc *.c -o main && + compile_time=$(($(date +%s%N) - $start_time)) && + echo "编译时间: $((compile_time / 1000000))ms" && + start_time=$(date +%s%N) && + ./main && + run_time=$(($(date +%s%N) - $start_time)) && + echo "运行时间: $((run_time / 1000000))ms"`, + java: `cd ${dockerConfig.WorkingDir} && + start_time=$(date +%s%N) && + javac *.java && + compile_time=$(($(date +%s%N) - $start_time)) && + echo "编译时间: $((compile_time / 1000000))ms" && + start_time=$(date +%s%N) && + java Main && + run_time=$(($(date +%s%N) - $start_time)) && + echo "运行时间: $((run_time / 1000000))ms"`, + python: `cd ${dockerConfig.WorkingDir} && + start_time=$(date +%s%N) && + python3 script.py && + run_time=$(($(date +%s%N) - $start_time)) && + echo "运行时间: $((run_time / 1000000))ms"` + }[language]; + console.log(`[PERF] 命令准备耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); + + // 阶段3: 创建容器 + stageStart = performance.now(); + container = await docker.createContainer({ + Image: dockerConfig.Image, + Cmd: ['sh', '-c', compileAndRunCmd], + WorkingDir: dockerConfig.WorkingDir, + HostConfig: { + AutoRemove: false, + Memory: 256 * 1024 * 1024, + NetworkMode: 'none', + Binds: [`${dockerConfig.WorkingDir}:/workspace`] + } + }); + console.log(`[PERF] 容器创建耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); + + // 阶段4: 文件上传 + stageStart = performance.now(); + const tarStream = await createFilesTar(files); + await container.putArchive(tarStream, { + path: dockerConfig.WorkingDir + }); + console.log(`[PERF] 文件上传耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); + + // 阶段5: 执行容器 + stageStart = performance.now(); + await container.start(); + let output = ''; + const logs = await container.logs({ + stdout: true, + stderr: true, + follow: true + }); + + const logStream = new Writable({ + write(chunk, _, callback) { + output += chunk.toString() + .replace(/[\x00-\x1F\x7F]/g, '') // Remove non-printable characters + .trim(); + callback(); + } + }); + + logs.pipe(logStream); + await new Promise(resolve => logStream.on('finish', resolve)); + await container.wait(); + console.log(`[PERF] 容器执行耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); + + // 阶段6: 读取结果 + stageStart = performance.now(); + const resultFilePath = path.posix.join( + dockerConfig.WorkingDir, + dockerConfig.ResultOutputFile + ); + const resultContent = await readContainerFile(container, resultFilePath); + console.log(`[PERF] 结果读取耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); + + return resultContent + ? { output, resultFileContent: resultContent } + : { output }; + + } catch (error) { + console.error('执行流程错误:', error); + return { + error: `执行错误: ${(error as Error).message}` + }; + } finally { + // 阶段7: 清理容器 + stageStart = performance.now(); + if (container) { + try { + await container.remove({ force: true }); + } catch (error) { + console.error('容器清理失败:', error); + } + } + console.log(`[PERF] 容器清理耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); + + // 总耗时统计 + const totalTime = performance.now() - totalStart; + console.log(`[PERF] 总运行时间: ${totalTime.toFixed(2)}ms`); } - const supportedLanguages = ['c', 'java', 'python']; - if (!supportedLanguages.includes(language)) { - throw new Error(`不支持的语言类型: ${language}`); - } - console.log(`[PERF] 参数校验耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); - - // 阶段2: 准备编译命令 - stageStart = performance.now(); - const compileAndRunCmd = { - c: `cd ${dockerConfig.WorkingDir} && gcc *.c -o main && ./main`, - java: `cd ${dockerConfig.WorkingDir} && javac *.java && java Main`, - python: `cd ${dockerConfig.WorkingDir} && python3 script.py` - }[language]; - console.log(`[PERF] 命令准备耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); - - // 阶段3: 创建容器 - stageStart = performance.now(); - container = await docker.createContainer({ - Image: dockerConfig.Image, - Cmd: ['sh', '-c', compileAndRunCmd], - WorkingDir: dockerConfig.WorkingDir, - HostConfig: { - AutoRemove: false, - Memory: 256 * 1024 * 1024, - NetworkMode: 'none', - Binds: [`${dockerConfig.WorkingDir}:/workspace`] - } - }); - console.log(`[PERF] 容器创建耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); - - // 阶段4: 文件上传 - stageStart = performance.now(); - const tarStream = await createFilesTar(files); - await container.putArchive(tarStream, { - path: dockerConfig.WorkingDir - }); - console.log(`[PERF] 文件上传耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); - - // 阶段5: 执行容器 - stageStart = performance.now(); - await container.start(); - let output = ''; - const logs = await container.logs({ - stdout: true, - stderr: true, - follow: true - }); - - const logStream = new Writable({ - write(chunk, _, callback) { - output += chunk.toString() - .replace(/[\x00-\x1F\x7F]/g, '') - .trim(); - callback(); - } - }); - - logs.pipe(logStream); - await new Promise(resolve => logStream.on('finish', resolve)); - await container.wait(); - console.log(`[PERF] 容器执行耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); - - // 阶段6: 读取结果 - stageStart = performance.now(); - const resultFilePath = path.posix.join( - dockerConfig.WorkingDir, - dockerConfig.ResultOutputFile - ); - const resultContent = await readContainerFile(container, resultFilePath); - console.log(`[PERF] 结果读取耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); - - return resultContent - ? { output, resultFileContent: resultContent } - : { output }; - - } catch (error) { - console.error('执行流程错误:', error); - return { - error: `执行错误: ${(error as Error).message}` - }; - } finally { - // 阶段7: 清理容器 - stageStart = performance.now(); - if (container) { - try { - await container.remove({ force: true }); - } catch (error) { - console.error('容器清理失败:', error); - } - } - console.log(`[PERF] 容器清理耗时: ${(performance.now() - stageStart).toFixed(2)}ms`); - - // 总耗时统计 - const totalTime = performance.now() - totalStart; - console.log(`[PERF] 总运行时间: ${totalTime.toFixed(2)}ms`); - } };