lcc/pylcc/pytool/perfSys.py (122 lines of code) (raw):
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
File Name: perfSys
Description :
Author : liaozhaoyan
date: 2022/10/19
-------------------------------------------------
Change Activity:
2022/10/19:
-------------------------------------------------
"""
__author__ = 'liaozhaoyan'
import time
import argparse
import psutil
from datetime import datetime
from multiprocessing import cpu_count
from treelib import Tree
from surftrace.surfElf import CelfKsym
from pylcc.lbcBase import ClbcBase
from pylcc.perfEvent import *
from pylcc.lbcStack import getKStacks
from quickSvg import Flame
bpfPog = r"""
#include "lbc.h"
#define STACK_COUNT 4096
LBC_HASH(calls, u32, u64, STACK_COUNT);
LBC_STACK(call_stack, STACK_COUNT);
static inline void add_hash(struct bpf_map_def* maps, u32 k, u64 v) {
u64 *pv = bpf_map_lookup_elem(maps, &k);
if (pv) {
__sync_fetch_and_add(pv, v);
}
else {
bpf_map_update_elem(maps, &k, &v, BPF_NOEXIST);
}
}
#define incr_hash(maps, k) add_hash(maps, k, 1)
SEC("perf_event")
int trace_stack(struct bpf_perf_event_data *ctx) {
int id = bpf_get_stackid(ctx, &call_stack, KERN_STACKID_FLAGS);
if (id > 0) {
incr_hash(&calls, id);
}
return 0;
}
char _license[] SEC("license") = "GPL";
"""
def getValue(data):
return data['count']
def getNote(tree, node):
root = tree.get_node(tree.root)
perRoot = node.data['count'] * 100.0 / root.data['count']
parent = tree.parent(node.identifier)
if parent is None:
perParent = 100.0
else:
perParent = node.data['count'] * 100.0 / parent.data['count']
return "catch %d samples, %f%% from root, %f%% from parent" % (node.data['count'], perRoot, perParent)
class CperfSys(ClbcBase):
def __init__(self, freq=200, wait=2, cpus=None):
super(CperfSys, self).__init__("sysHigh", bpf_str=bpfPog)
self._freq = freq
self._wait = wait
self._cpus = cpus
self._ksym = CelfKsym()
def loop(self):
pfConfig = {
"sample_freq": self._freq,
"freq": 1,
"type": PerfType.SOFTWARE,
"config": PerfSwIds.CPU_CLOCK,
}
if self._cpus is None:
self.attachAllCpuPerf("trace_stack", pfConfig)
else:
for index, isOn in enumerate(self._cpus):
if isOn:
self.attachPerfEvent("trace_stack", pfConfig, pid=-1, cpu=index)
time.sleep(self._wait)
res = self.maps['calls'].get()
self.setupTree(res)
def _filterChild(self, tree, nid, tag):
for child in tree.children(nid):
if child.tag == tag:
return child
return None
def _setupTitle(self):
onCpu = []
for index, isOn in enumerate(self._cpus):
if isOn:
onCpu.append(str(index))
return "sysHigh for cpu " + ":".join(onCpu)
def setupTree(self, res):
tree = Tree()
root = tree.create_node(tag="total", parent=tree.root, data={"func": "total", "count": 0})
for k, v in res.items():
last = root
syms = getKStacks(self.maps['call_stack'], k, self._ksym)
for sym in syms[::-1]:
sym = sym.encode()
node = self._filterChild(tree, last.identifier, sym)
if node is None:
node = tree.create_node(tag=sym, parent=last, data={"func": sym, "count": v})
else:
node.data["count"] += v
last = node
root.data['count'] += v
flame = Flame("%s.svg" % datetime.now().strftime("%Y%m%d_%H%M%S"))
flame.render(tree, getValue, getNote, self._setupTitle())
class CperfLoop(object):
def __init__(self, args):
super(CperfLoop, self).__init__()
self._args = args
self._cpus = cpu_count()
def _check(self, last, now):
perSys = []
for i in range(self._cpus):
perSys.append(now[i].system - last[i].system)
isRun = False
cpus = []
for v in perSys:
per = v / self._args.interval * 100.0
if per >= self._args.gate:
cpus.append(1)
isRun = True
else:
cpus.append(0)
print(perSys, isRun)
if isRun:
bpf = CperfSys(freq=self._args.freq, wait=self._args.sample, cpus=cpus)
bpf.loop()
def loop(self):
while True:
last = psutil.cpu_times(percpu=True)
time.sleep(self._args.interval)
now = psutil.cpu_times(percpu=True)
self._check(last, now)
if __name__ == "__main__":
examples = """examples: python perfSys -g 10 -i 3 -s 3 -f 200"""
parser = argparse.ArgumentParser(
description="collect sys high flame svg.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=examples
)
parser.add_argument('-i', '--interval', type=float, dest='interval', default=3,
help='system usage sampling interval time, uint second.')
parser.add_argument('-g', '--gate', type=float, dest='gate', default=10,
help='system usage limit trigger svg.')
parser.add_argument('-s', '--sample', type=float, dest='sample', default=3,
help='perf sample time.')
parser.add_argument('-f', '--freq', type=int, dest='freq', default=200,
help='perf sample frequency.')
args = parser.parse_args()
loop = CperfLoop(args)
loop.loop()
pass