lcc/pylcc/lib/lbcMaps.py (292 lines of code) (raw):
# -*- coding: utf-8 -*-
# cython:language_level=3
"""
-------------------------------------------------
File Name: lbcMaps
Description :
Author : liaozhaoyan
date: 2021/7/20
-------------------------------------------------
Change Activity:
2021/7/20:
-------------------------------------------------
"""
__author__ = 'liaozhaoyan'
import sys
try:
from collections.abc import MutableMapping
except ImportError:
from collections import MutableMapping
from surftrace import InvalidArgsException
from surftrace.surfCommon import CsurfList
class CffiValue(object):
"""
This is the object container provided for types like structs.
If the numeric type in the data stream is not within python's numeric range,
such as struct union etc, then need a container to load all values.
"""
def __init__(self):
super(CffiValue, self).__init__()
def __repr__(self):
"""
object native method, show ffi value information.
Returns:
ffi value information
Raises:
None
"""
res = {}
for mem in dir(self):
if mem.startswith("__") and mem.endswith("__"):
# strip object native member
continue
res[mem] = getattr(self, mem)
return str(res)
class CffiTrans(object):
"""
This is the conversion class of the FFI interface,
you only need to instantiate the class and call the value member function,
then you will get the native python value from c stream.
"""
if sys.version_info.major == 2:
# long is a data type that can be displayed directly for python2
_directList = (int, str, float, long)
else:
_directList = (int, str, float)
_cNativeType = ("char", "short", "int", "long", "float", "double", "enum")
def __init__(self, ffi):
"""
object native method, show ffi value information.
Parameters:
param1 - cffi object to analytical data
Returns:
CffiTrans
"""
super(CffiTrans, self).__init__()
self._ffi = ffi
def value(self, e):
"""
transform ffi cast stream to python value.
Parameters:
param1 - ffi cast stream
Returns:
python value, may int/string/CffiValue
"""
mems = dir(e)
if len(mems) > 0: # check cast stream contains child nodes
v = self._values(e, mems)
else:
v = self._value(e)
return v
def _value(self, e):
"""
transform ffi cast stream to python value, single mode.
Parameters:
param1 - ffi cast stream
Returns:
python value, just a single value.
Raise:
ValueError not support type.
"""
sType = self._ffi.getctype(self._ffi.typeof(e))
cEnd = sType[-1]
if cEnd == "*": # for point and native value
if sType.endswith("* *"): # point
v = self._point(sType[:-2])
else:
v = self._ffi.unpack(e, 1)[0]
elif cEnd == "]":
v = self._array(e, sType)
else:
raise ValueError("not support type: %s" % sType)
return v
def _values(self, e, mems):
"""
transform ffi cast stream to python value, multi mode.
Parameters:
param1 - ffi cast stream
param2 - ffi cast stream members
Returns:
CffiValue object.
Raise:
ValueError not support type.
"""
v = CffiValue()
for mem in mems:
vMem = getattr(e, mem)
if type(getattr(e, mem)) in CffiTrans._directList: # direct type
setattr(v, mem, vMem)
else:
tMem = self._ffi.getctype(self._ffi.typeof(vMem))
cEnd = tMem[-1] # tMem may like struct child *[2], so check by reverse order
if cEnd == ']': # array type at first
setattr(v, mem, self._array(vMem, tMem))
elif "*" in tMem: # then point type
setattr(v, mem, self._point(tMem))
elif dir(vMem) > 0: # for struct etc,recursive call value
setattr(v, mem, self.value(vMem))
else:
raise ValueError("not support type: %s" % tMem, vMem)
return v
def _array(self, e, tMem):
"""
transform ffi array to python value, parse array size at first.
Parameters:
param1 - ffi cast stream
param2 - ffi member from stream
Returns:
python list
"""
tType, sArr = tMem.split("[", 1)
return self._unpackArry(tType, e, sArr)
def _unpackArry(self, tType, e, sArr):
"""
unpack ffi array to python value, parse array size at first.
Parameters:
param1 - ffi member type from stream
param2 - ffi cast stream
param3 - size of Array, string type.
Returns:
python list
Raise:
ValueError not support type.
"""
res = None
sNum, remain = sArr.split(']', 1)
num = int(sNum)
if num > 0: # may zero array
res = []
t = tType.split(" ")[-1]
if len(remain): # multi array, recursive call
for i in range(num):
res.append(self._unpackArry(tType, e[i], remain[1:]))
elif t in CffiTrans._cNativeType:
if tType == "char": # for string
res = self._ffi.string(e)
else:
for i in range(num): # for normal value
res.append(e[i])
elif "*" in tType: # point
for i in range(num):
res.append(self._point(tType))
elif dir(e) > 0: # struct? array type
for i in range(num):
res.append(self.value(e[i]))
else:
raise ValueError("not support type: %s" % tType, e)
return res
@staticmethod
def _point(sType):
"""
show point information.
Parameters:
param1 - ffi cast stream
param2 - ffi member from stream
Returns:
python string to show point info.
"""
res = "point:%s" % sType
return res
class CtypeTable(object):
def __init__(self, fType, ffi):
super(CtypeTable, self).__init__()
self._type = fType
self.ffiType = self._setupFfi(self._type)
self.ffiSize = ffi.sizeof(self._type)
self._obj = CffiTrans(ffi)
self._localData = []
@staticmethod
def _setupFfi(s):
if s.endswith("]"):
return s
else:
return s + " *"
def add(self, data):
self._localData.append(self.load(data))
def clear(self):
self._localData = []
def output(self):
return self._localData
def load(self, data):
return self._obj.value(data)
class CeventBase(object):
def __init__(self, so, name, ffi):
self._so = so
self._id = self._so.lbc_bpf_get_maps_id(name.encode('utf-8'))
if self._id < 0:
raise InvalidArgsException("map %s, not such event" % name)
self.name = name
super(CeventBase, self).__init__()
self._ffi = ffi
@staticmethod
def _setupFfi(s):
if s.endswith("]"):
return s
else:
return s + " *"
class CtableBase(CeventBase):
def __init__(self, so, name, dTypes, ffi):
super(CtableBase, self).__init__(so, name, ffi)
self._kd = dTypes['fktype']
self._vd = dTypes['fvtype']
self.keys = CtypeTable(self._kd, ffi)
self.values = CtypeTable(self._vd, ffi)
def _getSize(self, fType):
return self._ffi.sizeof(fType)
def get(self):
self.keys.clear()
self.values.clear()
v = self._ffi.new(self.values.ffiType)
k1 = self._ffi.new(self.keys.ffiType)
k2 = self._ffi.new(self.keys.ffiType)
while self._so.lbc_map_get_next_key(self._id, k1, k2) == 0:
self.keys.add(k2)
self._so.lbc_map_lookup_elem(self._id, k2, v)
self.values.add(v)
self._ffi.memmove(k1, k2, self.keys.ffiSize)
return dict(zip(self.keys.output(), self.values.output()))
def getThenClear(self):
self.keys.clear()
self.values.clear()
v = self._ffi.new(self.values.ffiType)
k1 = self._ffi.new(self.keys.ffiType)
k2 = self._ffi.new(self.keys.ffiType)
while self._so.lbc_map_get_next_key(self._id, k1, k2) == 0:
self.keys.add(k2)
r = self._so.lbc_map_lookup_and_delete_elem(self._id, k2, v)
if r < 0:
raise InvalidArgsException(
"lbc_map_lookup_and_delete_elem return %d, os may not support this opertation." % r)
self.values.add(v)
self._ffi.memmove(k1, k2, self.keys.ffiSize)
r = dict(zip(self.keys.output(), self.values.output()))
return r
def getKeys(self):
self.keys.clear()
self.values.clear()
k1 = self._ffi.new(self.keys.ffiType)
k2 = self._ffi.new(self.keys.ffiType)
while self._so.lbc_map_get_next_key(self._id, k1, k2) == 0:
self.keys.add(k2)
self._ffi.memmove(k1, k2, self.keys.ffiSize)
return self.keys.output()
def getKeyValue(self, k):
res = None
key = self._ffi.new(self.keys.ffiType, k)
value = self._ffi.new(self.values.ffiType)
if self._so.lbc_map_lookup_elem(self._id, key, value) == 0:
res = self.values.load(value)
return res
def clear(self):
k1 = self._ffi.new(self.keys.ffiType)
k2 = self._ffi.new(self.keys.ffiType)
while self._so.lbc_map_get_next_key(self._id, k1, k2) == 0:
self._so.lbc_map_delete_elem(self._id, k2)
self._ffi.memmove(k1, k2, self.keys.ffiSize)
class CmapsHash(CtableBase):
def __init__(self, so, name, dTypes, ffi):
super(CmapsHash, self).__init__(so, name, dTypes, ffi)
class CmapsArray(CtableBase):
def __init__(self, so, name, dTypes, ffi):
super(CmapsArray, self).__init__(so, name, dTypes, ffi)
def get(self, size=10):
a = []
for i in range(size):
a.append(self.getKeyValue(i))
return a
class CmapsHist2(CmapsArray):
def __init__(self, so, name, dTypes, ffi):
super(CmapsHist2, self).__init__(so, name, dTypes, ffi)
def get(self, size=64):
return super(CmapsHist2, self).get(size)
def showHist(self, head="dummy", array=None):
if array is None:
array = self.get()
aList = CsurfList(1)
print(head)
aList.hist2Show(array)
class CmapsHist10(CmapsArray):
def __init__(self, so, name, dTypes, ffi):
super(CmapsHist10, self).__init__(so, name, dTypes, ffi)
def get(self, size=20):
return super(CmapsHist10, self).get(size)
def showHist(self, head="dummy", array=None):
if array is None:
array = self.get()
aList = CsurfList(1)
print(head)
aList.hist10Show(array)
class CmapsLruHash(CtableBase):
def __init__(self, so, name, dTypes, ffi):
super(CmapsLruHash, self).__init__(so, name, dTypes, ffi)
class CmapsPerHash(CtableBase):
def __init__(self, so, name, dTypes, ffi):
super(CmapsPerHash, self).__init__(so, name, dTypes, ffi)
class CmapsPerArray(CtableBase):
def __init__(self, so, name, dTypes, ffi):
super(CmapsPerArray, self).__init__(so, name, dTypes, ffi)
class CmapsLruPerHash(CtableBase):
def __init__(self, so, name, dTypes, ffi):
super(CmapsLruPerHash, self).__init__(so, name, dTypes, ffi)
class CmapsStack(CtableBase):
def __init__(self, so, name, dTypes, ffi):
super(CmapsStack, self).__init__(so, name, dTypes, ffi)
def getArr(self, stack_id):
return self.getKeyValue(stack_id)
class CmapsEvent(CeventBase):
def __init__(self, so, name, dTypes, ffi):
super(CmapsEvent, self).__init__(so, name, ffi)
self._d = dTypes["fvtype"]
self.ffiType = self._setupFfi(self._d)
self.cb = None
self.lostcb = None
self._obj = CffiTrans(ffi)
def open_perf_buffer(self, cb, lost=None):
self.cb = cb
self.lostcb = lost
def perf_buffer_poll(self, timeout=-1, obj=None):
if obj is None:
obj = self
if not hasattr(obj, "cb"):
raise ValueError("object %s has no attr callback." % obj)
if not hasattr(obj, "lostcb"):
obj.lostcb = None
@self._ffi.callback("void(void *ctx, int cpu, void *data, unsigned int size)")
def _callback(context, cpu, data, size):
if obj.cb:
obj.cb(cpu, data, size)
@self._ffi.callback("void(void *ctx, int cpu, unsigned long long cnt)")
def _lostcb(context, cpu, count):
if obj.lostcb:
obj.lostcb(cpu, count)
else:
print("cpu%d lost %d events" % (cpu, count))
self._so.lbc_set_event_cb(self._id, _callback, _lostcb)
self._so.lbc_event_loop(self._id, timeout)
def event(self, data):
e = self._ffi.cast(self.ffiType, data)
return self._obj.value(e)
mapsDict = {'event': CmapsEvent,
'hash': CmapsHash,
'array': CmapsArray,
'hist2': CmapsHist2,
'hist10': CmapsHist10,
'lruHash': CmapsLruHash,
'perHash': CmapsPerHash,
'perArray': CmapsPerArray,
'lruPerHash': CmapsLruPerHash,
'stack': CmapsStack, }
def paserMaps(so, name, dTypes):
t = dTypes['type']
if t in mapsDict:
return mapsDict['t'](so, name, dTypes)
if __name__ == "__main__":
pass