Skip to content

Commit

Permalink
hprof: Implement parser
Browse files Browse the repository at this point in the history
Signed-off-by: iipeace <[email protected]>
  • Loading branch information
iipeace committed Jul 22, 2024
1 parent 4c972b9 commit 7b09111
Showing 1 changed file with 158 additions and 10 deletions.
168 changes: 158 additions & 10 deletions guider/guider.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
__credits__ = "Peace Lee"
__license__ = "GPLv2"
__version__ = "3.9.8"
__revision__ = "240720"
__revision__ = "240722"
__maintainer__ = "Peace Lee"
__email__ = "[email protected]"
__repository__ = "https://github.com/iipeace/guider"
Expand Down Expand Up @@ -6012,7 +6012,7 @@ class ConfigMgr(object):
"PTRACE_EVENT_STOP": 128,
}

# ptrace o types #
# ptrace trace types #
PTRACE_O_TYPE = {
"PTRACE_O_TRACESYSGOOD": 1,
"PTRACE_O_TRACEFORK": 1 << PTRACE_EVENT_TYPE["PTRACE_EVENT_FORK"],
Expand Down Expand Up @@ -9585,9 +9585,26 @@ def convUlong2Long(retval):
return retval

@staticmethod
def convStr2Dict(strObj, verb=False):
def convStr2Dict(strObj, verb=False, duplist=False):
try:
return SysMgr.getPkg("json").loads(strObj)
# convert duplicated keys to list #
if duplist:

def _parser(pairs):
defaultdict = SysMgr.getPkg("collections").defaultdict
d = defaultdict(list)
for key, value in pairs:
d[key].append(value)
return {
key: value[0] if len(value) == 1 else value
for key, value in d.items()
}

return SysMgr.getPkg("json").loads(
strObj, object_pairs_hook=_parser
)
else:
return SysMgr.getPkg("json").loads(strObj)
except SystemExit:
sys.exit(0)
except:
Expand Down Expand Up @@ -41392,6 +41409,9 @@ def _getDesc(s, t=0):
# {0:1} {1:1} -q PERFETTO:/data/perfetto
# {0:1} {1:1} -q TRACECONV:/data/traceconv

- {2:1} for whole system with meta info
# {0:1} {1:1} -v

- {2:1} for specific processes
# {0:1} {1:1} -g a.out, surfaceflinger

Expand All @@ -41407,7 +41427,9 @@ def _getDesc(s, t=0):
- {2:1} for specific processes with 1000 bytes interval sampling
# {0:1} {1:1} -g a.out, surfaceflinger -T 1000

- {2:1} after setting debuggable attribute
- {2:1} after changing debuggable attribute
# {0:1} {1:1} -q SETDEBUG
# {0:1} {1:1} -q CLEARDEBUG
# {0:1} {1:1} -q SETDEBUG:com.android.phone

- {2:1} for specific processes with adding options
Expand Down Expand Up @@ -41473,6 +41495,9 @@ def _getDesc(s, t=0):
# {0:1} {1:1}
# {0:1} {1:1} -q SIMPLEPERF:/data/simpleperf

- {2:1} for whole system with meta info
# {0:1} {1:1} -v

- {2:1} for specific processes
# {0:1} {1:1} -g a.out, surfaceflinger

Expand Down Expand Up @@ -56592,13 +56617,15 @@ def doHeapProfRec(appName=None):
if exe:
cmdstr = "command: %s" % " ".join(exe)
targetstr = 'process_cmdline: "%s"' % exe
target = exe
elif "APP" in SysMgr.environList:
apps = ",".join(SysMgr.filterGroup)
if not apps:
SysMgr.printErr("failed to get app list")
return -1
cmdstr = "app: %s" % apps
targetstr = 'process_cmdline: "%s"' % apps
target = apps
elif SysMgr.filterGroup:
pids = []
for g in SysMgr.filterGroup:
Expand All @@ -56612,9 +56639,11 @@ def doHeapProfRec(appName=None):
SysMgr.getCommList(pids),
)
targetstr = 'process_cmdline: "%s"' % ",".join(pids)
target = pids[0]
else:
cmdstr = ": SYSTEM"
targetstr = "all_heaps: true"
target = None

# print command #
SysMgr.printInfo("set target " + cmdstr)
Expand Down Expand Up @@ -56655,12 +56684,18 @@ def doHeapProfRec(appName=None):
if SysMgr.warnEnable:
SysMgr.printWarn(config)

# check and set debuggable attribute #
# change debuggable attribute #
# WARN: target app will be restarted
for a in SysMgr.environList.get("SETDEBUG", []):
SysMgr.executeCommand(
["am set-debug-app -w --persistent " + a]
)
for t, cmd in (
("SETDEBUG", "set-debug-app"),
("CLEARDEBUG", "clear-debug-app"),
):
for a in SysMgr.environList.get(t, []):
if a == "SET":
a = target
SysMgr.executeCommand(
["am %s -w --persistent %s" % (cmd, a)]
)

# make default record command #
cmd = [binPath, "--txt", "-c", "-", "-o", outPath, "-d"]
Expand Down Expand Up @@ -57078,6 +57113,18 @@ def convHeapProfSample(path, scripts=[]):
if scripts == -1:
return scripts

# define lists #
buf = ""
stacks = {}
packets = {}
sourceList = [
"trace_config",
"system_info",
"packages_list",
"profile_packet",
"interned_data",
]

# parse lines #
totalCnt = 0
totalSize = 0
Expand All @@ -57086,6 +57133,46 @@ def convHeapProfSample(path, scripts=[]):
try:
UtilMgr.printProgress(idx, totalLength)
totalSize += len(l)

if l.startswith("packet {"):
buf = "{"
elif l.startswith("}"):
buf = buf.rstrip(",") + "}"
encoded = buf.replace("\\", "\\\\")
json = UtilMgr.convStr2Dict(encoded, True, True)

# save data #
if json:
for s in sourceList:
v = json.get(s)
if not v:
continue

# duplicated #
if s in packets:
if not isinstance(type(packets[s]), list):
packets[s] = [packets[s]]
packets[s].append(v)
else:
packets[s] = v

# reset buffer #
buf = ""
elif l.endswith("{\n"):
l = '"%s": {' % l.rsplit("{", 1)[0].strip()
buf += l
elif l.endswith("}\n"):
buf = buf.rstrip(",") + "},"
else:
ll = l.rstrip().split(":", 1)
if len(ll) == 1:
l = ll[0]
else:
l = '"%s": "%s",' % (
ll[0].strip(),
":".join(ll[1:]).strip('" ').replace('"', "'"),
)
buf += l
except SystemExit:
sys.exit(0)
except:
Expand All @@ -57094,6 +57181,67 @@ def convHeapProfSample(path, scripts=[]):
if totalLength:
UtilMgr.deleteProgress()

# print meta info #
for x in (
["packages_list", "trace_config", "system_info"]
if SysMgr.warnEnable
else []
):
if not x in packets:
continue
SysMgr.printWarn(
("[%s]: " % x) + UtilMgr.convDict2Str(packets[x]), True
)

# process profile info #
profData = packets.get("profile_packet")
if not profData:
SysMgr.printErr("no profiled sample data")
return -1

# process profile samples #
# callstack_id, self_allocated, self_freed, alloc_count, free_count #
for p in profData if isinstance(profData, list) else [profData]:
pdata = p.get("process_dumps")
if not pdata:
continue
samples = pdata.pop("samples")

# print profiling info #
if SysMgr.warnEnable:
SysMgr.printWarn(
"[prof_info]: " + UtilMgr.convDict2Str(pdata), True
)

# save callstack info #
for s in samples:
cid = s.pop("callstack_id")
if not cid:
continue

if not cid in stacks:
stacks[cid] = {}

# merge stats #
for n, v in s.items():
stacks[cid][n] = long(stacks[cid].get(n, 0)) + long(v)

# process intenred data #
"""
mapping_paths: object path
mappings: paths
function_names: function names
frames: function names, mappings
callstacks: frames
"""
internedData = packets.get("interned_data")
if not internedData:
SysMgr.printErr("no profiled intenred data")
return -1
for x in internedData:
for n, v in x.items():
print(n, v)

@staticmethod
def convSimplePerfSample(path, scripts=[]):
# read scripts from file #
Expand Down

0 comments on commit 7b09111

Please sign in to comment.