forked from nuttx/nuttx-update
new feature on trace dump support segger-rtt
Signed-off-by: xiajizhong <xiajizhong@xiaomi.com>
This commit is contained in:
parent
120c4a1660
commit
7160849f63
6 changed files with 362 additions and 23 deletions
|
@ -26,6 +26,7 @@
|
|||
#include <nuttx/note/noteram_driver.h>
|
||||
#include <nuttx/note/notectl_driver.h>
|
||||
#include <nuttx/note/notesnap_driver.h>
|
||||
#include <nuttx/segger/note_rtt.h>
|
||||
#include <nuttx/segger/sysview.h>
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -59,6 +60,14 @@ int note_initialize(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NOTE_RTT
|
||||
ret = notertt_register();
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRIVERS_NOTECTL
|
||||
ret = notectl_register();
|
||||
if (ret < 0)
|
||||
|
|
|
@ -174,6 +174,29 @@ config SERIAL_RTT_CONSOLE
|
|||
endif # SERIAL_RTT
|
||||
|
||||
if DRIVERS_NOTE
|
||||
|
||||
config NOTE_RTT
|
||||
bool "Note RTT driver, write note into Segger J-Link RTTLog"
|
||||
default n
|
||||
---help---
|
||||
If this option is selected, then jlink rtt is enabled to capture
|
||||
scheduler instrumentation data.
|
||||
|
||||
if NOTE_RTT
|
||||
|
||||
config NOTE_RTT_CHANNEL
|
||||
int "Note RTT driver, Segger J-Link stream channel"
|
||||
default 0
|
||||
---help---
|
||||
Channel of notertt jlink stream channel
|
||||
|
||||
config NOTE_RTT_BUFFER_SIZE_UP
|
||||
int "Note RTT driver, Segger J-Link buffer size"
|
||||
default 1024
|
||||
---help---
|
||||
Buffer size config for notertt jlink config buffer
|
||||
endif # NOTE_RTT
|
||||
|
||||
config SEGGER_SYSVIEW
|
||||
bool "Note SEGGER SystemView driver"
|
||||
select SEGGER_RTT
|
||||
|
@ -185,8 +208,6 @@ config SEGGER_SYSVIEW
|
|||
systems comprising multiple threads and interrupts. SystemView can ensure
|
||||
unintended interactions and resource conflicts.
|
||||
|
||||
endif # DRIVERS_NOTE
|
||||
|
||||
if SEGGER_SYSVIEW
|
||||
|
||||
config SEGGER_SYSVIEW_RTT_CHANNEL
|
||||
|
@ -206,7 +227,8 @@ config SEGGER_SYSVIEW_RAM_BASE
|
|||
default 0
|
||||
---help---
|
||||
The lowest RAM address used for IDs
|
||||
endif # SEGGER_SYSVIEW
|
||||
|
||||
endif
|
||||
endif # DRIVERS_NOTE
|
||||
|
||||
endmenu # Segger RTT drivers
|
||||
|
|
|
@ -65,6 +65,10 @@ ifeq ($(CONFIG_SYSLOG_RTT),y)
|
|||
CSRCS += segger/syslog_rtt.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_NOTE_RTT),y)
|
||||
CSRCS += segger/note_rtt.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SERIAL_RTT),y)
|
||||
CSRCS += segger/serial_rtt.c
|
||||
endif
|
||||
|
|
108
drivers/segger/note_rtt.c
Normal file
108
drivers/segger/note_rtt.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/****************************************************************************
|
||||
* drivers/segger/note_rtt.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/note/note_driver.h>
|
||||
#include <nuttx/segger/note_rtt.h>
|
||||
#include <nuttx/segger/rtt.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
struct notertt_s
|
||||
{
|
||||
struct note_driver_s driver;
|
||||
struct lib_rttoutstream_s stream;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
static void notertt_add(FAR struct note_driver_s *drv,
|
||||
FAR const void *note, size_t len);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static const struct note_driver_ops_s g_notertt_ops =
|
||||
{
|
||||
notertt_add,
|
||||
};
|
||||
|
||||
struct notertt_s g_notertt =
|
||||
{
|
||||
{
|
||||
&g_notertt_ops
|
||||
}
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: notertt_add
|
||||
*
|
||||
* Description:
|
||||
* Put the variable length note to rttoutstream
|
||||
*
|
||||
* Input Parameters:
|
||||
* buf - The note buffer
|
||||
* notelen - The buffer length
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void notertt_add(FAR struct note_driver_s *drv,
|
||||
FAR const void *buf, size_t notelen)
|
||||
{
|
||||
FAR struct notertt_s *note = (FAR struct notertt_s *)drv;
|
||||
lib_stream_puts(¬e->stream, buf, notelen);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: notertt_register
|
||||
*
|
||||
* Description:
|
||||
* Register a serial driver using note_driver_register
|
||||
*
|
||||
* Input Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero on succress. A negated errno value is returned on a failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int notertt_register(void)
|
||||
{
|
||||
lib_rttoutstream_open(&g_notertt.stream,
|
||||
CONFIG_NOTE_RTT_CHANNEL,
|
||||
CONFIG_NOTE_RTT_BUFFER_SIZE_UP);
|
||||
return note_driver_register(&g_notertt.driver);
|
||||
}
|
42
include/nuttx/segger/note_rtt.h
Normal file
42
include/nuttx/segger/note_rtt.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/****************************************************************************
|
||||
* include/nuttx/segger/note_rtt.h
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __INCLUDE_NUTTX_SEGGER_NOTE_RTT_H
|
||||
#define __INCLUDE_NUTTX_SEGGER_NOTE_RTT_H
|
||||
|
||||
/****************************************************************************
|
||||
* Name: notertt_register
|
||||
*
|
||||
* Description:
|
||||
* Register RTT note driver
|
||||
*
|
||||
* Input Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero on success. A negated errno value is returned on a failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NOTE_RTT
|
||||
int notertt_register(void);
|
||||
#endif
|
||||
|
||||
#endif /* __INCLUDE_NUTTX_SEGGER_NOTE_RTT_H */
|
|
@ -23,8 +23,11 @@ import argparse
|
|||
import bisect
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from typing import Union
|
||||
|
||||
from pycstruct import pycstruct
|
||||
|
||||
try:
|
||||
import cxxfilt
|
||||
import parse
|
||||
|
@ -102,10 +105,10 @@ class OtherModel(BaseModel):
|
|||
)
|
||||
ret = pattern.match(string)
|
||||
if ret is not None:
|
||||
return OtherModel(payload = string)
|
||||
return OtherModel(payload=string)
|
||||
|
||||
|
||||
class AtraceModel(BaseModel):
|
||||
class ATraceModel(BaseModel):
|
||||
sign: str
|
||||
pid: int
|
||||
func: str
|
||||
|
@ -115,10 +118,14 @@ class AtraceModel(BaseModel):
|
|||
|
||||
ret = pattern.parse(string)
|
||||
if ret is not None:
|
||||
return AtraceModel(**ret.named)
|
||||
return ATraceModel(**ret.named)
|
||||
|
||||
def dump(self):
|
||||
return "tracing_mark_write: %c|%d|%s" % (self.sign, self.pid, self.func)
|
||||
return "tracing_mark_write: %c|%d|%s" % (
|
||||
self.sign,
|
||||
self.pid,
|
||||
self.func,
|
||||
)
|
||||
|
||||
|
||||
class TraceModel(BaseModel):
|
||||
|
@ -126,7 +133,7 @@ class TraceModel(BaseModel):
|
|||
tid: int
|
||||
cpu: int
|
||||
time: float
|
||||
payload: Union[AtraceModel, OtherModel]
|
||||
payload: Union[ATraceModel, OtherModel]
|
||||
|
||||
def dump_one_trace(self):
|
||||
header = "%16s-%-5d [%03d] %12.6f: %s" % (
|
||||
|
@ -141,7 +148,7 @@ class TraceModel(BaseModel):
|
|||
|
||||
class Trace(object):
|
||||
def __payloadParse(self, string):
|
||||
trace = AtraceModel.parse(AtraceModel, string)
|
||||
trace = ATraceModel.parse(ATraceModel, string)
|
||||
if trace is not None:
|
||||
return trace
|
||||
trace = OtherModel.parse(OtherModel, string)
|
||||
|
@ -151,7 +158,7 @@ class Trace(object):
|
|||
def __init__(self, file):
|
||||
with open(file, "r") as tracefile:
|
||||
self.lines = tracefile.readlines()
|
||||
self.alltrace = list()
|
||||
self.all_trace = list()
|
||||
self.parse()
|
||||
|
||||
def parse(self):
|
||||
|
@ -163,15 +170,151 @@ class Trace(object):
|
|||
for line in self.lines:
|
||||
ret = header_pattern.parse(line.strip())
|
||||
if ret and ret.named["payload"]:
|
||||
self.alltrace.append(TraceModel(**ret.named))
|
||||
self.all_trace.append(TraceModel(**ret.named))
|
||||
|
||||
def dump_trace(self):
|
||||
formatted = ["# tracer: nop", "#"]
|
||||
for trace in self.alltrace:
|
||||
for trace in self.all_trace:
|
||||
formatted.append(trace.dump_one_trace())
|
||||
return formatted
|
||||
|
||||
|
||||
class ParseBinaryLogTool:
|
||||
def __init__(
|
||||
self,
|
||||
binary_log_path,
|
||||
elf_nuttx_path,
|
||||
out_path=None,
|
||||
size_long=4,
|
||||
config_endian_big=False,
|
||||
config_smp=0,
|
||||
):
|
||||
self.binary_log_path = binary_log_path
|
||||
self.elf_nuttx_path = elf_nuttx_path
|
||||
self.out_path = out_path
|
||||
self.symbol_tables = SymbolTables(self.elf_nuttx_path)
|
||||
self.symbol_tables.parse_symbol()
|
||||
with open(self.binary_log_path, "rb") as f:
|
||||
self.in_bytes = f.read()
|
||||
self.parsed = list()
|
||||
self.task_name_dict = dict()
|
||||
self.size_long = size_long
|
||||
self.size_note_common = 3 + size_long * 3
|
||||
self.config_endian_big = config_endian_big
|
||||
self.config_smp = config_smp
|
||||
|
||||
def parse_by_endian(self, lis):
|
||||
res = [hex(e)[2:] for e in lis] # strip prefix "0x"
|
||||
res = [e if len(e) == 2 else "0" + e if len(e) == 1 else "00" for e in res]
|
||||
if not self.config_endian_big:
|
||||
res.reverse()
|
||||
res = "0x" + "".join(res)
|
||||
return int(res, 16), res
|
||||
|
||||
def parse_one(self, st: int):
|
||||
if st >= len(self.in_bytes):
|
||||
print("error, index break bound")
|
||||
one = pycstruct.StructDef()
|
||||
one.add("uint8", "nc_length")
|
||||
one.add("uint8", "nc_type")
|
||||
one.add("uint8", "nc_priority")
|
||||
if self.config_smp > 0:
|
||||
one.add("uint8", "nc_cpu")
|
||||
one.add("uint8", "nc_pid", self.size_long)
|
||||
one.add("uint8", "nc_systime_sec", self.size_long)
|
||||
one.add("uint8", "nc_systime_nsec", self.size_long)
|
||||
res = one.deserialize(self.in_bytes, st)
|
||||
|
||||
# case type
|
||||
if res["nc_type"] == 0:
|
||||
one.add("uint8", "nsa_name", res["nc_length"] - self.size_note_common)
|
||||
elif res["nc_type"] == 22:
|
||||
one.add("uint8", "nst_ip", self.size_long) # pointer of func
|
||||
one.add("uint8", "nst_data") # B|E
|
||||
elif res["nc_type"] == 20: # case: NOTE_IRQ_ENTER
|
||||
one.add("uint8", "nih_irq")
|
||||
elif res["nc_type"] == 21: # case: NOTE_IRQ_LEAVE
|
||||
one.add("uint8", "nih_irq")
|
||||
else:
|
||||
print(f'skipped note, nc_type={res["nc_type"]}')
|
||||
|
||||
res = one.deserialize(self.in_bytes, st)
|
||||
# parse pid, systime ...
|
||||
res["nc_pid"] = self.parse_by_endian(res["nc_pid"])[0]
|
||||
res["nc_systime_sec"] = self.parse_by_endian(res["nc_systime_sec"])[0]
|
||||
res["nc_systime_nsec"] = self.parse_by_endian(res["nc_systime_nsec"])[0]
|
||||
if "nst_ip" in res:
|
||||
res["nst_ip"] = self.parse_by_endian(res["nst_ip"])[1]
|
||||
|
||||
# parse cpu, name ...
|
||||
if "nc_cpu" not in res:
|
||||
res["nc_cpu"] = 0
|
||||
if "nsa_name" in res:
|
||||
nsa_name = "".join(chr(i) for i in res["nsa_name"][:-1])
|
||||
self.task_name_dict[res["nc_pid"]] = nsa_name
|
||||
if "nst_data" in res:
|
||||
res["nst_data"] = chr(res["nst_data"])
|
||||
return res
|
||||
|
||||
def track_one(self, one): # print by case
|
||||
nc_type = one["nc_type"]
|
||||
nc_pid = one["nc_pid"]
|
||||
nc_cpu = one["nc_cpu"]
|
||||
nsa_name = self.task_name_dict.get(nc_pid, "noname")
|
||||
float_time = float(
|
||||
str(one["nc_systime_sec"]) + "." + str(one["nc_systime_nsec"])
|
||||
)
|
||||
|
||||
# case nc_type
|
||||
a_model, other_model = None, None
|
||||
if nc_type == 0: # case: NOTE_START
|
||||
payload = (
|
||||
f"sched_wakeup_new: comm={nsa_name} pid={nc_pid} target_cpu={nc_cpu}"
|
||||
)
|
||||
other_model = OtherModel(payload="").parse(payload)
|
||||
if nc_type == 3: # case: NOTE_RESUME
|
||||
payload = f"sched_waking: comm={nsa_name} pid={nc_pid} target_cpu={nc_cpu}"
|
||||
other_model = OtherModel(payload="").parse(payload)
|
||||
if nc_type == 22: # case: NOTE_DUMP_STRING
|
||||
func_name = self.symbol_tables.symbol_dict.get(
|
||||
int(one["nst_ip"], 16), "no_func_name"
|
||||
)
|
||||
payload = f'tracing_mark_write: {one["nst_data"]}|{nc_pid}|{func_name}'
|
||||
a_model = ATraceModel(sign="", pid=-1, func="").parse(payload)
|
||||
if nc_type == 20: # case: NOTE_IRQ_ENTER
|
||||
payload = f'irq_handler_entry: irq={one["nih_irq"]} name={one["nih_irq"]}'
|
||||
other_model = OtherModel(payload="").parse(payload)
|
||||
if nc_type == 21: # case: NOTE_IRQ_LEAVE
|
||||
payload = f'irq_handler_exit: irq={one["nih_irq"]} name={one["nih_irq"]}'
|
||||
other_model = OtherModel(payload="").parse(payload)
|
||||
|
||||
for mod in [a_model, other_model]:
|
||||
if mod is not None:
|
||||
self.parsed.append(
|
||||
TraceModel(
|
||||
name=nsa_name,
|
||||
tid=nc_pid,
|
||||
cpu=nc_cpu,
|
||||
time=float_time,
|
||||
payload=mod,
|
||||
)
|
||||
)
|
||||
|
||||
def parse_binary_log(self):
|
||||
st = 0
|
||||
while st < len(self.in_bytes):
|
||||
one = self.parse_one(st)
|
||||
self.track_one(one)
|
||||
st += one["nc_length"]
|
||||
if self.out_path is not None:
|
||||
with open(self.out_path, "wt") as f:
|
||||
for mod in self.parsed:
|
||||
f.write(mod.dump_one_trace() + "\n")
|
||||
else:
|
||||
for mod in self.parsed:
|
||||
print(f"debug, dump one={mod.dump_one_trace()}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-t", "--trace", help="original trace file", required=True)
|
||||
|
@ -184,21 +327,32 @@ if __name__ == "__main__":
|
|||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
trace = Trace(args.trace)
|
||||
if args.elf:
|
||||
file_type = subprocess.check_output(f"file -b {args.trace}", shell=True)
|
||||
file_type = str(file_type, "utf-8").lower()
|
||||
if "ascii" in file_type:
|
||||
print("trace log type is text")
|
||||
trace = Trace(args.trace)
|
||||
symbol = SymbolTables(args.elf)
|
||||
symbol.parse_symbol()
|
||||
|
||||
for onetrace in trace.alltrace:
|
||||
|
||||
if isinstance(onetrace.payload, AtraceModel) and re.fullmatch(
|
||||
for onetrace in trace.all_trace:
|
||||
if isinstance(onetrace.payload, ATraceModel) and re.fullmatch(
|
||||
r"^0x[0-9a-fA-F]+$", onetrace.payload.func
|
||||
):
|
||||
onetrace.payload.func = symbol.addr2symbol(
|
||||
int(onetrace.payload.func, 16)
|
||||
)
|
||||
|
||||
lines = trace.dump_trace()
|
||||
with open(args.out, "w") as out:
|
||||
out.writelines("\n".join(lines))
|
||||
print(os.path.abspath(args.out))
|
||||
lines = trace.dump_trace()
|
||||
with open(args.out, "w") as out:
|
||||
out.writelines("\n".join(lines))
|
||||
print(os.path.abspath(args.out))
|
||||
else:
|
||||
print("trace log type is binary")
|
||||
if args.elf:
|
||||
print(
|
||||
"parse_binary_log, default config, size_long=4, config_endian_big=False, config_smp=0"
|
||||
)
|
||||
parse_binary_log_tool = ParseBinaryLogTool(args.trace, args.elf, args.out)
|
||||
parse_binary_log_tool.symbol_tables.parse_symbol()
|
||||
parse_binary_log_tool.parse_binary_log()
|
||||
else:
|
||||
print("error, please add elf file path")
|
||||
|
|
Loading…
Reference in a new issue