Server IP : 184.154.167.98 / Your IP : 3.135.206.19 Web Server : Apache System : Linux pink.dnsnetservice.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 User : puertode ( 1767) PHP Version : 7.2.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /usr/libexec/pcp/pmdas/bcc/modules/ |
Upload File : |
# # Copyright (C) 2019 Marko Myllynen <myllynen@redhat.com> # Based on the klockstat BCC tool by David Valin and Jiri Olsa: # https://github.com/iovisor/bcc/blob/master/tools/klockstat.py # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # """ PCP BCC PMDA klockstat module """ # pylint: disable=invalid-name, too-many-instance-attributes, too-many-return-statements from ctypes import c_int from os import path from bcc import BPF from pcp.pmapi import pmUnits from cpmapi import PM_TYPE_U64, PM_SEM_COUNTER, PM_SEM_INSTANT, PM_COUNT_ONE, PM_TIME_NSEC from cpmapi import PM_ERR_PMID from cpmda import PMDA_FETCH_NOVALUES from modules.pcpbcc import PCPBCCBase # # BPF program # bpf_src = "modules/klockstat.bpf" # # PCP BCC PMDA constants # MODULE = 'klockstat' BASENS = 'kernel.lock.mutex.' units_count = pmUnits(0, 0, 1, 0, 0, PM_COUNT_ONE) units_nsecs = pmUnits(0, 1, 0, 0, PM_TIME_NSEC, 0) # # PCP BCC Module # class PCPBCCModule(PCPBCCBase): """ PCP BCC klockstat module """ def __init__(self, config, log, err, proc_refresh): """ Constructor """ PCPBCCBase.__init__(self, MODULE, config, log, err) self.pids = [] self.proc_filter = None self.proc_refresh = proc_refresh self.caller_filter = None self.stack_depth = 1 self.stack_storage_size = 16384 self.use_caller_offset = False self.stack_traces = None self.aq_counts = None self.aq_maxs = None self.aq_totals = None self.hl_counts = None self.hl_maxs = None self.hl_totals = None for opt in self.config.options(MODULE): if opt == 'process': self.proc_filter = self.config.get(MODULE, opt) self.update_pids(self.get_proc_info(self.proc_filter)) if opt == 'caller_filter': self.caller_filter = self.config.get(MODULE, opt).encode() if opt == 'stack_depth': self.stack_depth = int(self.config.get(MODULE, opt)) if opt == 'stack_storage_size': self.stack_storage_size = int(self.config.get(MODULE, opt)) if opt == 'use_caller_offset': self.use_caller_offset = self.config.getboolean(MODULE, opt) if self.stack_depth < 1: raise RuntimeError("stack_depth must be greater than zero.") self.stack_depth += 1 self.cache = None self.insts = None self.log("Initialized.") def metrics(self): """ Get metric definitions """ name = BASENS self.items = ( # Name - reserved - type - semantics - units - help (name + 'spin.avg', None, PM_TYPE_U64, PM_SEM_INSTANT, units_nsecs, 'spin avg'), (name + 'spin.count', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'spin count'), (name + 'spin.max', None, PM_TYPE_U64, PM_SEM_INSTANT, units_nsecs, 'spin max'), (name + 'spin.total', None, PM_TYPE_U64, PM_SEM_COUNTER, units_nsecs, 'spin total'), (name + 'hold.avg', None, PM_TYPE_U64, PM_SEM_INSTANT, units_nsecs, 'hold avg'), (name + 'hold.count', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'hold count'), (name + 'hold.max', None, PM_TYPE_U64, PM_SEM_INSTANT, units_nsecs, 'hold max'), (name + 'hold.total', None, PM_TYPE_U64, PM_SEM_COUNTER, units_nsecs, 'hold total'), ) return True, self.items def reset_cache(self): """ Reset internal cache """ self.cache = {} self.insts = {} def undef_cache(self): """ Undefine internal cache """ self.cache = None self.insts = None def compile(self): """ Compile BPF """ try: if not self.pids and self.proc_filter and not self.proc_refresh: raise RuntimeError("No process to attach found.") if not self.bpf_text: with open(path.dirname(__file__) + '/../' + bpf_src) as src: self.bpf_text = src.read() self.bpf_text = self.bpf_text.replace("STACK_STORAGE_SIZE", str(self.stack_storage_size)) if not self.pids and self.proc_filter and self.proc_refresh: self.log("No process to attach found, activation postponed.") return bpf_text = self.bpf_text bpf_text = self.apply_pid_filter(bpf_text, self.pids, False, True) if self.debug: self.log("BPF to be compiled:\n" + bpf_text.strip()) self.reset_cache() self.bpf = BPF(text=bpf_text) self.bpf.attach_kprobe(event="mutex_unlock", fn_name="mutex_unlock_enter") self.bpf.attach_kretprobe(event="mutex_lock", fn_name="mutex_lock_return") self.bpf.attach_kprobe(event="mutex_lock", fn_name="mutex_lock_enter") self.stack_traces = self.bpf["stack_traces"] self.aq_counts = self.bpf["aq_report_count"] self.aq_maxs = self.bpf["aq_report_max"] self.aq_totals = self.bpf["aq_report_total"] self.hl_counts = self.bpf["hl_report_count"] self.hl_maxs = self.bpf["hl_report_max"] self.hl_totals = self.bpf["hl_report_total"] self.log("Compiled.") except Exception as error: # pylint: disable=broad-except self.bpf = None self.undef_cache() self.err(str(error)) self.err("Module NOT active!") raise def refresh(self): """ Refresh BPF data """ if self.bpf is None: return None def get_fnname(name): """ Get function name as string """ try: return name.decode() except AttributeError: return name for key in self.aq_counts.keys(): stack = [] caller = "<Missed Kernel Stack>" if key.value > 0: stack = list(self.stack_traces.walk(key.value)) caller = self.bpf.ksym(stack[1], show_offset=self.use_caller_offset) if self.caller_filter and caller.find(self.caller_filter): continue caller = get_fnname(caller) for addr in stack[2:self.stack_depth]: caller += "+" caller += get_fnname(self.bpf.ksym(addr, show_offset=self.use_caller_offset)) self.cache[caller] = key self.insts[caller] = c_int(1) return self.insts def bpfdata(self, item, inst): """ Return BPF data as PCP metric value """ try: key = self.cache[self.pmdaIndom.inst_name_lookup(inst)] if item == 0: return [int(self.aq_totals[key].value / self.aq_counts[key].value), 1] elif item == 1: return [self.aq_counts[key].value, 1] elif item == 2: return [self.aq_maxs[key].value, 1] elif item == 3: return [self.aq_totals[key].value, 1] elif item == 4: return [int(self.hl_totals[key].value / self.hl_counts[key].value), 1] elif item == 5: return [self.hl_counts[key].value, 1] elif item == 6: return [self.hl_maxs[key].value, 1] elif item == 7: return [self.hl_totals[key].value, 1] else: return [PM_ERR_PMID, 0] except Exception: # pylint: disable=broad-except return [PMDA_FETCH_NOVALUES, 0]