@specs-feup/clava
Version:
A C/C++ source-to-source compiler written in Typescript
280 lines (225 loc) • 6.95 kB
text/typescript
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];
}
}
}