UNPKG

@specs-feup/clava

Version:

A C/C++ source-to-source compiler written in Typescript

280 lines (225 loc) 6.95 kB
import Io from "@specs-feup/lara/api/lara/Io.js"; import Strings from "@specs-feup/lara/api/lara/Strings.js"; import JavaTypes, { JavaClasses, } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; import Query from "@specs-feup/lara/api/weaver/Query.js"; import { FileJp, FunctionJp } from "../../Joinpoints.js"; import Clava from "../Clava.js"; import CMaker from "../cmake/CMaker.js"; import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; function GproferGetCxxFunction(signature: string) { return Query.search(FunctionJp, { signature: signature, hasDefinition: true, }).first(); } function GproferGetCFunction(signature: string) { return Query.search(FunctionJp, { name: signature, hasDefinition: true, }).first(); } interface GproferProfileFileContents { data: Record<string, Record<string, number>>; hotspots: Record<number, string>; } export default class Gprofer { private _runs: number; private _args: string[]; private _app = Clava.getProgram(); private _workingDir: JavaClasses.File | undefined = undefined; private _deleteWorkingDir: boolean = false; private _checkReturn: boolean = true; private _data: Record<string, Record<string, number>> = {}; private _hotSpots: Record<number, string> = {}; private _cmaker: CMaker = this.defaultCmaker(); private _gProfer = JavaTypes.Gprofer; constructor(runs: number = 1, args: string[] = []) { this._runs = runs; this._args = args; } getCmaker(): CMaker { return this._cmaker; } static _EXE_NAME = "gprofer_bin"; protected defaultCmaker(): CMaker { const cmaker = new CMaker(Gprofer._EXE_NAME, false); if (Clava.isCxx()) { cmaker.addCxxFlags("-no-pie", "-pg"); } else { cmaker.addFlags("-no-pie", "-pg"); } // sources for (const $jp of this._app.getDescendants("file")) { const $file = $jp as FileJp; if ($file.isHeader) { continue; } cmaker.getSources().addSource($file.filepath); } // includes for (const obj of Clava.getIncludeFolders()) { const userInclude = obj as string; debug("Adding include: " + userInclude); cmaker.addIncludeFolder(userInclude); } return cmaker; } protected static _buildName($function: FunctionJp): string { if (Clava.isCxx()) { /* signature or qualified name */ return $function.signature; } return $function.name; } setArgs(args: string[]) { this._args = args; return this; } setRuns(runs: number) { this._runs = runs; return this; } setCheckReturn(checkReturn: boolean) { this._checkReturn = checkReturn; return this; } setWorkingDir(workingDir: string, deleteWorkingDir: boolean) { this._workingDir = Io.getPath(workingDir); this._deleteWorkingDir = deleteWorkingDir; return this; } profile() { if (this._workingDir === undefined) { this._workingDir = Io.getTempFolder("gprofer_" + Strings.uuid()); this._deleteWorkingDir = true; } // compile the application const binary = this._cmaker.build( this._workingDir, Io.getPath(this._workingDir, "build") ); // call java gprofer const data = this._gProfer.profile( binary, this._args, this._runs, this._workingDir, this._deleteWorkingDir, this._checkReturn ); const json = this._gProfer.getJsonData(data) as string; // fill this._data and this._hotSpots const obj = JSON.parse(json); this._hotSpots = obj.hotspots; this._data = obj.table; return this; } getHotspotNames() { return this._hotSpots; } writeProfile(path: JavaClasses.File = Io.getPath("./gprofer.json")) { const profile: GproferProfileFileContents = { data: this._data, hotspots: this._hotSpots, }; Io.writeJson(path.getAbsolutePath(), profile); } readProfile(path: JavaClasses.File = Io.getPath("./gprofer.json")) { const obj = Io.readJson( path.getAbsolutePath() ) as GproferProfileFileContents; this._data = obj.data; this._hotSpots = obj.hotspots; } /** * * May return undefined if the desired function is a system or library function and not available in the source code. * */ getHotspot(rank: number = 0) { const _rank = rank; const signature = this._hotSpots[_rank]; const f = this._getHotspot(signature); if (f === undefined) { console.log( `Could not find hotspot with rank ${rank} and signature ${signature}. It may be a system function.\n` ); } return f; } /** * Internal method that uses the signature to identify a function. * */ protected _getHotspot(signature: string): FunctionJp | undefined { let f = undefined; if (Clava.isCxx()) { debug("finding Cxx function with signature " + signature); f = GproferGetCxxFunction(signature); } else { debug("finding C function with signature " + signature); f = GproferGetCFunction(signature); } return f; } getPercentage($function: FunctionJp) { return this.get("percentage", $function); } getCalls($function: FunctionJp) { return this.get("calls", $function); } getSelfSeconds($function: FunctionJp) { return this.get("selfSeconds", $function); } getSelfMsCall($function: FunctionJp) { return this.get("selfMsCall", $function); } getTotalMsCall($function: FunctionJp) { return this.get("totalMsCall", $function); } protected get(type: string, $function: FunctionJp) { const name = Gprofer._buildName($function); return this._data[name][type]; } print($function: FunctionJp) { const perc = this.getPercentage($function); const calls = this.getCalls($function); const self = this.getSelfSeconds($function); const selfCall = this.getSelfMsCall($function); const totalCall = this.getTotalMsCall($function); console.log($function.signature); if (perc !== undefined) { console.log(`\tPercentage: ${perc}%`); } if (calls !== undefined) { console.log(`\tCalls: ${calls}`); } if (self !== undefined) { console.log(`\tSelf seconds: ${self}s`); } if (selfCall !== undefined) { console.log(`\tSelf ms/call: ${selfCall}ms`); } if (totalCall !== undefined) { console.log(`\tTotal ms/call: ${totalCall}ms`); } } removeSystemFunctions() { const toRemove: number[] = []; for (const index in this._hotSpots) { const sigIndex: number = Number(index); const sig = this._hotSpots[sigIndex]; const $hs = this._getHotspot(sig); if ($hs === undefined) { // mark to remove from array toRemove.push(sigIndex); // remove from map delete this._data[sig]; } } // remove from array for (const sigIndex of toRemove) { delete this._hotSpots[sigIndex]; } } }