UNPKG

bpfcc

Version:

Frontend / bindings for BPF Compiler Collection (BCC)

414 lines 14.1 kB
"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