bpfcc
Version:
Frontend / bindings for BPF Compiler Collection (BCC)
414 lines • 14.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BPFModule = exports.load = exports.loadSync = exports.defaultOptions = exports.ProbeAttachType = void 0;
const util_1 = require("util");
const bpf_1 = require("bpf");
const exception_1 = require("./exception");
var exception_2 = require("./exception");
Object.defineProperty(exports, "Code", { enumerable: true, get: function () { return exception_2.Code; } });
Object.defineProperty(exports, "BCCError", { enumerable: true, get: function () { return exception_2.BCCError; } });
const native = require('../build/Release/bpfcc_binding');
var ProbeAttachType;
(function (ProbeAttachType) {
ProbeAttachType[ProbeAttachType["ENTRY"] = 0] = "ENTRY";
ProbeAttachType[ProbeAttachType["RETURN"] = 1] = "RETURN";
})(ProbeAttachType = exports.ProbeAttachType || (exports.ProbeAttachType = {}));
exports.defaultOptions = {
flags: 0,
rwEngineEnabled: native.rwEngineEnabled,
mapsNamespace: "",
allowRlimit: true,
cflags: [],
usdt: [],
autoload: true,
};
const initSync = native.BPF.prototype.initSync;
const initAsync = util_1.promisify(native.BPF.prototype.initAsync); // FIXME: does it work with 'this' bound?
function genericLoad(func, sync, program, options_) {
const options = { ...exports.defaultOptions, ...options_, program };
const bpf_ = new native.BPF();
const r = func.call(bpf_, options);
return sync ? post(bpf_) : r.then(() => post(bpf_));
function post(bpf_) {
const bpf = new BPFModule(bpf_);
if (options.autoload)
bpf.autoload();
return bpf;
}
}
/**
* Compile a program and load it into the kernel.
*
* **Note:** This is a heavy operation, use [[load]]
* to avoid blocking the event loop.
*
* @param program C code to compile
* @param options Additional options
* @returns Loaded program instance
*/
function loadSync(program, options) {
return genericLoad(initSync, true, program, options);
}
exports.loadSync = loadSync;
/**
* Compile a program and load it into the kernel.
*
* @param program C code to compile
* @param options Additional options
* @returns Promise for loaded program instance
*/
function load(program, options) {
return genericLoad(initAsync, false, program, options);
}
exports.load = load;
function ksymname(name) {
return; // TODO
}
function ksym(addr) {
return; // TODO
}
class BPFModule {
constructor(_bpf) {
this._bpf = _bpf;
}
/**
* Detach all functions & events.
*
* @category Event attaching
*/
detachAll() {
return exception_1.checkStatus(this._bpf.detachAll());
}
/**
* @category Event attaching
*/
attachKprobe(kernelFunc, probeFunc, options) {
options = options || {};
return exception_1.checkStatus(this._bpf.attachKprobe(kernelFunc, probeFunc, options.kernelFuncOffset, options.attachType, options.maxActive));
}
/**
* @category Event attaching
*/
detachKprobe(kernelFunc, options) {
options = options || {};
return exception_1.checkStatus(this._bpf.detachKprobe(kernelFunc, options.attachType));
}
/**
* @category Event attaching
*/
attachUprobe(binaryPath, symbol, probeFunc, options) {
options = options || {};
return exception_1.checkStatus(this._bpf.attachUprobe(binaryPath, symbol, probeFunc, options.symbolAddr, options.attachType, options.pid, options.symbolOffset));
}
/**
* @category Event attaching
*/
detachUprobe(binaryPath, symbol, options) {
options = options || {};
return exception_1.checkStatus(this._bpf.detachUprobe(binaryPath, symbol, options.symbolAddr, options.attachType, options.pid, options.symbolOffset));
}
/**
* Convenience method, see [[attachKprobe]].
*
* @category Event attaching
*/
attachKretprobe(kernelFunc, probeFunc, options) {
return this.attachKprobe(kernelFunc, probeFunc, { ...options, attachType: ProbeAttachType.RETURN });
}
/**
* Convenience method, see [[detachKprobe]].
*
* @category Event attaching
*/
detachKretprobe(kernelFunc) {
return this.detachKprobe(kernelFunc, { attachType: ProbeAttachType.RETURN });
}
/**
* Convenience method, see [[attachUprobe]].
*
* @category Event attaching
*/
attachUretprobe(binaryPath, symbol, probeFunc, options) {
return this.attachUprobe(binaryPath, symbol, probeFunc, { ...options, attachType: ProbeAttachType.RETURN });
}
/**
* Convenience method, see [[detachUprobe]].
*
* @category Event attaching
*/
detachUretprobe(binaryPath, symbol, options) {
return this.detachUprobe(binaryPath, symbol, { ...options, attachType: ProbeAttachType.RETURN });
}
/**
* @category Event attaching
*/
attachUsdt(usdt, options) {
options = options || {};
return exception_1.checkStatus(this._bpf.attachUsdt(usdt, options.pid));
}
/**
* @category Event attaching
*/
// attachUsdtAll() {
// return checkStatus(this._bpf.attachUsdtAll())
// }
/**
* @category Event attaching
*/
detachUsdt(usdt, options) {
options = options || {};
return exception_1.checkStatus(this._bpf.detachUsdt(usdt, options.pid));
}
/**
* @category Event attaching
*/
// detachUsdtAll() {
// return checkStatus(this._bpf.detachUsdtAll())
// }
/**
* @category Event attaching
*/
attachTracepoint(tracepoint, probeFunc) {
return exception_1.checkStatus(this._bpf.attachTracepoint(tracepoint, probeFunc));
}
/**
* @category Event attaching
*/
detachTracepoint(tracepoint) {
return exception_1.checkStatus(this._bpf.detachTracepoint(tracepoint));
}
/**
* @category Event attaching
*/
// attachRawTracepoint(tracepoint: string, probeFunc: string) {
// return checkStatus(this._bpf.attachRawTracepoint(tracepoint, probeFunc))
// }
/**
* @category Event attaching
*/
// detachRawTracepoint(tracepoint: string) {
// return checkStatus(this._bpf.detachRawTracepoint(tracepoint))
// }
/**
* @category Event attaching
*/
attachPerfEvent(evType, evConfig, probeFunc, samplePeriod, sampleFreq, options) {
options = options || {};
return exception_1.checkStatus(this._bpf.attachPerfEvent(evType, evConfig, probeFunc, samplePeriod, sampleFreq, options.pid, options.cpu, options.groupFd));
}
/**
* @category Event attaching
*/
detachPerfEvent(evType, evConfig) {
return exception_1.checkStatus(this._bpf.detachPerfEvent(evType, evConfig));
}
getSyscallFnName(name) {
return this._bpf.getSyscallFnName(name);
}
addModule(module) {
if (!this._bpf.addModule(module))
throw Error("Couldn't add module");
}
openPerfEvent(name, type, config) {
return exception_1.checkStatus(this._bpf.openPerfEvent(name, type, config));
}
closePerfEvent(name) {
return exception_1.checkStatus(this._bpf.closePerfEvent(name));
}
loadFunction(funcName, type) {
const [status, fd] = this._bpf.loadFunction(funcName, type);
exception_1.checkStatus(status);
return fd;
}
unloadFunction(funcName) {
return exception_1.checkStatus(this._bpf.unloadFunction(funcName));
}
// attachFunction(programFd: FD, attachableFd: FD, attachType: AttachType, flags: bigint) {
// return checkStatus(this._bpf.attachFunction(programFd, attachableFd, attachType, flags))
// }
// detachFunction(programFd: FD, attachableFd: FD, attachType: AttachType) {
// return checkStatus(this._bpf.detachFunction(programFd, attachableFd, attachType))
// }
freeBccMemory() {
// FIXME: better error checking?
if (this._bpf.freeBccMemory())
throw Error("Couldn't free memory");
}
/**
* Retrieves all registered eBPF maps on this program
* and their information, as a `(path, tableDesc)` dictionary.
* See [[TableDesc]].
*/
get maps() {
return new Map(this._bpf.getMaps());
}
/**
* Find the information of a map by name.
* Returns undefined if the map is not found.
*
* @param name Map name
* @category Module info
*/
findMap(name) {
return this._bpf.findMap(name);
}
/**
* Retrieves all declared functions
*/
get functions() {
return this._bpf.getFunctions();
}
/**
* Automatically load and attach functions beginning with
* special prefixes (`kprobe__`, `tracepoint__`, etc.).
*
* By default, this is automatically called by [[load]] or [[loadSync]].
*/
autoload() {
// Code adapted from the Python frontend //
const syscallPrefixes = [
'sys_',
'__x64_sys_',
'__x32_compat_sys_',
'__ia32_compat_sys_',
'__arm64_sys_',
'__s390x_sys_',
'__s390_sys_',
];
// Find current system's syscall prefix by testing on the BPF syscall.
// If no valid value found, will return the first possible value which
// would probably lead to error in later API calls.
function getSyscallPrefix() {
for (const prefix of syscallPrefixes) {
if (ksymname(prefix + 'bpf') !== undefined)
return prefix;
}
return syscallPrefixes[0];
}
// Given a Kernel function name that represents a syscall but already has a
// prefix included, transform it to current system's prefix. For example,
// if "sys_clone" provided, the helper may translate it to "__x64_sys_clone".
function fixSyscallFnname(x) {
for (const prefix of syscallPrefixes) {
if (x.startsWith(prefix))
return getSyscallPrefix() + x.substr(0, prefix.length);
}
return x;
}
for (const fn of this.functions) {
const m = /^(\w+)__(.+)$/.exec(fn);
if (!m)
continue;
const [, prefix, name] = m;
const prefixes = {
kprobe: () => this.attachKprobe(fixSyscallFnname(name), fn),
kretprobe: () => this.attachKretprobe(fixSyscallFnname(name), fn),
tracepoint: () => this.attachTracepoint(name.replace(/__/g, ':'), fn),
};
if (Object.hasOwnProperty.call(prefixes, prefix))
prefixes[prefix]();
}
}
// MAP ACCESS
/**
* Creates and returns a custom
* [MapRef](https://bpf.alba.sh/docs/interfaces/mapref.html)
* reference to the given map.
*
* The reference doesn't support closing the FD, and
* keeps the full BPF program alive for convenience.
*
* @param name Map name
* @category Map access
*/
getMapRef(name) {
const desc = this.findMap(name);
if (desc === undefined)
throw Error(`No map named ${name} found`);
// Use createMapRef to get info if available
const ref = bpf_1.createMapRef(desc.fd, { parameters: desc });
ref.close();
// Make ref hold us alive, since we own the FD
return Object.freeze({
...ref, fd: desc.fd, bpf: this,
close() { throw Error('BCC refs do not support closing; use detachAll() to unload the program'); }
});
}
/**
* Creates and returns a
* [RawMap](https://bpf.alba.sh/docs/classes/rawmap.html)
* instance to manipulate the given map.
*
* @param name Map name
* @category Map access
*/
getRawMap(name) {
return new bpf_1.RawMap(this.getMapRef(name));
}
/**
* Creates and returns a generic
* [IMap](https://bpf.alba.sh/docs/interfaces/imap.html)
* instance to manipulate the given map, using
* the given
* [conversions](https://bpf.alba.sh/docs/interfaces/typeconversion.html)
* for keys and values.
*
* @param name Map name
* @category Map access
*/
getMap(name, keyConv, valueConv) {
return new bpf_1.ConvMap(this.getMapRef(name), keyConv, valueConv);
}
/**
* Creates and returns a
* [RawArrayMap](https://bpf.alba.sh/docs/classes/rawarraymap.html)
* instance to manipulate the given array map.
*
* @param name Map name
* @category Map access
*/
getRawArrayMap(name) {
return new bpf_1.RawArrayMap(this.getMapRef(name));
}
/**
* Creates and returns a generic
* [IArrayMap](https://bpf.alba.sh/docs/interfaces/iarraymap.html)
* instance to manipulate the given array map, using
* the given
* [conversion](https://bpf.alba.sh/docs/interfaces/typeconversion.html)
* for values.
*
* @param name Map name
* @category Map access
*/
getArrayMap(name, valueConv) {
return new bpf_1.ConvArrayMap(this.getMapRef(name), valueConv);
}
/**
* Creates and returns a
* [RawQueueMap](https://bpf.alba.sh/docs/classes/rawqueuemap.html)
* instance to manipulate the given queue or stack map.
*
* @param name Map name
* @category Map access
*/
getRawQueueMap(name) {
return new bpf_1.RawQueueMap(this.getMapRef(name));
}
/**
* Creates and returns a generic
* [IQueueMap](https://bpf.alba.sh/docs/interfaces/iqueuemap.html)
* instance to manipulate the given queue or stack map, using
* the given
* [conversion](https://bpf.alba.sh/docs/interfaces/typeconversion.html)
* for values.
*
* @param name Map name
* @category Map access
*/
getQueueMap(name, valueConv) {
return new bpf_1.ConvQueueMap(this.getMapRef(name), valueConv);
}
}
exports.BPFModule = BPFModule;
//# sourceMappingURL=index.js.map