diff --git a/drivers/note/note_initialize.c b/drivers/note/note_initialize.c index 9c51185ff3..322a5c7443 100644 --- a/drivers/note/note_initialize.c +++ b/drivers/note/note_initialize.c @@ -26,6 +26,7 @@ #include #include #include +#include #include /**************************************************************************** @@ -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) diff --git a/drivers/segger/Kconfig b/drivers/segger/Kconfig index 95c0bf8af3..50125380cc 100644 --- a/drivers/segger/Kconfig +++ b/drivers/segger/Kconfig @@ -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 diff --git a/drivers/segger/Make.defs b/drivers/segger/Make.defs index 02306b97c7..b21bfbbb5c 100644 --- a/drivers/segger/Make.defs +++ b/drivers/segger/Make.defs @@ -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 diff --git a/drivers/segger/note_rtt.c b/drivers/segger/note_rtt.c new file mode 100644 index 0000000000..2ad8bc8bfc --- /dev/null +++ b/drivers/segger/note_rtt.c @@ -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 +#include +#include + +/**************************************************************************** + * 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); +} diff --git a/include/nuttx/segger/note_rtt.h b/include/nuttx/segger/note_rtt.h new file mode 100644 index 0000000000..7cb1242fb4 --- /dev/null +++ b/include/nuttx/segger/note_rtt.h @@ -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 */ diff --git a/tools/parsetrace.py b/tools/parsetrace.py index 22200e321d..6972830629 100755 --- a/tools/parsetrace.py +++ b/tools/parsetrace.py @@ -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")