From ac301ad263f79d73b66228d804afe22feffbf927 Mon Sep 17 00:00:00 2001 From: "FANG.Ge" Date: Tue, 17 May 2022 01:52:26 +0800 Subject: [PATCH] support gcov files that use relative-path info Signed-off-by: FANG.Ge --- README.md | 5 ++-- package.json | 2 +- src/coverageCache.ts | 15 ++++++++---- src/extension.ts | 54 ++++++++++++++++++++++++++++++++++---------- src/gcovInterface.ts | 5 ++-- 5 files changed, 59 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index f3447c2..51d1bf0 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,9 @@ During compilation, `gcc` will generate a `.gcno` file next to every `.o` file. This vscode extension uses the `gcov` program to parse these additional files and displays the information on top of your source files. For this to work, it has to find the generated `.gcda` for a given project. Those are usually in your build directory. If the extension does not find them on its own, you have to edit the `gcovViewer.buildDirectories` setting of your workspace folder. The most convenient way to do this is to use the `Gcov Viewer: Select Build Directory` command. ## Troubleshooting - -- Try passing `-fprofile-abs-path` to gcc. This helps the extension to match source files with their corresponding coverage data. +- Each of Methods can help the extension to match relative-path source files with their corresponding coverage data: + - Configuring `gcovViewer.buildDirectories` as build root directories correctly. + - Try passing `-fprofile-abs-path` to gcc. ## Known Issues diff --git a/package.json b/package.json index 5267f9f..a5c6dc9 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "gcov-viewer", "displayName": "Gcov Viewer", "description": "Decorate C/C++ source files with code coverage information generated by gcov.", - "version": "0.4.0", + "version": "0.5.0", "license": "MIT", "publisher": "JacquesLucke", "author": { diff --git a/src/coverageCache.ts b/src/coverageCache.ts index b3587f6..ada61d6 100644 --- a/src/coverageCache.ts +++ b/src/coverageCache.ts @@ -1,5 +1,5 @@ import { loadGcovData, GcovFileData } from './gcovInterface'; - +import { resolve } from 'path'; /** * Cache for all data loaded using gcov. This way we don't have to reload * it everytime the user looks at a new file. @@ -9,15 +9,20 @@ export class CoverageCache { demangledNames: Map = new Map(); loadedGcdaFiles: string[] = []; - async loadGcdaFiles(gcdaPaths: string[]) { - const gcovDataArray = await loadGcovData(gcdaPaths); + async loadGcdaFiles(buildDirectory: string, gcdaPaths: string[]) { + const gcovDataArray = await loadGcovData(buildDirectory, gcdaPaths); for (const gcovData of gcovDataArray) { for (const fileData of gcovData.files) { const cachedFileData = this.dataByFile.get(fileData.file); if (cachedFileData === undefined) { - this.dataByFile.set(fileData.file, { - file: fileData.file, + let realpath = fileData.file; + if (!realpath.startsWith("/")) { /* relative path to absolute path */ + realpath = resolve(buildDirectory, realpath) + } + + this.dataByFile.set(realpath, { + file: realpath, lines: [...fileData.lines], functions: [...fileData.functions], }); diff --git a/src/extension.ts b/src/extension.ts index 14ebd88..2e8bd54 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -88,13 +88,19 @@ function getBuildDirectories(): string[] { return buildDirectories; } -async function getGcdaPaths(progress?: MyProgress, token?: vscode.CancellationToken) { +class GcdaPathsGroup { + buildDirectory: string = ""; + gcdaPaths: string[] = []; +}; + +async function getGcdaPathsGroups(progress?: MyProgress, token?: vscode.CancellationToken) { progress?.report({ message: 'Searching .gcda files' }); const buildDirectories = getBuildDirectories(); let counter = 0; - const gcdaPaths: Set = new Set(); + const gcdaPathsGroups: Set = new Set(); for (const buildDirectory of buildDirectories) { + const gcdaPaths: Set = new Set(); await findAllFilesRecursively(buildDirectory, path => { if (path.endsWith('.gcda')) { gcdaPaths.add(path); @@ -102,8 +108,26 @@ async function getGcdaPaths(progress?: MyProgress, token?: vscode.CancellationTo counter++; progress?.report({ message: `[${counter}] Scanning (found ${gcdaPaths.size}): ${path}` }); }, token); + + if (gcdaPaths.size === 0) { + continue; + } + gcdaPathsGroups.add({buildDirectory : buildDirectory, gcdaPaths : Array.from(gcdaPaths)}); } + return Array.from(gcdaPathsGroups); +} + +async function getGcdaPaths(progress?: MyProgress, token?: vscode.CancellationToken) { + let gcdaPathsGroups: GcdaPathsGroup[] = await getGcdaPathsGroups(progress, token); + const gcdaPaths: Set = new Set(); + + for (const gcdaPathsGroup of gcdaPathsGroups) { + for (const path of gcdaPathsGroup.gcdaPaths) { + gcdaPaths.add(path); + } + } + return Array.from(gcdaPaths); } @@ -112,6 +136,7 @@ let coverageCache = new CoverageCache(); type MyProgress = vscode.Progress<{ message?: string; increment?: number }>; async function reloadCoverageDataFromPaths( + buildDirectory: string, paths: string[], totalPaths: number, progress: MyProgress, token: vscode.CancellationToken) { @@ -124,7 +149,7 @@ async function reloadCoverageDataFromPaths( return; } - await coverageCache.loadGcdaFiles(pathsChunk); + await coverageCache.loadGcdaFiles(buildDirectory, pathsChunk); progress.report({ increment: 100 * pathsChunk.length / totalPaths, @@ -164,22 +189,27 @@ async function reloadGcdaFiles() { coverageCache = new CoverageCache(); progress.report({ increment: 0 }); - const gcdaPaths = await getGcdaPaths(progress, token); - if (gcdaPaths.length === 0) { + const gcdaPathsGroups: GcdaPathsGroup[] = await getGcdaPathsGroups(progress, token); + if (gcdaPathsGroups.length === 0) { showNoFilesFoundMessage(); return; } - /* Shuffle paths make the processing time of the individual chunks more similar. */ - shuffleArray(gcdaPaths); - const pathChunks = splitArrayInChunks(gcdaPaths, os.cpus().length); - /* Process chunks asynchronously, so that gcov is invoked multiple times in parallel. */ const promises = []; - for (const pathChunk of pathChunks) { - promises.push(reloadCoverageDataFromPaths( - pathChunk, gcdaPaths.length, progress, token)); + + for (const gcdaPathsGroup of gcdaPathsGroups) { + /* Shuffle paths make the processing time of the individual chunks more similar. */ + shuffleArray(gcdaPathsGroup.gcdaPaths); + const pathChunks = splitArrayInChunks(gcdaPathsGroup.gcdaPaths, os.cpus().length); + + + for (const pathChunk of pathChunks) { + promises.push(reloadCoverageDataFromPaths(gcdaPathsGroup.buildDirectory, + pathChunk, gcdaPathsGroup.gcdaPaths.length, progress, token)); + } } + await Promise.all(promises); } ); diff --git a/src/gcovInterface.ts b/src/gcovInterface.ts index fc625ed..fd4ad58 100644 --- a/src/gcovInterface.ts +++ b/src/gcovInterface.ts @@ -58,7 +58,7 @@ export async function isGcovCompatible() { }); } -export async function loadGcovData(paths: string[]): Promise { +export async function loadGcovData(buildDirectory: string, paths: string[]): Promise { if (paths.length === 0) { return []; } @@ -70,7 +70,8 @@ export async function loadGcovData(paths: string[]): Promise { command += ` "${path}"`; } return new Promise((resolve, reject) => { - child_process.exec(command, { maxBuffer: 256 * 1024 * 1024 }, (err, stdout, stderr) => { + child_process.exec(command, { maxBuffer: 256 * 1024 * 1024, cwd : buildDirectory }, + (err, stdout, stderr) => { if (err) { console.error(`exec error: ${err}`); reject();