├── .clang-format ├── .github └── workflows │ └── actions.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile.am ├── README.org ├── acinclude.m4 ├── benchmark └── benchmarks.c ├── ci ├── Jenkinsfile ├── gen-cvs-stimuli.sh ├── get-cvs-stimuli.sh ├── get-gcc.sh ├── get-pulp-rt-stimuli.sh ├── run-cli-tests.sh ├── run-static-analyzer.sh ├── run-tests-32.sh └── run-tests-64.sh ├── configure-debug ├── configure.ac ├── data ├── interrupt ├── trdb_packets ├── trdb_stimuli └── tx_spi ├── doc └── .gitkeep ├── doxyfile ├── include ├── disassembly.h ├── serialize.h ├── svdpi.h ├── trace_debugger.h └── trdb_sv.h ├── internal ├── disassembly_private.h ├── kvec.h ├── riscv_encoding.h ├── trdb_private.h └── utils.h ├── src ├── disassembly.c ├── dpi │ └── trdb_sv.c ├── error.c ├── serialize.c ├── trace_debugger.c ├── trdb.c └── utils.c └── test ├── test_trdb.sh ├── test_trdb_diff.sh ├── tests.c ├── trdb_diff_expected ├── trdb_normal_expected └── workaround.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | IndentWidth: 4 4 | UseTab: Never 5 | BreakBeforeBraces: Linux 6 | AllowShortIfStatementsOnASingleLine: false 7 | AllowShortLoopsOnASingleLine: false 8 | AllowShortFunctionsOnASingleLine: false 9 | IndentCaseLabels: false 10 | AlignEscapedNewlinesLeft: false 11 | AlignTrailingComments: true 12 | AlignOperands: true 13 | AllowAllParametersOfDeclarationOnNextLine: false 14 | AlignAfterOpenBracket: true 15 | SpaceAfterCStyleCast: false 16 | BreakStringLiterals: false 17 | SortIncludes: false 18 | ContinuationIndentWidth: 4 19 | ColumnLimit: 80 20 | IndentPPDirectives: AfterHash 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | ForEachMacros: 24 | - 'TAILQ_FOREACH' 25 | - 'TAILQ_FOREACH_REVERSE' 26 | BreakBeforeBinaryOperators: None 27 | MaxEmptyLinesToKeep: 1 28 | AlwaysBreakAfterDefinitionReturnType: None 29 | AlwaysBreakAfterReturnType: None 30 | AlwaysBreakBeforeMultilineStrings: false 31 | AlignConsecutiveAssignments: true 32 | ... 33 | -------------------------------------------------------------------------------- /.github/workflows/actions.yml: -------------------------------------------------------------------------------- 1 | name: TRDB CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | with: 17 | submodules: true 18 | - name: autoreconf 19 | run: autoreconf -vif 20 | - name: configure 21 | run: ./configure 22 | - name: make 23 | run: make 24 | - name: make check 25 | run: make check 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | *.lo 4 | *.la 5 | *.log 6 | *.trs 7 | riscv-fesvr 8 | riscv-isa-sim-32 9 | riscv-isa-sim-64 10 | riscv-tests-32 11 | riscv-tests-64 12 | riscv-traces-32 13 | riscv-traces-64 14 | compile_commands.json 15 | trdb-spike-32 16 | trdb-spike-64 17 | data 18 | config.h 19 | libtrdb.a 20 | tests 21 | trdb 22 | benchmarks 23 | doc/html 24 | doc/latex 25 | 26 | 27 | .Tpo 28 | Makefile.in 29 | /ar-lib 30 | /mdate-sh 31 | /py-compile 32 | /test-driver 33 | /ylwrap 34 | .deps/ 35 | .dirstamp 36 | .libs 37 | libtool 38 | 39 | autom4te.cache 40 | /autoscan.log 41 | /autoscan-*.log 42 | /aclocal.m4 43 | /compile 44 | /config.cache 45 | /config.guess 46 | /config.h.in 47 | /config.log 48 | /config.status 49 | /config.sub 50 | /configure 51 | /configure.scan 52 | /depcomp 53 | /install-sh 54 | /missing 55 | /stamp-h1 56 | 57 | /ltmain.sh 58 | 59 | /texinfo.tex 60 | 61 | m4/libtool.m4 62 | m4/ltoptions.m4 63 | m4/ltsugar.m4 64 | m4/ltversion.m4 65 | m4/lt~obsolete.m4 66 | 67 | *~ 68 | .cache 69 | 70 | Makefile 71 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/riscv-binutils-gdb"] 2 | path = lib/riscv-binutils-gdb 3 | url = https://github.com/pulp-platform/riscv-binutils-gdb.git 4 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # Author: Robert Balas 3 | SUBDIRS = lib/riscv-binutils-gdb/bfd lib/riscv-binutils-gdb/opcodes lib/riscv-binutils-gdb/libiberty lib/riscv-binutils-gdb/zlib 4 | 5 | dist_doc_DATA = README.org 6 | bin_PROGRAMS = trdb 7 | noinst_PROGRAMS = tests benchmarks 8 | 9 | TRDB_LINKER_INCLUDES = -Ilib/riscv-binutils-gdb/include -Ilib/riscv-binutils-gdb/bfd 10 | TRDB_ALL_LINKER_FLAGS = -Llib/riscv-binutils-gdb/opcodes -Llib/riscv-binutils-gdb/bfd -Llib/riscv-binutils-gdb/libiberty -Llib/riscv-binutils-gdb/zlib 11 | TRDB_ALL_LINKER_LIBS= -lbfd -lopcodes -liberty -lz -ldl -lc 12 | 13 | # TRDB CLI tool 14 | trdb_SOURCES = src/trace_debugger.c src/utils.c src/serialize.c \ 15 | src/error.c src/disassembly.c src/trdb.c 16 | 17 | trdb_LDFLAGS = $(TRDB_ALL_LINKER_FLAGS) 18 | trdb_LDADD = $(TRDB_ALL_LINKER_LIBS) 19 | 20 | # Tests 21 | tests_SOURCES = src/trace_debugger.c src/utils.c src/serialize.c \ 22 | src/error.c src/disassembly.c test/tests.c 23 | tests_LDFLAGS = $(TRDB_ALL_LINKER_FLAGS) 24 | tests_LDADD = $(TRDB_ALL_LINKER_LIBS) 25 | 26 | TESTS=tests 27 | LOG_DRIVER=./tests 28 | 29 | # Benchmarks 30 | benchmarks_SOURCES = src/trace_debugger.c src/utils.c src/serialize.c \ 31 | src/error.c src/disassembly.c benchmark/benchmarks.c 32 | benchmarks_LDFLAGS = $(TRDB_ALL_LINKER_FLAGS) 33 | benchmarks_LDADD = $(TRDB_ALL_LINKER_LIBS) 34 | 35 | # Dynamic library 36 | lib_LTLIBRARIES = libtrdb.la 37 | libtrdb_la_SOURCES = src/trace_debugger.c src/utils.c src/serialize.c \ 38 | src/error.c src/disassembly.c src/dpi/trdb_sv.c 39 | libtrdb_la_LDFLAGS = $(TRDB_ALL_LINKER_FLAGS) 40 | libtrdb_la_LIBADD = $(TRDB_ALL_LINKER_LIBS) 41 | 42 | include_HEADERS = include/disassembly.h include/serialize.h include/trace_debugger.h 43 | 44 | AM_CFLAGS = -std=gnu11 -Wall -Wextra -Werror=format-security -Wno-missing-field-initializers -Wno-unused-function -Wno-missing-braces -fdiagnostics-color 45 | AM_CPPFLAGS = -D_GNU_SOURCE -Iinclude -Iinternal $(TRDB_LINKER_INCLUDES) -D_GLIBCXX_ASSERTIONS 46 | 47 | # workaround for shared source files 48 | # https://www.gnu.org/software/automake/manual/html_node/Objects-created-both-with-libtool-and-without.html 49 | trdb_CFLAGS = $(AM_CFLAGS) 50 | tests_CFLAGS = $(AM_CFLAGS) 51 | benchmarks_CFLAGS = $(AM_CFLAGS) 52 | 53 | doxygen-doc: doxyfile 54 | doxygen doxyfile 55 | 56 | # patched spike to produce traces (32-bit and 64-bit) 57 | define spike_template = 58 | 59 | # we use sed to cutoff the internal boot sequence of spike. Alternatively we 60 | # could add this code to the binary but this is easier. 61 | spike-traces-$(1): riscv-tests-$(1)/benchmarks/build.ok spike-$(1) 62 | mkdir -p riscv-traces-$(1) 63 | for benchmark in riscv-tests-$(1)/benchmarks/*.riscv; do \ 64 | ./trdb-spike-$(1) \ 65 | --ust-trace=riscv-traces-$(1)/$$$$(basename $$$$benchmark).cvs \ 66 | $$$$benchmark; \ 67 | sed -i 2,6d riscv-traces-$(1)/$$$$(basename $$$$benchmark).cvs; \ 68 | cp $$$$benchmark riscv-traces-$(1)/$$$$(basename $$$$benchmark); \ 69 | done 70 | 71 | spike-$(1): riscv-isa-sim-$(1)/build.ok riscv-fesvr/build.ok 72 | 73 | riscv-isa-sim-$(1)/build.ok: riscv-fesvr/build.ok 74 | rm -rf riscv-isa-sim-$(1) 75 | git clone https://github.com/pulp-platform/riscv-isa-sim riscv-isa-sim-$(1) -b spike_trace_path 76 | cd riscv-isa-sim-$(1) && \ 77 | LDFLAGS="-L../riscv-fesvr" ./configure --with-isa=rv$(1)imc 78 | cd riscv-isa-sim-$(1) && \ 79 | ln -s ../riscv-fesvr/fesvr . && \ 80 | $(MAKE) && \ 81 | touch build.ok 82 | cd .. 83 | echo "#!/usr/bin/env bash" > trdb-spike-$(1) 84 | echo "LD_LIBRARY_PATH=./riscv-isa-sim-$(1):./riscv-fesvr ./riscv-isa-sim-$(1)/spike \"\$$$$@\"" \ 85 | >> trdb-spike-$(1) 86 | chmod u+x trdb-spike-$(1) 87 | 88 | test-trdb-$(1): 89 | echo "#!/usr/bin/env bash" > trdb-spike-$(1) 90 | echo "LD_LIBRARY_PATH=./riscv-isa-sim-$(1):./riscv-fesvr ./riscv-isa-sim-$(1)/spike \"\$$$$@\"" \ 91 | >> trdb-spike-$(1) 92 | chmod u+x trdb-spike-$(1) 93 | 94 | # riscv-benchmark-tests 95 | # have your $RISCV point to your compiler 96 | riscv-tests-$(1)/benchmarks/build.ok: 97 | rm -rf riscv-tests-$(1) 98 | git clone https://github.com/riscv/riscv-tests/ --recursive riscv-tests-$(1) 99 | cd riscv-tests-$(1) && git checkout ea85805 --recurse-submodules 100 | cd riscv-tests-$(1) && ./configure --with-xlen=$(1) 101 | cd riscv-tests-$(1) && $(MAKE) benchmarks RISCV_GCC_OPTS="-DPREALLOCATE=1 -mcmodel=medany -static -std=gnu99 -O2 -ffast-math -fno-common -fno-builtin-printf -fno-tree-loop-distribute-patterns -march=rv$(1)gcv -mabi=ilp32" 102 | cd riscv-tests-$(1) && touch build.ok 103 | endef 104 | 105 | 106 | $(eval $(call spike_template,32)) 107 | $(eval $(call spike_template,64)) 108 | 109 | 110 | riscv-fesvr/build.ok: 111 | rm -rf riscv-fesvr 112 | git clone https://github.com/riscv/riscv-fesvr.git riscv-fesvr 113 | cd riscv-fesvr && ./configure && $(MAKE) && touch build.ok 114 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * TRDB -- Library and CLI Tools for RISC-V Processor Tracing 2 | This library provides software tooling for decompressing, compressing and 3 | disassembling captured execution traces of a RISC-V Core and is compliant with 4 | the [[https://github.com/riscv/riscv-trace-spec][RISC-V Processor Trace Specification]]. It is used together with the 5 | [[https://github.com/pulp-platform/trace_debugger][hardware trace module]] of the PULP project. 6 | 7 | ** Features 8 | =libtrdb= provides routines that can be used to 9 | + decompress trace packets 10 | + disassemble traces 11 | + model a trace encoding source (processor interface to trace packets) 12 | + used as part of a RTL testbench through DPI 13 | 14 | The =trdb= command line tool is used to provide a command line interface to 15 | =libtrdb= functionalities. 16 | 17 | The trace source model accepts a stimuli/trace file and produces the sequence 18 | of packages that the [[https://github.com/pulp-platform/trace_debugger][PULP RTL]] IP would. An example of how such a trace file 19 | looks like can be found in =data/trdb_stimuli=. This is what the RISC-V core 20 | sends to the PULP hardware trace debugger each cycle. It is used by the 21 | testbench of the PULP hardware tracer in by employing via the DPI. 22 | 23 | The =trdb= binary provides functionality to decompress recorded packages and 24 | to compress stimuli/trace files. 25 | 26 | ** Build 27 | To build =trdb=, =tests= and =libtrdb.a= call 28 | 29 | #+BEGIN_SRC bash 30 | autoreconf -vif 31 | ./configure 32 | make 33 | #+END_SRC 34 | 35 | or if you want a debug build (debug symbols, asserts, address sanitizer, debug level logging, no 36 | optimizations) use the helper script 37 | 38 | #+BEGIN_SRC bash 39 | ./configure-debug 40 | #+END_SRC 41 | 42 | instead of =./configure=. 43 | 44 | =libtrdb.so= is a library for providing the C-model, decompression, 45 | disassembly functionality and a simple interface using the SystemVerilog DPI. 46 | 47 | Run 48 | 49 | #+BEGIN_SRC bash 50 | make doxygen-doc 51 | #+END_SRC 52 | 53 | to build the documentation with doxygen. It will be put under =doc/=. 54 | 55 | ** Usage of =trdb= 56 | *** Compression 57 | =./trdb --compress TRACE-FILE= to compress the instruction sequence given in 58 | =TRACE-FILE= to a sequence of packets printed in a human readable format to 59 | stdout. Add =--binary-format pulp= to produce binary data matching the PULP 60 | hardware trace debugger output. 61 | 62 | *** Decompression 63 | By calling =./trdb --extract --bfd ELF-BINARY PULP-BINARY-PACKETS= you can 64 | reconstruct the original instruction sequence, which was generated by 65 | running =ELF-BINARY=, from a packet sequence =PULP-BINARY-PACKETS=. The 66 | output will be a dump of the instruction information which could be 67 | recovered. 68 | 69 | *** Disassembly 70 | Most of the times one is interested in the diassembled instruction sequence. 71 | For that there are the flags =--disassemble= (disassemble code), 72 | =--function-context= (clearly delimit when we are entering a function), 73 | =--line-numbers= (show file names and line numbers) and =--source= (mix 74 | disassembly with source code) which can be added to the decompression 75 | command. Those flags are similar to the ones in GNU objdump. 76 | 77 | For more information about the options, run =trdb --help=. 78 | 79 | *** Example 80 | The file at =data/trdb_stimuli= was produced by running =data/interrupt= on 81 | [[https://github.com/pulp-platform/pulpissimo][PULPissimo]]. It contains the executed instruction sequence and some meta 82 | information. The executable itself is the compiled version of [[https://github.com/pulp-platform/trace_debugger/tree/master/driver/test_interrupt][this]] example. 83 | 84 | Let us first emulate the trace debugger hardware by calling 85 | #+BEGIN_SRC bash 86 | ./trdb --binary-format pulp --compress -o my_packets data/trdb_stimuli 87 | #+END_SRC 88 | The generated packets can be inspected with 89 | #+BEGIN_SRC bash 90 | ./trdb --binary-format pulp --dump my_packets 91 | #+END_SRC 92 | Now we can try to recover the original instruction sequence by running 93 | #+BEGIN_SRC bash 94 | ./trdb --binary-format pulp --bfd data/interrupt --extract my_packets 95 | #+END_SRC 96 | or even better to see some disassembly 97 | #+BEGIN_SRC bash 98 | ./trdb -dSsl --binary-format pulp --bfd data/interrupt --extract my_packets 99 | #+END_SRC 100 | 101 | 102 | ** Usage of =libtrdb= 103 | Always first initialize and hold a =trdb_ctx= struct by calling =trdb_new()=, 104 | which contains the context and state of the trace debugger functions. On 105 | creation it will use some sane configuration settings. 106 | 107 | Feel free to control the logging level by setting either the environment 108 | variable =TRDB_LOG= to err, info or debug before calling =trdb_new()= or 109 | during runtime with =trdb_set_log_priority=. Furthermore you can also hook 110 | your own logging function by setting it with =trdb_set_log_fn=. By default 111 | everything will be printf'd to stderr. 112 | 113 | To run the C-model call =trdb_compress_trace_step= for each cycle and keep 114 | passing in =struct tr_instr= describing the retired instruction of the CPU. 115 | The state of the execution will be recorded in =trdb_ctx=. Generated packet 116 | will be added to the =struct trdb_packet_head= pointer. Such a packet list 117 | must be freed by the user by calling =trdb_free_packet_list=. 118 | 119 | To reset a =trdb_ctx= to its initial state use =trdb_reset_compression= or 120 | =trdb_reset_decompression= depending on your usage. 121 | 122 | Remember to release the library context after you are finished with 123 | =trdb_free=. 124 | 125 | ** Benchmarks 126 | By default a benchmarking executable is built 127 | 128 | Call 129 | #+BEGIN_SRC bash 130 | ./benchmarks 131 | #+END_SRC 132 | 133 | to run the built-in benchmarks. 134 | 135 | ** Tests 136 | Simply run 137 | #+BEGIN_SRC bash 138 | ./tests 139 | #+END_SRC 140 | 141 | ** parse-opcodes 142 | The code in riscv_encoding.h was generated with [[https://github.com/pulp-platform/riscv-opcodes][riscv-opcodes]], a forked 143 | version which also incorporates PULP specific instructions. 144 | -------------------------------------------------------------------------------- /acinclude.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_subdirs_configure.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_SUBDIRS_CONFIGURE( [subdirs], [mandatory arguments], [possibly merged arguments], [replacement arguments], [forbidden arguments]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # AX_SUBDIRS_CONFIGURE attempts to be the equivalent of AC_CONFIG_SUBDIRS 12 | # with customizable options for configure scripts. 13 | # 14 | # Run the configure script for each directory from the comma-separated m4 15 | # list 'subdirs'. This macro can be used multiple times. All arguments of 16 | # this macro must be comma-separated lists. 17 | # 18 | # All command line arguments from the parent configure script will be 19 | # given to the subdirectory configure script after the following 20 | # modifications (in that order): 21 | # 22 | # 1. The arguments from the 'mandatory arguments' list shall always be 23 | # appended to the argument list. 24 | # 25 | # 2. The arguments from the 'possibly merged arguments' list shall be 26 | # added if not present in the arguments of the parent configure script or 27 | # merged with the existing argument otherwise. 28 | # 29 | # 3. The arguments from the 'replacement arguments' list shall be added if 30 | # not present in the arguments of the parent configure script or replace 31 | # the existing argument otherwise. 32 | # 33 | # 4. The arguments from the 'forbidden arguments' list shall always be 34 | # removed from the argument list. 35 | # 36 | # The lists 'mandatory arguments' and 'forbidden arguments' can hold any 37 | # kind of argument. The 'possibly merged arguments' and 'replacement 38 | # arguments' expect their arguments to be of the form --option-name=value. 39 | # 40 | # This macro aims to remain as close as possible to the AC_CONFIG_SUBDIRS 41 | # macro. It corrects the paths for '--srcdir' and adds 42 | # '--disable-option-checking' and '--silent' if necessary. However, it 43 | # does not change the '--cache-file' argument: typically, configure 44 | # scripts run with different arguments will not be able to share the same 45 | # cache. If you wish to share a single cache, you should give an absolute 46 | # path to '--cache-file'. 47 | # 48 | # This macro also sets the output variable subdirs_extra to the list of 49 | # directories recorded with AX_SUBDIRS_CONFIGURE. This variable can be 50 | # used in Makefile rules or substituted in configured files. 51 | # 52 | # This macro shall do nothing more than managing the arguments of the 53 | # configure script. Just like when using AC_CONFIG_SUBDIRS, it is up to 54 | # the user to check any requirements or define and substitute any required 55 | # variable for the remainder of the project. 56 | # 57 | # Configure scripts recorded with AX_SUBDIRS_CONFIGURE may be executed 58 | # before configure scripts recorded with AC_CONFIG_SUBDIRS. 59 | # 60 | # Without additional arguments, the behaviour of AX_SUBDIRS_CONFIGURE 61 | # should be identical to the behaviour of AC_CONFIG_SUBDIRS, apart from 62 | # the contents of the variables subdirs and subdirs_extra (except that 63 | # AX_SUBDIRS_CONFIGURE expects a comma-separated m4 list): 64 | # 65 | # AC_CONFIG_SUBDIRS([something]) 66 | # AX_SUBDIRS_CONFIGURE([something]) 67 | # 68 | # This macro may be called multiple times. 69 | # 70 | # Usage example: 71 | # 72 | # Let us assume our project has 4 dependencies, namely A, B, C and D. Here 73 | # are some characteristics of our project and its dependencies: 74 | # 75 | # - A does not require any special option. 76 | # 77 | # - we want to build B with an optional feature which can be enabled with 78 | # its configure script's option '--enable-special-feature'. 79 | # 80 | # - B's configure script is strange and has an option '--with-B=build'. 81 | # After close inspection of its documentation, we don't want B to receive 82 | # this option. 83 | # 84 | # - C and D both need B. 85 | # 86 | # - Just like our project, C and D can build B themselves with the option 87 | # '--with-B=build'. 88 | # 89 | # - We want C and D to use the B we build instead of building it 90 | # themselves. 91 | # 92 | # Our top-level configure script will be called as follows: 93 | # 94 | # $ --with-A=build --with-B=build --with-C=build \ 95 | # --with-D=build --some-option 96 | # 97 | # Thus we have to make sure that: 98 | # 99 | # - neither B, C or D receive the option '--with-B=build' 100 | # 101 | # - C and D know where to find the headers and libraries of B. 102 | # 103 | # Under those conditions, we can use the AC_CONFIG_SUBDIRS macro for A, 104 | # but need to use AX_SUBDIRS_CONFIGURE for B, C and D: 105 | # 106 | # - B must receive '--enable-special-feature' but cannot receive 107 | # '--with-B=build' 108 | # 109 | # - C and D cannot receive '--with-B=build' (or else it would be built 110 | # thrice) and need to be told where to find B (since we are building it, 111 | # it would probably not be available in standard paths). 112 | # 113 | # Here is a configure.ac snippet that solves our problem: 114 | # 115 | # AC_CONFIG_SUBDIRS([dependencies/A]) 116 | # AX_SUBDIRS_CONFIGURE( 117 | # [dependencies/B], [--enable-special-feature], [], [], 118 | # [--with-B=build]) 119 | # AX_SUBDIRS_CONFIGURE( 120 | # [[dependencies/C],[dependencies/D]], 121 | # [], 122 | # [[CPPFLAGS=-I${ac_top_srcdir}/dependencies/B -I${ac_top_builddir}/dependencies/B], 123 | # [LDFLAGS=-L${ac_abs_top_builddir}/dependencies/B/.libs]], 124 | # [--with-B=system], 125 | # []) 126 | # 127 | # If using automake, the following can be added to the Makefile.am (we use 128 | # both $(subdirs) and $(subdirs_extra) since our example above used both 129 | # AC_CONFIG_SUBDIRS and AX_SUBDIRS_CONFIGURE): 130 | # 131 | # SUBDIRS = $(subdirs) $(subdirs_extra) 132 | # 133 | # LICENSE 134 | # 135 | # Copyright (c) 2017 Harenome Ranaivoarivony-Razanajato 136 | # 137 | # This program is free software; you can redistribute it and/or modify it 138 | # under the terms of the GNU General Public License as published by the 139 | # Free Software Foundation; either version 3 of the License, or (at your 140 | # option) any later version. 141 | # 142 | # This program is distributed in the hope that it will be useful, but 143 | # WITHOUT ANY WARRANTY; without even the implied warranty of 144 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 145 | # Public License for more details. 146 | # 147 | # Under Section 7 of GPL version 3, you are granted additional permissions 148 | # described in the Autoconf Configure Script Exception, version 3.0, as 149 | # published by the Free Software Foundation. 150 | # 151 | # You should have received a copy of the GNU General Public License along 152 | # with this program. If not, see . 153 | 154 | #serial 6 155 | 156 | AC_DEFUN([AX_SUBDIRS_CONFIGURE], 157 | [ 158 | dnl Calls to AC_CONFIG_SUBDIRS perform preliminary steps and build a list 159 | dnl '$subdirs' which is used later by _AC_OUTPUT_SUBDIRS (used by AC_OUTPUT) 160 | dnl to actually run the configure scripts. 161 | dnl This macro performs similar preliminary steps but uses 162 | dnl AC_CONFIG_COMMANDS_PRE to delay the final tasks instead of building an 163 | dnl intermediary list and relying on another macro. 164 | dnl 165 | dnl Since each configure script can get different options, a special variable 166 | dnl named 'ax_sub_configure_args_' is constructed for each 167 | dnl subdirectory. 168 | 169 | # Various preliminary checks. 170 | AC_REQUIRE([AC_DISABLE_OPTION_CHECKING]) 171 | AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) 172 | AS_LITERAL_IF([$1], [], 173 | [AC_DIAGNOSE([syntax], [$0: you should use literals])]) 174 | 175 | m4_foreach(subdir_path, [$1], 176 | [ 177 | ax_dir="subdir_path" 178 | 179 | dnl Build the argument list in a similar fashion to AC_CONFIG_SUBDIRS. 180 | dnl A few arguments found in the final call to the configure script are not 181 | dnl added here because they rely on variables that may not yet be available 182 | dnl (see below the part that is similar to _AC_OUTPUT_SUBDIRS). 183 | # Do not complain, so a configure script can configure whichever parts of a 184 | # large source tree are present. 185 | if test -d "$srcdir/$ax_dir"; then 186 | _AC_SRCDIRS(["$ax_dir"]) 187 | # Remove --cache-file, --srcdir, and --disable-option-checking arguments 188 | # so they do not pile up. 189 | ax_args= 190 | ax_prev= 191 | eval "set x $ac_configure_args" 192 | shift 193 | for ax_arg; do 194 | if test -n "$ax_prev"; then 195 | ax_prev= 196 | continue 197 | fi 198 | case $ax_arg in 199 | -cache-file | --cache-file | --cache-fil | --cache-fi | --cache-f \ 200 | | --cache- | --cache | --cach | --cac | --ca | --c) 201 | ax_prev=cache_file ;; 202 | -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ 203 | | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* \ 204 | | --c=*) 205 | ;; 206 | --config-cache | -C) 207 | ;; 208 | -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) 209 | ax_prev=srcdir ;; 210 | -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) 211 | ;; 212 | -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) 213 | ax_prev=prefix ;; 214 | -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* \ 215 | | --p=*) 216 | ;; 217 | --disable-option-checking) 218 | ;; 219 | *) case $ax_arg in 220 | *\'*) ax_arg=$(AS_ECHO(["$ax_arg"]) | sed "s/'/'\\\\\\\\''/g");; 221 | esac 222 | AS_VAR_APPEND([ax_args], [" '$ax_arg'"]) ;; 223 | esac 224 | done 225 | # Always prepend --disable-option-checking to silence warnings, since 226 | # different subdirs can have different --enable and --with options. 227 | ax_args="--disable-option-checking $ax_args" 228 | # Options that must be added as they are provided. 229 | m4_ifnblank([$2], [m4_foreach(opt, [$2], [AS_VAR_APPEND(ax_args, " 'opt'") 230 | ])]) 231 | # New options that may need to be merged with existing options. 232 | m4_ifnblank([$3], [m4_foreach(opt, [$3], 233 | [ax_candidate="opt" 234 | ax_candidate_flag="${ax_candidate%%=*}" 235 | ax_candidate_content="${ax_candidate#*=}" 236 | if test "x$ax_candidate" != "x" -a "x$ax_candidate_flag" != "x"; then 237 | if echo "$ax_args" | grep -- "${ax_candidate_flag}=" >/dev/null 2>&1; then 238 | [ax_args=$(echo $ax_args | sed "s,\(${ax_candidate_flag}=[^']*\),\1 ${ax_candidate_content},")] 239 | else 240 | AS_VAR_APPEND(ax_args, " 'opt'") 241 | fi 242 | fi 243 | ])]) 244 | # New options that must replace existing options. 245 | m4_ifnblank([$4], [m4_foreach(opt, [$4], 246 | [ax_candidate="opt" 247 | ax_candidate_flag="${ax_candidate%%=*}" 248 | ax_candidate_content="${ax_candidate#*=}" 249 | if test "x$ax_candidate" != "x" -a "x$ax_candidate_flag" != "x"; then 250 | if echo "$ax_args" | grep -- "${ax_candidate_flag}=" >/dev/null 2>&1; then 251 | [ax_args=$(echo $ax_args | sed "s,${ax_candidate_flag}=[^']*,${ax_candidate},")] 252 | else 253 | AS_VAR_APPEND(ax_args, " 'opt'") 254 | fi 255 | fi 256 | ])]) 257 | # Options that must be removed. 258 | m4_ifnblank([$5], [m4_foreach(opt, [$5], [ax_args=$(echo $ax_args | sed "s,'opt',,") 259 | ])]) 260 | AS_VAR_APPEND([ax_args], [" '--srcdir=$ac_srcdir'"]) 261 | 262 | # Add the subdirectory to the list of target subdirectories. 263 | ax_subconfigures="$ax_subconfigures $ax_dir" 264 | # Save the argument list for this subdirectory. 265 | dnl $1 is a path to some subdirectory: m4_bpatsubsts() is used to convert 266 | dnl $1 into a valid shell variable name. 267 | dnl For instance, "ax_sub_configure_args_path/to/subdir" becomes 268 | dnl "ax_sub_configure_args_path_to_subdir". 269 | ax_var=$(printf "$ax_dir" | tr -c "0-9a-zA-Z_" "_") 270 | eval "ax_sub_configure_args_$ax_var=\"$ax_args\"" 271 | eval "ax_sub_configure_$ax_var=\"yes\"" 272 | else 273 | AC_MSG_WARN([could not find source tree for $ax_dir]) 274 | fi 275 | 276 | dnl Add some more arguments to the argument list and then actually run the 277 | dnl configure script. This is mostly what happens in _AC_OUTPUT_SUBDIRS 278 | dnl except it does not iterate over an intermediary list. 279 | AC_CONFIG_COMMANDS_PRE( 280 | dnl This very line cannot be quoted! m4_foreach has some work here. 281 | ax_dir="subdir_path" 282 | [ 283 | # Convert the path to the subdirectory into a shell variable name. 284 | ax_var=$(printf "$ax_dir" | tr -c "0-9a-zA-Z_" "_") 285 | ax_configure_ax_var=$(eval "echo \"\$ax_sub_configure_$ax_var\"") 286 | if test "$no_recursion" != "yes" -a "x$ax_configure_ax_var" = "xyes"; then 287 | AC_SUBST([subdirs_extra], ["$subdirs_extra $ax_dir"]) 288 | ax_msg="=== configuring in $ax_dir ($(pwd)/$ax_dir)" 289 | _AS_ECHO_LOG([$ax_msg]) 290 | _AS_ECHO([$ax_msg]) 291 | AS_MKDIR_P(["$ax_dir"]) 292 | _AC_SRCDIRS(["$ax_dir"]) 293 | 294 | ax_popdir=$(pwd) 295 | cd "$ax_dir" 296 | 297 | # Check for guested configure; otherwise get Cygnus style configure. 298 | if test -f "$ac_srcdir/configure.gnu"; then 299 | ax_sub_configure=$ac_srcdir/configure.gnu 300 | elif test -f "$ac_srcdir/configure"; then 301 | ax_sub_configure=$ac_srcdir/configure 302 | elif test -f "$ac_srcdir/configure.in"; then 303 | # This should be Cygnus configure. 304 | ax_sub_configure=$ac_aux_dir/configure 305 | else 306 | AC_MSG_WARN([no configuration information is in $ax_dir]) 307 | ax_sub_configure= 308 | fi 309 | 310 | if test -n "$ax_sub_configure"; then 311 | # Get the configure arguments for the current configure. 312 | eval "ax_sub_configure_args=\"\$ax_sub_configure_args_${ax_var}\"" 313 | 314 | # Always prepend --prefix to ensure using the same prefix 315 | # in subdir configurations. 316 | ax_arg="--prefix=$prefix" 317 | case $ax_arg in 318 | *\'*) ax_arg=$(AS_ECHO(["$ax_arg"]) | sed "s/'/'\\\\\\\\''/g");; 319 | esac 320 | ax_sub_configure_args="'$ax_arg' $ax_sub_configure_args" 321 | if test "$silent" = yes; then 322 | ax_sub_configure_args="--silent $ax_sub_configure_args" 323 | fi 324 | 325 | AC_MSG_NOTICE([running $SHELL $ax_sub_configure $ax_sub_configure_args --cache-file=$cache_file]) 326 | eval "\$SHELL \"$ax_sub_configure\" $ax_sub_configure_args --cache-file=\"$cache_file\"" \ 327 | || AC_MSG_ERROR([$ax_sub_configure failed for $ax_dir]) 328 | fi 329 | 330 | cd "$ax_popdir" 331 | fi 332 | ]) 333 | ]) 334 | ]) 335 | -------------------------------------------------------------------------------- /benchmark/benchmarks.c: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Trace Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /* 21 | * Author: Robert Balas (balasr@student.ethz.ch) 22 | * Description: Run benchmarks for trdb 23 | */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "utils.h" 33 | #include "trace_debugger.h" 34 | #include "disassembly.h" 35 | #include "serialize.h" 36 | 37 | struct result { 38 | char *name; 39 | size_t instrcnt; 40 | size_t packetcnt; 41 | size_t payload; 42 | double bpi; 43 | }; 44 | 45 | static char *conv_tf(bool b) 46 | { 47 | return b ? "true" : "false"; 48 | } 49 | 50 | static double percent(size_t a, size_t total) 51 | { 52 | return (double)a / total * 100; 53 | } 54 | 55 | int compress_cvs_trace(const char *trace_path, struct result *comparison) 56 | { 57 | int status = 0; 58 | struct trdb_ctx *ctx = trdb_new(); 59 | struct disassembler_unit dunit = {0}; 60 | struct disassemble_info dinfo = {0}; 61 | dunit.dinfo = &dinfo; 62 | 63 | trdb_init_disassembler_unit_for_pulp(&dunit, NULL); 64 | 65 | struct trdb_packet_head packet_list; 66 | TAILQ_INIT(&packet_list); 67 | struct trdb_instr_head instr_list; 68 | TAILQ_INIT(&instr_list); 69 | 70 | if (!ctx) { 71 | fprintf(stderr, "Library context allocation failed.\n"); 72 | status = -1; 73 | goto fail; 74 | } 75 | bool full_address = false; 76 | bool implicit_ret = true; 77 | bool compress_bmap = true; 78 | 79 | trdb_set_dunit(ctx, &dunit); 80 | trdb_set_full_address(ctx, full_address); 81 | trdb_set_pulp_extra_packet(ctx, false); 82 | trdb_set_implicit_ret(ctx, implicit_ret); 83 | trdb_set_compress_branch_map(ctx, compress_bmap); 84 | 85 | size_t instrcnt = 0; 86 | status = trdb_cvs_to_trace_list(ctx, trace_path, &instr_list, &instrcnt); 87 | if (status < 0) { 88 | fprintf(stderr, "CVS to tr_instr failed\n"); 89 | status = -1; 90 | goto fail; 91 | } 92 | 93 | struct tr_instr *instr; 94 | TAILQ_FOREACH (instr, &instr_list, list) { 95 | int step = trdb_compress_trace_step_add(ctx, &packet_list, instr); 96 | if (step == -1) { 97 | fprintf(stderr, "Compress trace failed\n"); 98 | status = -1; 99 | goto fail; 100 | } 101 | } 102 | struct trdb_packet_stats stats = {0}; 103 | trdb_get_packet_stats(ctx, &stats); 104 | 105 | size_t packets = trdb_get_packetcnt(ctx); 106 | size_t payloadbits = trdb_get_payloadbits(ctx); 107 | size_t instrs = trdb_get_instrcnt(ctx); 108 | size_t pulpbits = trdb_get_pulpbits(ctx); 109 | size_t exception_packets = stats.exception_packets; 110 | size_t addr_only_packets = stats.addr_only_packets; 111 | size_t start_packets = stats.start_packets; 112 | size_t diff_packets = stats.diff_packets; 113 | size_t abs_packets = stats.abs_packets; 114 | size_t bmap_full_packets = stats.bmap_full_packets; 115 | size_t bmap_full_addr_packets = stats.bmap_full_addr_packets; 116 | 117 | /* compare also against the version which doesn't use address and branchmap 118 | * compression 119 | */ 120 | trdb_reset_compression(ctx); 121 | 122 | trdb_set_full_address(ctx, true); 123 | trdb_set_pulp_extra_packet(ctx, false); 124 | trdb_set_implicit_ret(ctx, false); 125 | trdb_set_compress_branch_map(ctx, false); 126 | 127 | TAILQ_FOREACH (instr, &instr_list, list) { 128 | int step = trdb_compress_trace_step_add(ctx, &packet_list, instr); 129 | if (step == -1) { 130 | fprintf(stderr, "Compress trace failed\n"); 131 | status = -1; 132 | goto fail; 133 | } 134 | } 135 | 136 | size_t noc_instrs = trdb_get_instrcnt(ctx); 137 | size_t noc_payloadbits = trdb_get_payloadbits(ctx); 138 | 139 | printf("Name of test: %s\n", trace_path); 140 | if (comparison->instrcnt != instrcnt) 141 | printf("Number of instruction mismatch! %zu, %zu\n", 142 | comparison->instrcnt, instrcnt); 143 | printf("no ret packet count:%zu\n", trdb_get_packetcnt(ctx)); 144 | printf( 145 | "pulp settings: full address: %s / implicit return: %s / compress full branch map: %s\n", 146 | conv_tf(full_address), conv_tf(implicit_ret), conv_tf(compress_bmap)); 147 | printf("pulp instructions: %zu, packets: %zu, payload bytes: %zu\n", 148 | instrcnt, packets, payloadbits / 8); 149 | printf("ultra instructions: %zu, packets: %zu, payload bytes: %zu\n", 150 | comparison->instrcnt, comparison->packetcnt, comparison->payload); 151 | printf("pulp packet stats: diff: %zu (%2.2lf%%) / abs: %zu (%2.2lf%%) / " 152 | "addr_only: %zu (%2.2lf%%) \n bmap_full: %zu (%2.2lf%%) / " 153 | "bmap_full_with_addr: %zu (%2.2lf%%)\n start: %zu (%2.2lf%%) / " 154 | "exception: %zu (%2.2lf%%)\n", 155 | diff_packets, percent(diff_packets, packets), abs_packets, 156 | percent(abs_packets, packets), addr_only_packets, 157 | percent(addr_only_packets, packets), bmap_full_packets, 158 | percent(bmap_full_packets, packets), bmap_full_addr_packets, 159 | percent(bmap_full_addr_packets, packets), start_packets, 160 | percent(start_packets, packets), exception_packets, 161 | percent(exception_packets, packets)); 162 | double bpi_noc_payload = noc_payloadbits / (double)noc_instrs; 163 | double bpi_payload = payloadbits / (double)instrs; 164 | double bpi_full = (payloadbits + packets * 6) / (double)instrs; 165 | double bpi_pulp = (pulpbits / (double)instrs); 166 | printf("Bits per instruction (ultra ): %lf\n", comparison->bpi); 167 | printf("Bits per instruction (payload no compr): %lf " 168 | "(%+2.lf%% vs ultra)\n", 169 | bpi_noc_payload, (bpi_noc_payload / comparison->bpi * 100 - 100)); 170 | printf("Bits per instruction (payload ): %lf " 171 | "(%+2.lf%% vs ultra)\n", 172 | bpi_payload, (bpi_payload / comparison->bpi) * 100 - 100); 173 | printf("Bits per instruction (payload + header): %lf " 174 | "(%+2.lf%% vs payload)\n", 175 | bpi_full, bpi_full / bpi_payload * 100 - 100); 176 | printf("Bits per instruction (pulp ): %lf " 177 | "(%+2.lf%% vs payload + header)\n", 178 | bpi_pulp, bpi_pulp / bpi_full * 100 - 100); 179 | printf("\n"); 180 | 181 | fail: 182 | trdb_free_packet_list(&packet_list); 183 | trdb_free_instr_list(&instr_list); 184 | trdb_free(ctx); 185 | return status; 186 | } 187 | 188 | int main() 189 | { 190 | int status = EXIT_SUCCESS; 191 | 192 | struct result cmp_results[] = {{"dhyrstone", 215015, 1308, 5628, 0.209}, 193 | {"median", 15015, 207, 810, 0.432}, 194 | {"mm", 297038, 644, 2011, 0.054}, 195 | {"mt-matmul", 41454, 344, 953, 0.184}, 196 | {"mt-vvadd", 61072, 759, 2049, 0.268}, 197 | {"multiply", 55016, 546, 1837, 0.267}, 198 | {"pmp", 425, 7, 39, 0.734}, 199 | {"qsort", 235015, 2052, 8951, 0.305}, 200 | {"rsort", 375016, 683, 2077, 0.044}, 201 | {"spmv", 70015, 254, 1154, 0.132}, 202 | {"towers", 15016, 72, 237, 0.126}, 203 | {"vvadd", 10016, 111, 316, 0.252}}; 204 | 205 | const char *tv_cvs[] = { 206 | "data/cvs/dhrystone.spike_trace", "data/cvs/median.spike_trace", 207 | "data/cvs/mm.spike_trace", "data/cvs/mt-matmul.spike_trace", 208 | "data/cvs/mt-vvadd.spike_trace", "data/cvs/multiply.spike_trace", 209 | "data/cvs/pmp.spike_trace", "data/cvs/qsort.spike_trace", 210 | "data/cvs/rsort.spike_trace", "data/cvs/spmv.spike_trace", 211 | "data/cvs/towers.spike_trace", "data/cvs/vvadd.spike_trace"}; 212 | 213 | for (unsigned j = 0; j < TRDB_ARRAY_SIZE(tv_cvs); j++) { 214 | const char *stim = tv_cvs[j]; 215 | if (access(stim, R_OK)) { 216 | fprintf(stderr, "File not found, skipping test at %s\n", stim); 217 | continue; 218 | } 219 | if (compress_cvs_trace(stim, cmp_results + j)) 220 | status = EXIT_FAILURE; 221 | } 222 | 223 | const char *mibench_cvs[] = { 224 | "data/mibench_cvs/cjpeg.trace", "data/mibench_cvs/djpeg.trace", 225 | "data/fft.trace", "data/mibench_cvs/rijndael.trace", 226 | "data/mibench_cvs/sha.trace"}; 227 | 228 | struct result dummy = {"dhyrstone", 1, 1, 1, 1}; 229 | for (unsigned j = 0; j < TRDB_ARRAY_SIZE(mibench_cvs); j++) { 230 | const char *stim = mibench_cvs[j]; 231 | if (access(stim, R_OK)) { 232 | fprintf(stderr, "File not found, skipping test at %s\n", stim); 233 | continue; 234 | } 235 | if (compress_cvs_trace(stim, &dummy)) 236 | status = EXIT_FAILURE; 237 | } 238 | 239 | return status; 240 | } 241 | -------------------------------------------------------------------------------- /ci/Jenkinsfile: -------------------------------------------------------------------------------- 1 | //def labels = ['trdb-gcc-centos', 'trdb-clang-centos', 'trdb-gcc-ubuntu', 'trdb-clang-ubuntu'] 2 | //def cc_envs = ['CC=gcc', 'CC=clang', 'CC=gcc', 'CC=clang'] 3 | //def os_envs = ['centos7', 'centos7', 'ubuntu', 'ubuntu'] 4 | def labels = ['trdb-gcc-centos', 'trdb-clang-centos'] 5 | def cc_envs = ['CC=gcc', 'CC=clang'] 6 | def os_envs = ['centos7', 'centos7'] 7 | 8 | def builders = [:] 9 | 10 | for (int i = 0; i < labels.size(); i++) { 11 | def label = labels[i] 12 | def cc_env = cc_envs[i] 13 | def os_env = os_envs[i] 14 | 15 | // Create a map to pass in to the 'parallel' step so we can fire all the builds at once 16 | builders[label] = { 17 | node (os_env && ' && !vm1-centos7'){ 18 | try { 19 | stage('Preparation of ' + label) { 20 | withEnv(['SPIKE_TEST_TRACES_URL=https://github.com/bluewww/spike-instruction-trace-patch', 21 | 'PATH+WHATEVER=/home/balasr/.local/bin:/home/balasr/.riscv/bin']) { 22 | git 'https://github.com/pulp-platform/trdb/' 23 | //sh "./ci/get-cvs-stimuli.sh" 24 | sh "make riscv-fesvr/build.ok" 25 | 26 | def steps = [:] 27 | steps["spike-traces-32"] = { 28 | sh "make spike-traces-32" 29 | } 30 | steps["spike-traces-64"] = { 31 | sh "make spike-traces-64" 32 | } 33 | parallel steps 34 | } 35 | } 36 | stage('Build (32-bit) of ' + label) { 37 | if (isUnix()) { 38 | withEnv([cc_env]) { 39 | sh "make all" 40 | } 41 | } else { 42 | echo "unix only" 43 | } 44 | } 45 | stage('Test (32-bit) of ' + label) { 46 | sh "make test" 47 | } 48 | stage('Build (64-bit) of ' + label) { 49 | if (isUnix()) { 50 | withEnv([cc_env]) { 51 | sh "make clean all CFLAGS=-DTRDB_ARCH64" 52 | } 53 | } else { 54 | echo "unix only" 55 | } 56 | } 57 | stage('Test (64-bit) of ' + label) { 58 | sh "make test" 59 | } 60 | } catch (e) { 61 | currentBuild.result = "FAILED" 62 | echo "SENDING E-MAIL" 63 | notifyFailed() 64 | throw e 65 | } 66 | } 67 | } 68 | } 69 | 70 | def notifyFailed() { 71 | emailext ( 72 | subject: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'", 73 | body: """

FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]':

74 |

Check console output at "${env.JOB_NAME} [${env.BUILD_NUMBER}]"

""", 75 | recipientProviders: [[$class: 'DevelopersRecipientProvider']] 76 | ) 77 | } 78 | 79 | parallel builders 80 | -------------------------------------------------------------------------------- /ci/gen-cvs-stimuli.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 5 | 6 | cd ${ROOT} 7 | make spike-traces-32 8 | make spike-traces-64 9 | -------------------------------------------------------------------------------- /ci/get-cvs-stimuli.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 5 | 6 | git clone "$SPIKE_TEST_TRACES_URL" ${ROOT}/spike-traces 7 | 8 | mkdir ${ROOT}/data/cvs 9 | 10 | cp ${ROOT}/spike-traces/* ${ROOT}/data/cvs/ 11 | -------------------------------------------------------------------------------- /ci/get-gcc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | VERSION="bb41926cb5a62e6cbe4b659ded6ff52c70b2baf1" 6 | 7 | 8 | mkdir -p $RISCV/ 9 | 10 | cd $RISCV 11 | 12 | if [ -z ${NUM_JOBS} ]; then 13 | NUM_JOBS=1 14 | fi 15 | 16 | if ! [ -e $RISCV/bin/riscv32-unknown-elf-gcc ]; then 17 | if ! [ -e $RISCV/riscv-gnu-toolchain ]; then 18 | git clone --recursive https://github.com/riscv/riscv-gnu-toolchain.git 19 | fi 20 | 21 | cd riscv-gnu-toolchain 22 | git checkout $VERSION 23 | git submodule update --init --recursive 24 | 25 | if [[ $1 -ne "0" || -z ${1} ]]; then 26 | echo "Compiling RISC-V Toolchain" 27 | ./configure --prefix=$RISCV --with-arch=rv32gc --with-abi=ilp32 28 | make -j${NUM_JOBS} 29 | make install 30 | echo "Compilation Finished" 31 | fi 32 | fi 33 | -------------------------------------------------------------------------------- /ci/get-pulp-rt-stimuli.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | -------------------------------------------------------------------------------- /ci/run-cli-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 5 | 6 | cd ${ROOT}/test 7 | ./test_trdb_diff.sh > diff_out 8 | ./test_trdb.sh > normal_out 9 | diff <(tail diff_out) <(tail trdb_diff_expected) 10 | diff <(tail normal_out) <(tail trdb_normal_expected) 11 | -------------------------------------------------------------------------------- /ci/run-static-analyzer.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 2 | set -e 3 | 4 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 5 | 6 | "$SCAN_BUILD" make -C ${ROOT} clean all 7 | -------------------------------------------------------------------------------- /ci/run-tests-32.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 5 | 6 | # 32-bit tests 7 | make -C ${ROOT} clean all test 8 | 9 | ./run-cli-tests.sh 10 | -------------------------------------------------------------------------------- /ci/run-tests-64.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 5 | 6 | # 64-bit tests 7 | make -C ${ROOT} clean all test CFLAGS=-DTRDB_ARCH64 8 | -------------------------------------------------------------------------------- /configure-debug: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | ./configure CFLAGS="-Og -g -fno-strict-aliasing -DENABLE_LOGGING -DENABLE_DEBUG -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=leak" 3 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl SPDX-License-Identifier: Apache-2.0 2 | dnl Author: Robert Balas 3 | AC_INIT([trdb], [0.2], [balasr@iis.ee.ethz.ch]) 4 | AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) 5 | AC_PROG_CC 6 | AC_PROG_RANLIB 7 | AM_PROG_AR 8 | LT_INIT 9 | AC_CONFIG_HEADERS([config.h]) 10 | AC_CONFIG_FILES([ 11 | Makefile 12 | ]) 13 | AX_SUBDIRS_CONFIGURE([lib/riscv-binutils-gdb/bfd], [--enable-targets=riscv32, --with-pic]) 14 | AX_SUBDIRS_CONFIGURE([lib/riscv-binutils-gdb/opcodes], [--enable-targets=riscv32, --with-pic]) 15 | AX_SUBDIRS_CONFIGURE([lib/riscv-binutils-gdb/libiberty], [], [[CFLAGS=-O2 -g -fpic]]) 16 | AX_SUBDIRS_CONFIGURE([lib/riscv-binutils-gdb/zlib], [], [[CFLAGS=-O2 -g -fpic]]) 17 | AC_OUTPUT 18 | -------------------------------------------------------------------------------- /data/interrupt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulp-platform/trdb/621656e06ed79760bf57f077b0830c68a074ba86/data/interrupt -------------------------------------------------------------------------------- /data/trdb_packets: -------------------------------------------------------------------------------- 1 | PACKET 3: F_SYNC 2 | subformat : SF_START 3 | context : 4 | privilege : 0x7 5 | branch : false 6 | address : 0x1c008080 7 | PACKET 0: F_BRANCH_FULL 8 | branches : 0 9 | branch_map: 0x0 10 | address : 0x0 11 | PACKET 0: F_BRANCH_FULL 12 | branches : 9 13 | branch_map: 0x100 14 | address : 0x1c009218 15 | PACKET 0: F_BRANCH_FULL 16 | branches : 3 17 | branch_map: 0x3 18 | address : 0x1c009228 19 | PACKET 0: F_BRANCH_FULL 20 | branches : 3 21 | branch_map: 0x6 22 | address : 0x1c0094ea 23 | PACKET 2: F_ADDR_ONLY 24 | address : 0x1c009238 25 | PACKET 2: F_ADDR_ONLY 26 | address : 0x1c008ad0 27 | PACKET 0: F_BRANCH_FULL 28 | branches : 1 29 | branch_map: 0x1 30 | address : 0x1c008ad6 31 | PACKET 2: F_ADDR_ONLY 32 | address : 0x1c008ad8 33 | PACKET 0: F_BRANCH_FULL 34 | branches : 1 35 | branch_map: 0x1 36 | address : 0x1c00903a 37 | PACKET 0: F_BRANCH_FULL 38 | branches : 1 39 | branch_map: 0x1 40 | address : 0x1c009052 41 | PACKET 0: F_BRANCH_FULL 42 | branches : 1 43 | branch_map: 0x1 44 | address : 0x1c008ada 45 | PACKET 0: F_BRANCH_FULL 46 | branches : 1 47 | branch_map: 0x0 48 | address : 0x1c008d5c 49 | PACKET 2: F_ADDR_ONLY 50 | address : 0x1c008db8 51 | PACKET 2: F_ADDR_ONLY 52 | address : 0x1c008b24 53 | PACKET 0: F_BRANCH_FULL 54 | branches : 1 55 | branch_map: 0x0 56 | address : 0x1c008f76 57 | PACKET 0: F_BRANCH_FULL 58 | branches : 6 59 | branch_map: 0xc 60 | address : 0x1c009002 61 | PACKET 2: F_ADDR_ONLY 62 | address : 0x1c008e5c 63 | PACKET 0: F_BRANCH_FULL 64 | branches : 4 65 | branch_map: 0xd 66 | address : 0x1c008b24 67 | PACKET 0: F_BRANCH_FULL 68 | branches : 1 69 | branch_map: 0x0 70 | address : 0x1c0091d0 71 | PACKET 0: F_BRANCH_FULL 72 | branches : 18 73 | branch_map: 0x20000 74 | address : 0x1c008b24 75 | PACKET 0: F_BRANCH_FULL 76 | branches : 1 77 | branch_map: 0x0 78 | address : 0x1c009252 79 | PACKET 2: F_ADDR_ONLY 80 | address : 0x1c008b24 81 | PACKET 0: F_BRANCH_FULL 82 | branches : 1 83 | branch_map: 0x0 84 | address : 0x1c00925c 85 | PACKET 2: F_ADDR_ONLY 86 | address : 0x1c008b24 87 | PACKET 0: F_BRANCH_FULL 88 | branches : 1 89 | branch_map: 0x0 90 | address : 0x1c00928a 91 | PACKET 0: F_BRANCH_FULL 92 | branches : 1 93 | branch_map: 0x1 94 | address : 0x1c0092b0 95 | PACKET 0: F_BRANCH_FULL 96 | branches : 4 97 | branch_map: 0x3 98 | address : 0x1c009002 99 | PACKET 2: F_ADDR_ONLY 100 | address : 0x1c008c26 101 | PACKET 0: F_BRANCH_FULL 102 | branches : 1 103 | branch_map: 0x1 104 | address : 0x1c0092cc 105 | PACKET 0: F_BRANCH_FULL 106 | branches : 4 107 | branch_map: 0x3 108 | address : 0x1c009002 109 | PACKET 2: F_ADDR_ONLY 110 | address : 0x1c008c26 111 | PACKET 0: F_BRANCH_FULL 112 | branches : 1 113 | branch_map: 0x1 114 | address : 0x1c0092dc 115 | PACKET 0: F_BRANCH_FULL 116 | branches : 1 117 | branch_map: 0x0 118 | address : 0x1c008b24 119 | PACKET 0: F_BRANCH_FULL 120 | branches : 1 121 | branch_map: 0x0 122 | address : 0x1c009532 123 | PACKET 2: F_ADDR_ONLY 124 | address : 0x1c008b24 125 | PACKET 0: F_BRANCH_FULL 126 | branches : 1 127 | branch_map: 0x0 128 | address : 0x1c009822 129 | PACKET 0: F_BRANCH_FULL 130 | branches : 1 131 | branch_map: 0x0 132 | address : 0x1c008b24 133 | PACKET 0: F_BRANCH_FULL 134 | branches : 1 135 | branch_map: 0x0 136 | address : 0x1c00a0dc 137 | PACKET 0: F_BRANCH_FULL 138 | branches : 4 139 | branch_map: 0x3 140 | address : 0x1c009002 141 | PACKET 2: F_ADDR_ONLY 142 | address : 0x1c008c26 143 | PACKET 0: F_BRANCH_FULL 144 | branches : 1 145 | branch_map: 0x1 146 | address : 0x1c00a0f2 147 | PACKET 0: F_BRANCH_FULL 148 | branches : 4 149 | branch_map: 0x3 150 | address : 0x1c009002 151 | PACKET 2: F_ADDR_ONLY 152 | address : 0x1c008c26 153 | PACKET 0: F_BRANCH_FULL 154 | branches : 1 155 | branch_map: 0x1 156 | address : 0x1c00a104 157 | PACKET 0: F_BRANCH_FULL 158 | branches : 1 159 | branch_map: 0x0 160 | address : 0x1c008b24 161 | PACKET 0: F_BRANCH_FULL 162 | branches : 2 163 | branch_map: 0x3 164 | address : 0x1c008aec 165 | PACKET 0: F_BRANCH_FULL 166 | branches : 2 167 | branch_map: 0x1 168 | address : 0x1c008b00 169 | PACKET 2: F_ADDR_ONLY 170 | address : 0x1c0080be 171 | PACKET 2: F_ADDR_ONLY 172 | address : 0x1c008a52 173 | PACKET 0: F_BRANCH_FULL 174 | branches : 2 175 | branch_map: 0x0 176 | address : 0x1c009194 177 | PACKET 3: F_SYNC 178 | subformat : SF_EXCEPTION 179 | context : 180 | privilege : 0x7 181 | branch : false 182 | address : 0x1c008068 183 | ecause : 0x1a 184 | interrupt : true 185 | tval : 0x0 186 | PACKET 3: F_SYNC 187 | subformat : SF_START 188 | context : 189 | privilege : 0x7 190 | branch : false 191 | address : 0x1c0081be 192 | PACKET 0: F_BRANCH_FULL 193 | branches : 2 194 | branch_map: 0x1 195 | address : 0x1c009198 196 | PACKET 0: F_BRANCH_FULL 197 | branches : 2 198 | branch_map: 0x1 199 | address : 0x1c008a98 200 | PACKET 0: F_BRANCH_FULL 201 | branches : 1 202 | branch_map: 0x0 203 | address : 0x1c008a9e 204 | PACKET 0: F_BRANCH_FULL 205 | branches : 2 206 | branch_map: 0x1 207 | address : 0x1c0097d2 208 | PACKET 0: F_BRANCH_FULL 209 | branches : 3 210 | branch_map: 0x3 211 | address : 0x1c0097dc 212 | PACKET 0: F_BRANCH_FULL 213 | branches : 3 214 | branch_map: 0x3 215 | address : 0x1c0097dc 216 | PACKET 0: F_BRANCH_FULL 217 | branches : 3 218 | branch_map: 0x3 219 | address : 0x1c0097dc 220 | PACKET 0: F_BRANCH_FULL 221 | branches : 3 222 | branch_map: 0x3 223 | address : 0x1c0097dc 224 | PACKET 0: F_BRANCH_FULL 225 | branches : 3 226 | branch_map: 0x3 227 | address : 0x1c0097dc 228 | PACKET 0: F_BRANCH_FULL 229 | branches : 3 230 | branch_map: 0x3 231 | address : 0x1c0097dc 232 | PACKET 0: F_BRANCH_FULL 233 | branches : 3 234 | branch_map: 0x3 235 | address : 0x1c0097dc 236 | PACKET 0: F_BRANCH_FULL 237 | branches : 3 238 | branch_map: 0x3 239 | address : 0x1c0097dc 240 | PACKET 0: F_BRANCH_FULL 241 | branches : 3 242 | branch_map: 0x3 243 | address : 0x1c0097dc 244 | PACKET 0: F_BRANCH_FULL 245 | branches : 3 246 | branch_map: 0x3 247 | address : 0x1c0097dc 248 | PACKET 0: F_BRANCH_FULL 249 | branches : 3 250 | branch_map: 0x3 251 | address : 0x1c0097dc 252 | PACKET 0: F_BRANCH_FULL 253 | branches : 3 254 | branch_map: 0x3 255 | address : 0x1c0097dc 256 | PACKET 0: F_BRANCH_FULL 257 | branches : 3 258 | branch_map: 0x3 259 | address : 0x1c0097dc 260 | PACKET 0: F_BRANCH_FULL 261 | branches : 3 262 | branch_map: 0x2 263 | address : 0x1c0097e4 264 | PACKET 0: F_BRANCH_FULL 265 | branches : 2 266 | branch_map: 0x1 267 | address : 0x1c0097e6 268 | PACKET 2: F_ADDR_ONLY 269 | address : 0x1c008aaa 270 | PACKET 2: F_ADDR_ONLY 271 | address : 0x1c0080d2 272 | PACKET 0: F_BRANCH_FULL 273 | branches : 1 274 | branch_map: 0x1 275 | address : 0x1c008b40 276 | PACKET 0: F_BRANCH_FULL 277 | branches : 1 278 | branch_map: 0x1 279 | address : 0x1c0080d8 280 | -------------------------------------------------------------------------------- /data/tx_spi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulp-platform/trdb/621656e06ed79760bf57f077b0830c68a074ba86/data/tx_spi -------------------------------------------------------------------------------- /doc/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulp-platform/trdb/621656e06ed79760bf57f077b0830c68a074ba86/doc/.gitkeep -------------------------------------------------------------------------------- /include/disassembly.h: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Trace Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /** 21 | * @file disassembly.h 22 | * @author Robert Balas (balasr@student.ethz.ch) 23 | * @brief Collection of disassembly routines using libopcdes and libbfd 24 | */ 25 | 26 | #ifndef __DISASSEMBLY_H__ 27 | #define __DISASSEMBLY_H__ 28 | 29 | #include 30 | #include 31 | 32 | #include "config.h" 33 | #include "bfd.h" 34 | #include "demangle.h" 35 | #include "dis-asm.h" 36 | #include "trace_debugger.h" 37 | 38 | /** 39 | * Used to capture all information and functions needed to disassemble. 40 | */ 41 | struct disassembler_unit { 42 | disassembler_ftype disassemble_fn; /**< does the actual disassembly */ 43 | struct disassemble_info *dinfo; /**< context for disassembly */ 44 | }; 45 | 46 | #define TRDB_NO_ALIASES 1 47 | #define TRDB_PREFIX_ADDRESSES 2 48 | #define TRDB_DO_DEMANGLE 4 49 | #define TRDB_DISPLAY_FILE_OFFSETS 8 50 | #define TRDB_LINE_NUMBERS 16 51 | #define TRDB_SOURCE_CODE 32 52 | #define TRDB_FUNCTION_CONTEXT 64 53 | #define TRDB_INLINES 128 54 | 55 | struct trdb_ctx; 56 | 57 | /** 58 | * Store disassembly configuration and context. 59 | */ 60 | struct trdb_disasm_aux { 61 | /* current disassembly context */ 62 | bfd *abfd; 63 | asection *sec; 64 | bfd_boolean require_sec; 65 | 66 | arelent **dynrelbuf; 67 | long dynrelcount; 68 | 69 | arelent *reloc; 70 | 71 | /* symbols extracted from bfd */ 72 | asymbol **symbols; 73 | long symcount; 74 | asymbol **dynamic_symbols; 75 | long dynsymcount; 76 | asymbol *synthethic_symbols; 77 | long synthcount; 78 | asymbol **sorted_symbols; 79 | long sorted_symcount; 80 | 81 | uint32_t config; /**< stores the below settings as bitfield */ 82 | bool no_aliases; /**< set to true to always diassemble to most general 83 | representation */ 84 | bool prefix_addresses; /**< different address format */ 85 | bool do_demangle; /**< demangle symbols using bfd */ 86 | bool display_file_offsets; /**< display file offsets for displayed symbols 87 | */ 88 | bool with_line_numbers; /**< periodically show line numbers mixed with 89 | assembly */ 90 | bool with_source_code; /**< show source code mixed with assembly */ 91 | bool with_function_context; /**< show when instruction lands on symbol value 92 | of a function */ 93 | bool unwind_inlines; /**< Print all inlines for source line */ 94 | }; 95 | 96 | /* The number of zeroes we want to see before we start skipping them. The number 97 | * is arbitrarily chosen. 98 | */ 99 | 100 | #define DEFAULT_SKIP_ZEROES 8 101 | 102 | /* The number of zeroes to skip at the end of a section. If the number of zeroes 103 | * at the end is between SKIP_ZEROES_AT_END and SKIP_ZEROES, they will be 104 | * disassembled. If there are fewer than SKIP_ZEROES_AT_END, they will be 105 | * skipped. This is a heuristic attempt to avoid disassembling zeroes inserted 106 | * by section alignment. 107 | */ 108 | 109 | #define DEFAULT_SKIP_ZEROES_AT_END 3 110 | 111 | /** 112 | * Initialize disassemble_info from libopcodes with hardcoded values for the 113 | * PULP platform. 114 | * @param dinfo filled with information of the PULP platform 115 | */ 116 | void trdb_init_disassemble_info_for_pulp(struct disassemble_info *dinfo); 117 | 118 | /** 119 | * Initialize disassembler_unit with settings for the PULP platform. 120 | * @param dunit filled with information for PULP 121 | * @param options configure the formatting behaviour or NULL 122 | * @return 0 on success, -1 otherwise 123 | */ 124 | int trdb_init_disassembler_unit_for_pulp(struct disassembler_unit *dunit, 125 | char *options); 126 | 127 | /** 128 | * Initialize disassemble_info from libopcodes by grabbing data out of @p abfd. 129 | * 130 | * The options string can be non-@c NULL to pass disassembler specific settings 131 | * to libopcodes. Currently supported is "no-aliases", which disassembles common 132 | * abbreviations for certain instructions. 133 | * 134 | * @param dinfo filled with information from @p abfd 135 | * @param abfd the bfd representing the binary 136 | * @param options disassembly options passed to libopcodes 137 | */ 138 | void trdb_init_disassemble_info_from_bfd(struct disassemble_info *dinfo, 139 | bfd *abfd, char *options); 140 | 141 | /** 142 | * Initialize disassembler_unit. 143 | * 144 | * A side from calling init_disassemble_info_from_bfd(), it also sets 145 | * #disassembler_unit.disassemble_fn to the architecture matching @p abfd. 146 | * 147 | * @param dunit filled with information from @p abfd 148 | * @param abfd the bfd representing the binary 149 | * @param options disassembly options passed to libopcodes 150 | * @return 0 on success, a negative error code otherwise 151 | * @return -trdb_invalid if @p dunit or #disassembler_unit.dinfo in @p 152 | * dunit is NULL 153 | * @return -trdb_arch_support if architecture is not supported 154 | */ 155 | int trdb_init_disassembler_unit(struct disassembler_unit *dunit, bfd *abfd, 156 | char *options); 157 | 158 | /** 159 | * Initialize disassembler_unit and its containing disassemble_info from 160 | * libopcodes by grabbing meta data out of @p abfd. Furthermore it allocates 161 | * internal structures to allow #disassembler_unit.disassemble_fn in @p 162 | * dunit to resolve addresses to nearest symbols. TODO: trdb_ctx sets demangle, 163 | * disassembly options and more. 164 | * 165 | * @param c the trace debugger context containing settings 166 | * @param abfd the bfd representing the binary 167 | * @param dunit filled with information from @p abfd 168 | * @return 0 on success, a negative error code otherwise 169 | * @return -trdb_invalid if @p c, @p abfd or @p dunit is NULL 170 | * @return -trdb_nomem if out of memory 171 | * @return -trdb_arch_support if architecture is not supported 172 | */ 173 | int trdb_alloc_dinfo_with_bfd(struct trdb_ctx *c, bfd *abfd, 174 | struct disassembler_unit *dunit); 175 | /** 176 | * Free the memory allocated to @p abfd and @p dunit by a call to 177 | * trdb_alloc_dinfo_with_bfd(). 178 | * 179 | * @param c the trace debugger context containing settings 180 | * @param abfd the bfd representing the binary 181 | * @param dunit filled with information from @p abfd 182 | */ 183 | void trdb_free_dinfo_with_bfd(struct trdb_ctx *c, bfd *abfd, 184 | struct disassembler_unit *dunit); 185 | 186 | /** 187 | * Configure the disassembler with flags. E.g. set @p settings to 188 | * TRDB_SOURCE_CODE | TRDB_LINE_NUMBERS to show the source code and file line 189 | * numbers mixed with disassembly. 190 | * 191 | * @param dunit the disassembler 192 | * @param settings configuration flags which can be set as described 193 | */ 194 | void trdb_set_disassembly_conf(struct disassembler_unit *dunit, 195 | uint32_t settings); 196 | 197 | /** 198 | * Read the disassembler configuration flags. Test for enabled features by 199 | * and'ing the returned value with e.g. TRDB_SOURCE_CODE. 200 | * 201 | * @param dunit the disassembler whose settings should be read 202 | * @param conf written with the disassembly configuration if successfull 203 | * @return 0 on success, a negative error code otherwise 204 | * @return -trdb_invalid if trdb_disasm_aux in @p dunit is NULL 205 | */ 206 | int trdb_get_disassembly_conf(struct disassembler_unit *dunit, uint32_t *conf); 207 | 208 | /** 209 | * A #print_address_func used in disassemble_info, set by 210 | * trdb_alloc_dinfo_with_bfd(), which resolve addresses to symbols. This 211 | * callback is only well defined if its disassemble_info struct was initialized 212 | * using trdb_alloc_dinfo_with_bfd(). 213 | * 214 | * This is a callback function, if registered, called by libopcodes to custom 215 | * format addresses. It can also be abused to print other information. 216 | * 217 | * @param vma the virtual memory address where this instruction is located at 218 | * @param inf context of disassembly 219 | */ 220 | void trdb_print_address(bfd_vma vma, struct disassemble_info *inf); 221 | 222 | /** 223 | * A #symbol_at_address function used in disassemble_info, set by 224 | * trdb_alloc_dinfo_with_bfd(). 225 | * 226 | * This is a callback function, which gives libopcodes information about 227 | * specific addresses. Determines if the given address has a symbol associated 228 | * with it. 229 | * 230 | * @param vma the virtual memory address where this instruction is located at 231 | * @param inf context of disassembly 232 | * @return 1 if there is a symbol at @p vma, 0 otherwise 233 | */ 234 | int trdb_symbol_at_address(bfd_vma vma, struct disassemble_info *inf); 235 | 236 | /** 237 | * Print the bfd section header to stdout. 238 | * 239 | * @param abfd the bfd representing the binary 240 | * @param section which section to print 241 | * @param ignored is ignored and only for compatiblity 242 | */ 243 | void trdb_dump_section_header(bfd *abfd, asection *section, void *ignored); 244 | 245 | /** 246 | * Print bfd architecuture information to stdout. 247 | * 248 | * @param abfd the bfd representing the binary 249 | */ 250 | void trdb_dump_bin_info(bfd *abfd); 251 | 252 | /** 253 | * Print all section names of bfd to stdout. 254 | * 255 | * @param abfd the bfd representing the binary 256 | */ 257 | void trdb_dump_section_names(bfd *abfd); 258 | 259 | /** 260 | * Print all supported targets of the used libopcodes to stdout. 261 | */ 262 | void trdb_dump_target_list(void); 263 | 264 | /** 265 | * Default #print_address_func used in disassemble_info, set by 266 | * init_disassemble_info_for_pulp() or init_disassemble_info_from_bfd(). 267 | * 268 | * This is a callback function, if registered, called by libopcodes to custom 269 | * format addresses. It can also be abused to print other information. 270 | * 271 | * @param vma the virtual memory address where this instruction is located at 272 | * @param dinfo context of disassembly 273 | */ 274 | void trdb_riscv32_print_address(bfd_vma vma, struct disassemble_info *dinfo); 275 | 276 | /** 277 | * Return if vma is contained in the given section of abfd. 278 | * 279 | * @param abfd the bfd representing the binary 280 | * @param section the section to test against 281 | * @param vma the virtual memory address to test for 282 | * @return whether @p vma is in contained in @p section 283 | */ 284 | bool trdb_vma_in_section(bfd *abfd, asection *section, bfd_vma vma); 285 | 286 | /** 287 | * Return the section of @p abfd that contains the given @p vma or @c NULL. 288 | * 289 | * @param abfd the bfd representing the binary 290 | * @param vma the virtual memory address to 291 | * @return the section which contains @p vma or @c NULL 292 | */ 293 | asection *trdb_get_section_for_vma(bfd *abfd, bfd_vma vma); 294 | 295 | /** 296 | * Disassemble the given asection and print it calling fprintf_func. 297 | * 298 | * @p inf must point to a disassembler_unit. 299 | * 300 | * @param abfd the bfd representing the binary 301 | * @param section the section to disassemble 302 | */ 303 | void trdb_disassemble_section(bfd *abfd, asection *section, void *inf); 304 | 305 | /** 306 | * Disassemble @p len bytes of data pointed to by @p data with the given 307 | * @p dunit and print it calling fprintf_func. 308 | * 309 | * @param len number of bytes in @p data 310 | * @param data raw data block to disassemble 311 | * @param dunit disassembly context 312 | */ 313 | void trdb_disassemble_block(size_t len, bfd_byte *data, 314 | struct disassembler_unit *dunit); 315 | 316 | /** 317 | * Disassemble the given instr with the pretended address and disassembler_unit 318 | * and print it calling fprintf_func. 319 | * 320 | * @param instr the raw instruction value up to 64 bits 321 | * @param addr the address where the instruction is located at 322 | * @param dunit disassembly context 323 | */ 324 | void trdb_disassemble_single_instruction(insn_t instr, addr_t addr, 325 | struct disassembler_unit *dunit); 326 | 327 | /** 328 | * Disassemble the given instr with the pretended address and print it calling 329 | * fprintf_func. Will configure a disassembler with default settings for the 330 | * PULP platform. This function is slow since we create a dissasembler for each 331 | * call. It is to be used sparingly for single instructions. 332 | * 333 | * @param instr the raw instruction value up to 32 bits 334 | * @param addr the address where the instruction is located at 335 | */ 336 | void trdb_disassemble_single_instruction_slow(insn_t instr, addr_t addr); 337 | 338 | /** 339 | * Disassemble the given instruction, indicated by its address @p addr and the 340 | * bfd @p abfd which contains it, using the settings in @p dunit and print it by 341 | * calling fprintf_func. 342 | * 343 | * @param c the trace debugger context 344 | * @param abfd the bfd which contains the instruction 345 | * @param addr the address of the instruction 346 | * @param dunit disassembly context 347 | */ 348 | void trdb_disassemble_instruction_with_bfd(struct trdb_ctx *c, bfd *abfd, 349 | bfd_vma addr, 350 | struct disassembler_unit *dunit); 351 | 352 | #endif 353 | -------------------------------------------------------------------------------- /include/serialize.h: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Trace Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /** 21 | * @file serialize.h 22 | * @author Robert Balas (balasr@student.ethz.ch) 23 | * @brief Read binary data into packets 24 | */ 25 | 26 | #include "trace_debugger.h" 27 | 28 | /** 29 | * Packs the @p packet into an array @p bin, aligned by @p align and writes the 30 | * packet length in bits into @p bitcnt. This function is specific to the PULP 31 | * platform since it omits certain fields such as "context", which are always 32 | * set to zero. 33 | * 34 | * @param c a trace debugger context 35 | * @param packet the data to serialize 36 | * @param bitcnt written with the number of bits in @p packet 37 | * @param align alignment of packet bits in @p bin, larger or equal to zero and 38 | * smaller than eight. 39 | * @param bin the array where the packet bits will be written into 40 | * @return 0 on success, a negative error code otherwise. 41 | * @return -trdb_invalid if @p align >= 8 42 | * @return -trdb_bad_packet if packet is longer than 16 bytes or unknown 43 | */ 44 | int trdb_pulp_serialize_packet(struct trdb_ctx *c, struct tr_packet *packet, 45 | size_t *bitcnt, uint8_t align, uint8_t bin[]); 46 | 47 | /** 48 | * Read a packet from @p fp stored in the PULP binary format. 49 | * 50 | * @param c trace debugger context 51 | * @param fp file to read from 52 | * @param packet filled out with read packet data 53 | * @param bytes the number of bytes of the packet (with header) 54 | * @return 0 on success, a negative error code otherwise 55 | * @return -trdb_invalid if @p c, @p fp or @p packet is NULL 56 | * @return -trdb_bad_packet if only an incomplete packet could be read from @p 57 | * fp or none at all 58 | * @return -trdb_file_read if the contents of @p fp could not be fully read 59 | * @return -trdb_bad_config if the decoding assumptions don't hold because of 60 | * contradictionary data in @p packet 61 | */ 62 | int trdb_pulp_read_single_packet(struct trdb_ctx *c, FILE *fp, 63 | struct tr_packet *packet, uint32_t *bytes); 64 | 65 | /** 66 | * Repeatedly calls trdb_pulp_read_single_packet until encountering EOF or an 67 | * incomplete packet. The incomplete packet is just ignored and all the previous 68 | * good packets are kept. 69 | * 70 | * @param c trace debugger context 71 | * @param path file from which to read packet data 72 | * @param packet_list appened with read packets 73 | * @return 0 on success, a negative error code otherwise 74 | * @return -trdb_invalid if @p c, @p path or @p packet_list is NULL 75 | * @return -trdb_file_open if file at @p path could not be found 76 | * @return -trdb_nomem if out of memory 77 | */ 78 | int trdb_pulp_read_all_packets(struct trdb_ctx *c, const char *path, 79 | struct trdb_packet_head *packet_list); 80 | 81 | /** 82 | * Serialize a single packet, like the PULP trace debugger. 83 | * @param c trace debugger context 84 | * @param packet data to serialize 85 | * @param fp file to write to 86 | * @return 0 on success, a negative error code otherwise 87 | * @return -trdb_invalid if @p c, @p fp or @p packet is NULL 88 | * @return -trdb_file_write if @p fp could not be fully written 89 | * @return -trdb_bad_packet if @p packet is malformed 90 | */ 91 | int trdb_pulp_write_single_packet(struct trdb_ctx *c, struct tr_packet *packet, 92 | FILE *fp); 93 | 94 | /** 95 | * Write a list of tr_packets to a file located at @p path. 96 | * 97 | * This function calls trdb_serialize_packet() to tightly align the packets, 98 | * which can have a non-power-of-two size. 99 | * 100 | * @param c the context/state of the trace debugger 101 | * @param path where the file is located at 102 | * @param packet_list list of packets to write 103 | * @return 0 on success, a negative error code otherwise 104 | * @return -trdb_invalid if @p c, @p path or @p packet_list is NULL 105 | * @return -trdb_file_open if file at @p path could not be full read 106 | * @return -trdb_file_write if file at @p path could not be fully written 107 | */ 108 | int trdb_write_packets(struct trdb_ctx *c, const char *path, 109 | struct trdb_packet_head *packet_list); 110 | 111 | /** 112 | * Read a stimuli file at @p path into a list of tr_instr. This function 113 | * allocates the struts in a sequential fashion. 114 | * 115 | * @param c the context/state of the trace debugger 116 | * @param path where the stimuli file is located at 117 | * @param instrs list anchor where to append the tr_instr to 118 | * @param count written with the number of produced tr_instr. 119 | * @return 0 on success, a negative error code otherwise 120 | * @return -trdb_invalid if @p c, @p path or @p instrs is NULL 121 | * @return -trdb_file_open if file at @p path could not be opened 122 | * @return -trdb_nomem if out of memory 123 | * @return -trdb_scan_file if parsing the file at @p failed 124 | */ 125 | int trdb_stimuli_to_trace_list(struct trdb_ctx *c, const char *path, 126 | struct trdb_instr_head *instrs, size_t *count); 127 | 128 | /** 129 | * Read a stimuli file at @p path into an array of tr_instr. 130 | * 131 | * @param c the context/state of the trace debugger 132 | * @param path where the stimuli file is located at 133 | * @param samples where to write the read array of tr_instr 134 | * @param count written with the number of produced tr_instr 135 | * @return 0 on success, a negative error code otherwise 136 | * @return -trdb_invalid if @p c, @p path or @p instrs is NULL 137 | * @return -trdb_file_open if file at @p path could not be opened 138 | * @return -trdb_nomem if out of memory 139 | * @return -trdb_scan_file if parsing the file at @p failed 140 | */ 141 | int trdb_stimuli_to_trace(struct trdb_ctx *c, const char *path, 142 | struct tr_instr **samples, size_t *count); 143 | 144 | /** 145 | * Load cvs file, where each line represents an input vector to the trace 146 | * debugger, into a list of tr_instr. 147 | * 148 | * @param c the context/state of the trace debugger 149 | * @param path where the cvs file is located at 150 | * @param instrs list anchor where to append the tr_instr to 151 | * @param count written with the number of produced tr_instr 152 | * @return 0 on success, a negative error code otherwise 153 | * @return -trdb_error_code.trdb_invalid if @p path or @p instrs is NULL 154 | * @return -trdb_file_open if file at @p path could not be opened 155 | * @return -trdb_bad_cvs_header if first line of file at @p path does not match 156 | * expected value 157 | * @return -trdb_nomem if out of memory 158 | * @return -trdb_scan_state_invalid if scanning comma seperated values fails 159 | * @return -trdb_scan_file if parsing the file at @p failed 160 | */ 161 | int trdb_cvs_to_trace_list(struct trdb_ctx *c, const char *path, 162 | struct trdb_instr_head *instrs, size_t *count); 163 | -------------------------------------------------------------------------------- /include/svdpi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * svdpi.h 3 | * 4 | * SystemVerilog Direct Programming Interface (DPI). 5 | * 6 | * This file contains the constant definitions, structure definitions, 7 | * and routine declarations used by SystemVerilog DPI. 8 | * 9 | * This file is from the SystemVerilog IEEE 1800-2023 Annex I. 10 | */ 11 | 12 | #ifndef INCLUDED_SVDPI 13 | #define INCLUDED_SVDPI 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | /* Define size-critical types on all OS platforms. */ 20 | #if defined (_MSC_VER) 21 | typedef unsigned __int64 uint64_t; 22 | typedef unsigned __int32 uint32_t; 23 | typedef unsigned __int8 uint8_t; 24 | typedef signed __int64 int64_t; 25 | typedef signed __int32 int32_t; 26 | typedef signed __int8 int8_t; 27 | #elif defined(__MINGW32__) 28 | #include 29 | #elif defined(__APPLE__) 30 | #include 31 | #elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) 32 | #include 33 | #else 34 | #include 35 | #endif 36 | 37 | /* Use to import a symbol into dll */ 38 | #ifndef DPI_DLLISPEC 39 | #if (defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__)) 40 | #define DPI_DLLISPEC __declspec(dllimport) 41 | #else 42 | #define DPI_DLLISPEC 43 | #endif 44 | #endif 45 | 46 | /* Use to export a symbol from dll */ 47 | #ifndef DPI_DLLESPEC 48 | #if (defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__)) 49 | #define DPI_DLLESPEC __declspec(dllexport) 50 | #else 51 | #define DPI_DLLESPEC 52 | #endif 53 | #endif 54 | 55 | /* Use to mark a function as external */ 56 | #ifndef DPI_EXTERN 57 | #define DPI_EXTERN 58 | #endif 59 | 60 | #ifndef DPI_PROTOTYPES 61 | #define DPI_PROTOTYPES 62 | /* object is defined imported by the application */ 63 | #define XXTERN DPI_EXTERN DPI_DLLISPEC 64 | /* object is exported by the application */ 65 | #define EETERN DPI_EXTERN DPI_DLLESPEC 66 | #endif 67 | 68 | /* canonical representation */ 69 | #define sv_0 0 70 | #define sv_1 1 71 | #define sv_z 2 72 | #define sv_x 3 73 | 74 | /* common type for 'bit' and 'logic' scalars. */ 75 | typedef uint8_t svScalar; 76 | typedef svScalar svBit; /* scalar */ 77 | typedef svScalar svLogic; /* scalar */ 78 | 79 | /* 80 | * DPI representation of packed arrays. 81 | * 2-state and 4-state vectors, exactly the same as PLI's avalue/bvalue. 82 | */ 83 | #ifndef VPI_VECVAL 84 | #define VPI_VECVAL 85 | typedef struct t_vpi_vecval { 86 | uint32_t aval; 87 | uint32_t bval; 88 | } s_vpi_vecval, *p_vpi_vecval; 89 | #endif 90 | 91 | /* (a chunk of) packed logic array */ 92 | typedef s_vpi_vecval svLogicVecVal; 93 | 94 | /* (a chunk of) packed bit array */ 95 | typedef uint32_t svBitVecVal; 96 | 97 | /* Number of chunks required to represent the given width packed array */ 98 | #define SV_PACKED_DATA_NELEMS(WIDTH) (((WIDTH) + 31) >> 5) 99 | 100 | /* 101 | * Because the contents of the unused bits is undetermined, 102 | * the following macros can be handy. 103 | */ 104 | #define SV_MASK(N) (~(0xffffffffU << (N))) 105 | 106 | #define SV_GET_UNSIGNED_BITS(VALUE, N) \ 107 | ((N) == 32 ? (VALUE) : ((VALUE) & SV_MASK(N))) 108 | 109 | #define SV_GET_SIGNED_BITS(VALUE, N) \ 110 | ((N) == 32 ? (VALUE) : \ 111 | (((VALUE) & (1 << (N))) ? ((VALUE) | ~SV_MASK(N)) : ((VALUE) & SV_MASK(N)))) 112 | 113 | #ifndef VPI_TIME 114 | #define VPI_TIME 115 | typedef struct t_vpi_time { 116 | int32_t type; 117 | uint32_t high, low; 118 | double real; 119 | } s_vpi_time, *p_vpi_time; 120 | 121 | #define vpiScaledRealTime 1 122 | #define vpiSimTime 2 123 | #define vpiSuppressTime 3 124 | #endif 125 | 126 | /* time value */ 127 | typedef s_vpi_time svTimeVal; 128 | 129 | /* time value types */ 130 | #define sv_scaled_real_time vpiScaledRealTime 131 | #define sv_sim_time vpiSimTime 132 | 133 | /* 134 | * Implementation-dependent representation. 135 | */ 136 | /* 137 | * Return implementation version information string ("1800-2005" or "SV3.1a"). 138 | */ 139 | XXTERN const char* svDpiVersion( void ); 140 | 141 | /* a handle to a scope (an instance of a module or interface) */ 142 | XXTERN typedef void* svScope; 143 | 144 | /* a handle to a generic object (actually, unsized array) */ 145 | XXTERN typedef void* svOpenArrayHandle; 146 | 147 | /* 148 | * Bit-select utility functions. 149 | * 150 | * Packed arrays are assumed to be indexed n-1:0, 151 | * where 0 is the index of LSB 152 | */ 153 | 154 | /* s=source, i=bit-index */ 155 | XXTERN svBit svGetBitselBit(const svBitVecVal* s, int i); 156 | XXTERN svLogic svGetBitselLogic(const svLogicVecVal* s, int i); 157 | 158 | /* d=destination, i=bit-index, s=scalar */ 159 | XXTERN void svPutBitselBit(svBitVecVal* d, int i, svBit s); 160 | XXTERN void svPutBitselLogic(svLogicVecVal* d, int i, svLogic s); 161 | 162 | /* 163 | * Part-select utility functions. 164 | * 165 | * A narrow (<=32 bits) part-select is extracted from the 166 | * source representation and written into the destination word. 167 | * 168 | * Normalized ranges and indexing [n-1:0] are used for both arrays. 169 | * 170 | * s=source, d=destination, i=starting bit index, w=width 171 | * like for variable part-selects; limitations: w <= 32 172 | */ 173 | XXTERN void svGetPartselBit(svBitVecVal* d, const svBitVecVal* s, int i, int w); 174 | XXTERN void svGetPartselLogic(svLogicVecVal* d, const svLogicVecVal* s, int i, int w); 175 | 176 | XXTERN void svPutPartselBit(svBitVecVal* d, const svBitVecVal s, int i, int w); 177 | XXTERN void svPutPartselLogic(svLogicVecVal* d, const svLogicVecVal s, int i, int w); 178 | 179 | /* 180 | * Open array querying functions 181 | * These functions are modeled upon the SystemVerilog array 182 | * querying functions and use the same semantics. 183 | * 184 | * If the dimension is 0, then the query refers to the 185 | * packed part of an array (which is one-dimensional). 186 | * Dimensions > 0 refer to the unpacked part of an array. 187 | */ 188 | /* h= handle to open array, d=dimension */ 189 | XXTERN int svLeft(const svOpenArrayHandle h, int d); 190 | XXTERN int svRight(const svOpenArrayHandle h, int d); 191 | XXTERN int svLow(const svOpenArrayHandle h, int d); 192 | XXTERN int svHigh(const svOpenArrayHandle h, int d); 193 | XXTERN int svIncrement(const svOpenArrayHandle h, int d); 194 | XXTERN int svSize(const svOpenArrayHandle h, int d); 195 | XXTERN int svDimensions(const svOpenArrayHandle h); 196 | 197 | /* 198 | * Pointer to the actual representation of the whole array of any type 199 | * NULL if not in C layout 200 | */ 201 | XXTERN void *svGetArrayPtr(const svOpenArrayHandle); 202 | 203 | /* total size in bytes or 0 if not in C layout */ 204 | XXTERN int svSizeOfArray(const svOpenArrayHandle); 205 | 206 | /* 207 | * Return a pointer to an element of the array 208 | * or NULL if index outside the range or null pointer 209 | */ 210 | XXTERN void *svGetArrElemPtr(const svOpenArrayHandle, int indx1, ...); 211 | 212 | /* specialized versions for 1-, 2- and 3-dimensional arrays: */ 213 | XXTERN void *svGetArrElemPtr1(const svOpenArrayHandle, int indx1); 214 | XXTERN void *svGetArrElemPtr2(const svOpenArrayHandle, int indx1, int indx2); 215 | XXTERN void *svGetArrElemPtr3(const svOpenArrayHandle, int indx1, int indx2, 216 | int indx3); 217 | 218 | /* 219 | * Functions for copying between simulator storage and user space. 220 | * These functions copy the whole packed array in either direction. 221 | * The user is responsible for allocating an array to hold the 222 | * canonical representation. 223 | */ 224 | 225 | /* s=source, d=destination */ 226 | /* From user space into simulator storage */ 227 | XXTERN void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, 228 | int indx1, ...); 229 | XXTERN void svPutBitArrElem1VecVal(const svOpenArrayHandle d, const svBitVecVal* s, 230 | int indx1); 231 | XXTERN void svPutBitArrElem2VecVal(const svOpenArrayHandle d, const svBitVecVal* s, 232 | int indx1, int indx2); 233 | XXTERN void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, 234 | int indx1, int indx2, int indx3); 235 | 236 | XXTERN void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, 237 | int indx1, ...); 238 | XXTERN void svPutLogicArrElem1VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, 239 | int indx1); 240 | XXTERN void svPutLogicArrElem2VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, 241 | int indx1, int indx2); 242 | XXTERN void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, 243 | int indx1, int indx2, int indx3); 244 | 245 | /* From simulator storage into user space */ 246 | XXTERN void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, 247 | int indx1, ...); 248 | XXTERN void svGetBitArrElem1VecVal(svBitVecVal* d, const svOpenArrayHandle s, 249 | int indx1); 250 | XXTERN void svGetBitArrElem2VecVal(svBitVecVal* d, const svOpenArrayHandle s, 251 | int indx1, int indx2); 252 | XXTERN void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, 253 | int indx1, int indx2, int indx3); 254 | XXTERN void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, 255 | int indx1, ...); 256 | XXTERN void svGetLogicArrElem1VecVal(svLogicVecVal* d, const svOpenArrayHandle s, 257 | int indx1); 258 | XXTERN void svGetLogicArrElem2VecVal(svLogicVecVal* d, const svOpenArrayHandle s, 259 | int indx1, int indx2); 260 | XXTERN void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, 261 | int indx1, int indx2, int indx3); 262 | 263 | XXTERN svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...); 264 | XXTERN svBit svGetBitArrElem1(const svOpenArrayHandle s, int indx1); 265 | XXTERN svBit svGetBitArrElem2(const svOpenArrayHandle s, int indx1, int indx2); 266 | XXTERN svBit svGetBitArrElem3(const svOpenArrayHandle s, int indx1, int indx2, 267 | int indx3); 268 | XXTERN svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...); 269 | XXTERN svLogic svGetLogicArrElem1(const svOpenArrayHandle s, int indx1); 270 | XXTERN svLogic svGetLogicArrElem2(const svOpenArrayHandle s, int indx1, int indx2); 271 | XXTERN svLogic svGetLogicArrElem3(const svOpenArrayHandle s, int indx1, int indx2, 272 | int indx3); 273 | XXTERN void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, 274 | ...); 275 | XXTERN void svPutLogicArrElem1(const svOpenArrayHandle d, svLogic value, int indx1); 276 | XXTERN void svPutLogicArrElem2(const svOpenArrayHandle d, svLogic value, int indx1, 277 | int indx2); 278 | XXTERN void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value, int indx1, 279 | int indx2, int indx3); 280 | XXTERN void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...); 281 | XXTERN void svPutBitArrElem1(const svOpenArrayHandle d, svBit value, int indx1); 282 | XXTERN void svPutBitArrElem2(const svOpenArrayHandle d, svBit value, int indx1, 283 | int indx2); 284 | XXTERN void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, 285 | int indx2, int indx3); 286 | 287 | /* Functions for working with DPI context */ 288 | 289 | /* 290 | * Retrieve the active instance scope currently associated with the executing 291 | * imported function. Unless a prior call to svSetScope has occurred, this 292 | * is the scope of the function's declaration site, not call site. 293 | * Returns NULL if called from C code that is *not* an imported function. 294 | */ 295 | XXTERN svScope svGetScope( void ); 296 | 297 | /* 298 | * Set context for subsequent export function execution. 299 | * This function shall be called before calling an export function, unless 300 | * the export function is called while executing an import function. In that 301 | * case the export function shall inherit the scope of the surrounding import 302 | * function. This is known as the "default scope". 303 | * The return is the previous active scope (per svGetScope) 304 | */ 305 | XXTERN svScope svSetScope(const svScope scope); 306 | 307 | /* Gets the fully qualified name of a scope handle */ 308 | XXTERN const char* svGetNameFromScope(const svScope); 309 | 310 | /* 311 | * Retrieve svScope to instance scope of an arbitrary function declaration. 312 | * (can be either module, program, interface, or generate scope) 313 | * The return value shall be NULL for unrecognized scope names. 314 | */ 315 | XXTERN svScope svGetScopeFromName(const char* scopeName); 316 | 317 | /* 318 | * Store an arbitrary user data pointer for later retrieval by svGetUserData() 319 | * The userKey is generated by the user. It needs to be guaranteed by the user to 320 | * be unique from all other userKey's for all unique data storage requirements 321 | * It is recommended that the address of static functions or variables in the 322 | * user's C code be used as the userKey. 323 | * It is illegal to pass in NULL values for either the scope or userData 324 | * arguments. It is also an error to call svPutUserData() with an invalid 325 | * svScope. This function returns -1 for all error cases, 0 upon success. It is 326 | * suggested that userData values of 0 (NULL) not be used as otherwise it can 327 | * be impossible to discern error status returns when calling svGetUserData() 328 | */ 329 | XXTERN int svPutUserData(const svScope scope, void *userKey, void* userData); 330 | 331 | /* 332 | * Retrieve an arbitrary user data pointer that was previously 333 | * stored by a call to svPutUserData(). See the comment above 334 | * svPutUserData() for an explanation of userKey, as well as 335 | * restrictions on NULL and illegal svScope and userKey values. 336 | * This function returns NULL for all error cases, 0 upon success. 337 | * This function also returns NULL in the event that a prior call 338 | * to svPutUserData() was never made. 339 | */ 340 | XXTERN void* svGetUserData(const svScope scope, void* userKey); 341 | 342 | /* 343 | * Returns the file and line number in the SV code from which the import call 344 | * was made. If this information available, returns TRUE and updates fileName 345 | * and lineNumber to the appropriate values. Behavior is unpredictable if 346 | * fileName or lineNumber are not appropriate pointers. If this information is 347 | * not available return FALSE and contents of fileName and lineNumber not 348 | * modified. Whether this information is available or not is implementation- 349 | * specific. Note that the string provided (if any) is owned by the SV 350 | * implementation and is valid only until the next call to any SV function. 351 | * Applications shall not modify this string or free it. 352 | */ 353 | XXTERN int svGetCallerInfo(const char** fileName, int *lineNumber); 354 | 355 | /* 356 | * Returns 1 if the current execution thread is in the disabled state. 357 | * Disable protocol shall be adhered to if in the disabled state. 358 | */ 359 | XXTERN int svIsDisabledState( void ); 360 | 361 | /* 362 | * Imported functions call this API function during disable processing to 363 | * acknowledge that they are correctly participating in the DPI disable protocol. 364 | * This function shall be called before returning from an imported function that is 365 | * in the disabled state. 366 | */ 367 | XXTERN void svAckDisabledState( void ); 368 | 369 | /* Mantis 5713/D9 370 | * Retrieve the current simulation time, scaled to the time unit of the scope. 371 | * If scope is NULL, then time is scaled to the simulation time unit. 372 | * It is an error to call svGetTime() with an invalid svScope. 373 | * This function returns -1 for all error cases, 0 upon success. 374 | */ 375 | XXTERN int svGetTime(const svScope scope, svTimeVal* time); 376 | 377 | /* 378 | * Retrieve the time unit for scope. 379 | * If scope is NULL, then simulation time unit is retrieved. 380 | * It is an error to call svGetTimeUnit() with an invalid svScope. 381 | * This function returns -1 for all error cases, 0 upon success. 382 | */ 383 | XXTERN int svGetTimeUnit(const svScope scope, int32_t* time_unit); 384 | 385 | /* 386 | * Retrieve the time precision for scope. 387 | * If scope is NULL, then simulation time unit is retrieved. 388 | * It is an error to call svGetTimePrecision() with an invalid svScope. 389 | * This function returns -1 for all error cases, 0 upon success. 390 | */ 391 | XXTERN int svGetTimePrecision(const svScope scope, int32_t* time_precision); 392 | 393 | 394 | /* 395 | ********************************************************** 396 | * DEPRECATED PORTION OF FILE STARTS FROM HERE. 397 | * IEEE-1800-compliant tools may not provide 398 | * support for the following functionality. 399 | ********************************************************** 400 | */ 401 | 402 | /* 403 | * Canonical representation of packed arrays 404 | * 2-state and 4-state vectors, modeled upon PLI's avalue/bvalue 405 | */ 406 | #define SV_CANONICAL_SIZE(WIDTH) (((WIDTH)+31)>>5) 407 | typedef unsigned int svBitVec32;/* (a chunk of) packed bit array */ 408 | typedef struct { unsigned int c; unsigned int d;} 409 | svLogicVec32; /* (a chunk of) packed logic array */ 410 | 411 | /* reference to a standalone packed array */ 412 | typedef void* svBitPackedArrRef; 413 | typedef void* svLogicPackedArrRef; 414 | 415 | /* 416 | * total size in bytes of the simulator's representation of a packed array 417 | * width in bits 418 | */ 419 | XXTERN int svSizeOfBitPackedArr(int width); 420 | XXTERN int svSizeOfLogicPackedArr(int width); 421 | 422 | /* Translation between the actual representation and the canonical representation */ 423 | 424 | /* s=source, d=destination, w=width */ 425 | /* actual <-- canonical */ 426 | XXTERN void svPutBitVec32(svBitPackedArrRef d, const svBitVec32* s, int w); 427 | XXTERN void svPutLogicVec32(svLogicPackedArrRef d, const svLogicVec32* s, int w); 428 | 429 | /* canonical <-- actual */ 430 | XXTERN void svGetBitVec32(svBitVec32* d, const svBitPackedArrRef s, int w); 431 | XXTERN void svGetLogicVec32(svLogicVec32* d, const svLogicPackedArrRef s, int w); 432 | 433 | /* 434 | * Bit-select functions 435 | * Packed arrays are assumed to be indexed n-1:0, 436 | * where 0 is the index of LSB 437 | */ 438 | 439 | /* s=source, i=bit-index */ 440 | XXTERN svBit svGetSelectBit(const svBitPackedArrRef s, int i); 441 | XXTERN svLogic svGetSelectLogic(const svLogicPackedArrRef s, int i); 442 | 443 | /* d=destination, i=bit-index, s=scalar */ 444 | XXTERN void svPutSelectBit(svBitPackedArrRef d, int i, svBit s); 445 | XXTERN void svPutSelectLogic(svLogicPackedArrRef d, int i, svLogic s); 446 | 447 | /* 448 | * functions for part-select 449 | * 450 | * a narrow (<=32 bits) part-select is copied between 451 | * the implementation representation and a single chunk of 452 | * canonical representation 453 | * Normalized ranges and indexing [n-1:0] are used for both arrays: 454 | * the array in the implementation representation and the canonical array. 455 | * 456 | * s=source, d=destination, i=starting bit index, w=width 457 | * like for variable part-selects; limitations: w <= 32 458 | */ 459 | 460 | /* canonical <-- actual */ 461 | XXTERN void svGetPartSelectBit(svBitVec32* d, const svBitPackedArrRef s, 462 | int i, int w); 463 | XXTERN svBitVec32 svGetBits(const svBitPackedArrRef s, int i, int w); 464 | XXTERN svBitVec32 svGet32Bits(const svBitPackedArrRef s, int i); /* 32-bits */ 465 | 466 | XXTERN uint64_t svGet64Bits(const svBitPackedArrRef s, int i); 467 | 468 | /* 64-bits */ 469 | XXTERN void svGetPartSelectLogic(svLogicVec32* d, const svLogicPackedArrRef s, 470 | int i, int w); 471 | /* actual <-- canonical */ 472 | XXTERN void svPutPartSelectBit(svBitPackedArrRef d, const svBitVec32 s, 473 | int i, int w); 474 | XXTERN void svPutPartSelectLogic(svLogicPackedArrRef d, const svLogicVec32* s, 475 | int i, int w); 476 | 477 | /* 478 | * Functions for open array translation between simulator and canonical 479 | * representations. These functions copy the whole packed array in either 480 | * direction. The user is responsible for allocating an array in the 481 | * canonical representation. 482 | */ 483 | 484 | /* s=source, d=destination */ 485 | /* actual <-- canonical */ 486 | XXTERN void svPutBitArrElemVec32(const svOpenArrayHandle d, const svBitVec32* s, 487 | int indx1, ...); 488 | XXTERN void svPutBitArrElem1Vec32(const svOpenArrayHandle d, const svBitVec32* s, 489 | int indx1); 490 | XXTERN void svPutBitArrElem2Vec32(const svOpenArrayHandle d, const svBitVec32* s, 491 | int indx1, int indx2); 492 | XXTERN void svPutBitArrElem3Vec32(const svOpenArrayHandle d, const svBitVec32* s, 493 | int indx1, int indx2, int indx3); 494 | XXTERN void svPutLogicArrElemVec32(const svOpenArrayHandle d, const svLogicVec32* s, 495 | int indx1, ...); 496 | XXTERN void svPutLogicArrElem1Vec32(const svOpenArrayHandle d, const svLogicVec32* s, 497 | int indx1); 498 | XXTERN void svPutLogicArrElem2Vec32(const svOpenArrayHandle d, const svLogicVec32* s, 499 | int indx1, int indx2); 500 | XXTERN void svPutLogicArrElem3Vec32(const svOpenArrayHandle d, const svLogicVec32* s, 501 | int indx1, int indx2, int indx3); 502 | 503 | /* canonical <-- actual */ 504 | XXTERN void svGetBitArrElemVec32(svBitVec32* d, const svOpenArrayHandle s, 505 | int indx1, ...); 506 | XXTERN void svGetBitArrElem1Vec32(svBitVec32* d, const svOpenArrayHandle s, 507 | int indx1); 508 | XXTERN void svGetBitArrElem2Vec32(svBitVec32* d, const svOpenArrayHandle s, 509 | int indx1, int indx2); 510 | XXTERN void svGetBitArrElem3Vec32(svBitVec32* d, const svOpenArrayHandle s, 511 | int indx1, int indx2, int indx3); 512 | XXTERN void svGetLogicArrElemVec32(svLogicVec32* d, const svOpenArrayHandle s, 513 | int indx1, ...); 514 | XXTERN void svGetLogicArrElem1Vec32(svLogicVec32* d, const svOpenArrayHandle s, 515 | int indx1); 516 | XXTERN void svGetLogicArrElem2Vec32(svLogicVec32* d, const svOpenArrayHandle s, 517 | int indx1, int indx2); 518 | XXTERN void svGetLogicArrElem3Vec32(svLogicVec32* d, const svOpenArrayHandle s, 519 | int indx1, int indx2, int indx3); 520 | 521 | /* 522 | ********************************************************** 523 | * DEPRECATED PORTION OF FILE ENDS HERE. 524 | ********************************************************** 525 | */ 526 | 527 | #undef DPI_EXTERN 528 | 529 | #ifdef DPI_PROTOTYPES 530 | #undef DPI_PROTOTYPES 531 | #undef XXTERN 532 | #undef EETERN 533 | #endif 534 | 535 | #ifdef __cplusplus 536 | } 537 | #endif 538 | 539 | #endif 540 | -------------------------------------------------------------------------------- /include/trace_debugger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Trace Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /** 21 | * @file trace_debugger.h 22 | * @author Robert Balas (balasr@student.ethz.ch) 23 | * @brief Software model for the hardware trace debugger. 24 | */ 25 | 26 | #ifndef __TRACE_DEBUGGER_H__ 27 | #define __TRACE_DEBUGGER_H__ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | #include "config.h" 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | /* instruction and address types */ 41 | #ifdef TRDB_ARCH64 42 | typedef uint64_t insn_t; /* _t violates posix, but no one seems to care anyway*/ 43 | typedef uint64_t addr_t; 44 | # define PRIxINSN PRIx64 45 | # define PRIxADDR PRIx64 46 | # define SCNxINSN SCNx64 47 | # define SCNxADDR SCNx64 48 | #else 49 | typedef uint32_t insn_t; 50 | typedef uint32_t addr_t; 51 | # define PRIxINSN PRIx32 52 | # define PRIxADDR PRIx32 53 | # define SCNxINSN SCNx32 54 | # define SCNxADDR SCNx32 55 | #endif 56 | 57 | /* fix nameconflict with basename in libiberty and libgen */ 58 | #define HAVE_DECL_BASENAME 1 59 | #include "bfd.h" 60 | #include "disassembly.h" 61 | 62 | /* custom header */ 63 | #define PACKETLEN 7 64 | #define PULPPKTLEN 4 65 | #define MSGTYPELEN 2 66 | 67 | /* given header */ 68 | #define FORMATLEN 2 69 | #define BRANCHLEN 5 70 | #define CAUSELEN 5 71 | #define PRIVLEN 3 72 | 73 | #ifdef TRDB_ARCH64 74 | # define XLEN 64 75 | # define ILEN 64 76 | #else 77 | # define XLEN 32 78 | # define ILEN 32 79 | #endif 80 | 81 | #define CONTEXTLEN 32 82 | 83 | /* timer */ 84 | #define TIMELEN 40 85 | 86 | /* sw dump */ 87 | #define SWLEN 32 88 | 89 | #define INSTR_STR_LEN 128 90 | 91 | /* forward declarations */ 92 | struct disassembler_unit; 93 | 94 | /** 95 | * Retired instruction captured by the interface to the RISC-V CPU, with some 96 | * metadata. 97 | */ 98 | struct tr_instr { 99 | bool valid; /**< whether this sample/struct is valid */ 100 | bool exception; /**< instruction trapped */ 101 | bool interrupt; /**< exception caused by interrupt */ 102 | uint32_t cause : CAUSELEN; /**< exception cause */ 103 | addr_t tval : XLEN; /**< not used in PULP, trap value */ 104 | uint32_t priv : PRIVLEN; /**< privilege mode */ 105 | addr_t iaddr : XLEN; /**< instruction address */ 106 | insn_t instr : ILEN; /**< raw instruction value */ 107 | bool compressed; /**< instruction was originally compressed */ 108 | TAILQ_ENTRY(tr_instr) list; /**< anchor for tail queue/linked list */ 109 | }; 110 | 111 | /** 112 | * Struct to represent a list of tr_instr. 113 | */ 114 | TAILQ_HEAD(trdb_instr_head, tr_instr); /**< list pointer struct to tr_instr*/ 115 | 116 | /** 117 | * All the possible trace packet types. 118 | */ 119 | enum tr_packet_format { 120 | F_BRANCH_FULL = 0, 121 | F_BRANCH_DIFF = 1, 122 | F_ADDR_ONLY = 2, 123 | F_SYNC = 3 124 | }; 125 | 126 | /** 127 | * Trace packet subformats for sync packets. 128 | */ 129 | enum tr_packet_subformat { SF_START = 0, SF_EXCEPTION = 1, SF_CONTEXT = 2 }; 130 | 131 | /** 132 | * All packet types 133 | */ 134 | enum tr_msg_type { W_EMPTY = 0, W_TIMER = 1, W_TRACE = 2, W_SOFTWARE = 3 }; 135 | 136 | /** 137 | * Canonical trace packet representation. This is the high level definition of a 138 | * packet with some meta information (first part of tr_packet) about the payload 139 | * (second part). The meta information is specific to the transportation 140 | * mechanism on the hardware. For the PULP platform there is just a small packet 141 | * length indicator 142 | * 143 | * There are four possible packet types. See 144 | * riscv-trace-spec for 145 | * details. A list or array of such packets and a bfd struct represent together 146 | * a compressed list or array of tr_instr. 147 | */ 148 | struct tr_packet { 149 | /* transport layer header */ 150 | uint32_t length : PACKETLEN; /**< length of packet in bits */ 151 | uint32_t msg_type : 2; /**< payload type */ 152 | /* custom data written by user, valid when msg_type = W_SOFTWARE*/ 153 | uint32_t userdata : SWLEN; 154 | 155 | /* timer data */ 156 | uint64_t time; 157 | 158 | /* actual payload of spec, valid when msg_type = W_TRACE */ 159 | uint32_t format : 2; /**< header denoting the packet type */ 160 | uint32_t branches : 5; /**< number of branches saved in branch_map */ 161 | uint32_t branch_map; /**< bits indicating taken=1 branches */ 162 | uint32_t subformat : 2; /**< further specifies F_SYNC packets */ 163 | uint32_t context : CONTEXTLEN; /**< not used in PULP, context switch */ 164 | uint32_t privilege : PRIVLEN; /**< current privilege mode */ 165 | /* we need this if the first instruction of an exception is a branch, since 166 | * that won't be reorcded into the branch map 167 | */ 168 | bool branch; /**< special case for F_SYNC packets */ 169 | addr_t address : XLEN; /**< address of the instruction */ 170 | uint32_t ecause : CAUSELEN; /**< exception cause */ 171 | bool interrupt; /**< exception through interrupt */ 172 | addr_t tval : XLEN; /**< not used in PULP, trap information */ 173 | TAILQ_ENTRY(tr_packet) list; /**< anchor for tail queue/linked list */ 174 | }; 175 | 176 | /** 177 | * Struct to represent a list of tr_packet. 178 | */ 179 | TAILQ_HEAD(trdb_packet_head, tr_packet); 180 | 181 | /** 182 | * Keep information about generated packets. 183 | */ 184 | struct trdb_packet_stats { 185 | size_t packets; 186 | size_t addr_only_packets; 187 | size_t exception_packets; 188 | size_t start_packets; 189 | size_t diff_packets; 190 | size_t abs_packets; 191 | size_t bmap_full_packets; 192 | size_t bmap_full_addr_packets; 193 | }; 194 | 195 | /** 196 | * Error codes. 197 | */ 198 | enum trdb_error_code { 199 | trdb_ok, 200 | trdb_internal, 201 | trdb_invalid, 202 | trdb_bad_instr, 203 | trdb_bad_packet, 204 | trdb_bad_config, 205 | trdb_bad_ras, 206 | trdb_bad_cvs_header, 207 | trdb_unimplemented, 208 | trdb_nomem, 209 | trdb_file_open, 210 | trdb_file_size, 211 | trdb_file_read, 212 | trdb_file_write, 213 | trdb_scan_file, 214 | trdb_scan_state_invalid, 215 | trdb_arch_support, 216 | trdb_section_empty, 217 | trdb_bad_vma 218 | }; 219 | 220 | /** 221 | * Return a human readable string. 222 | */ 223 | const char *trdb_errstr(enum trdb_error_code); 224 | 225 | /** 226 | * Decode a function return value to an error code. 227 | */ 228 | enum trdb_error_code trdb_errcode(int status); 229 | 230 | /** 231 | * For type punning of packet data. It's pretty annoying to pack our packets 232 | * tightly, since our packet members are not 8 bit aligned. We assure that each 233 | * packet is smaller that 128 bits, pack all packet members into a 128 bit 234 | * integer and read it out to bytes through the union. Since uint8_t doesn't 235 | * count as a char the compiler assumes a uint8_t pointer can't alias a 236 | * __uint128_t, thus the union. Or we could use -fno-strict-aliasing. 237 | */ 238 | union trdb_pack { 239 | __uint128_t bits; /* non-portable gcc stuff. TODO: fix */ 240 | uint8_t bin[16]; /* since uint8_t =/= char strict aliasing might 241 | * mess your shit up. Careful this is also 242 | * endian dependent 243 | */ 244 | }; 245 | 246 | struct trdb_config; 247 | 248 | /** 249 | * Library/trace debugger context, needs to be hold by program and passed to 250 | * quite a few functions. 251 | */ 252 | struct trdb_ctx; 253 | 254 | /** 255 | * Reset the trace debugger context to a clean state, do this before calling a 256 | * sequence of trdb_compress_trace_step(). 257 | * 258 | * @param ctx trace debugger context 259 | */ 260 | void trdb_reset_compression(struct trdb_ctx *ctx); 261 | 262 | /** 263 | * Reset the trace debugger context to a clean state, do this before calling a 264 | * trdb_decompress_trace(). 265 | * 266 | * @param ctx trace debugger context 267 | */ 268 | void trdb_reset_decompression(struct trdb_ctx *ctx); 269 | 270 | /** 271 | * Creates trdb library/trace debugger context. Fills in default values. Set 272 | * $TRDB_LOG for different default logging level. 273 | * 274 | * @return a new trdb library context 275 | */ 276 | struct trdb_ctx *trdb_new(void); 277 | 278 | /** 279 | * Destroys a trdb context. 280 | * 281 | * @param ctx a trace debugger context to destroy 282 | */ 283 | void trdb_free(struct trdb_ctx *ctx); 284 | 285 | /** 286 | * Hook a custom logging function for the specified trace debugger context. Per 287 | * default there is a function that logs to stderr. 288 | * 289 | * @param ctx a trace debugger context 290 | * @param log_fn a custom logging function 291 | */ 292 | void trdb_set_log_fn(struct trdb_ctx *ctx, 293 | void (*log_fn)(struct trdb_ctx *ctx, int priority, 294 | const char *file, int line, const char *fn, 295 | const char *format, va_list args)); 296 | 297 | /** 298 | * Get the current logging priority. The value controls which messages are 299 | * logged. 300 | * 301 | * @param ctx a trace debugger context 302 | * @return the current logging priority 303 | */ 304 | int trdb_get_log_priority(struct trdb_ctx *ctx); 305 | 306 | /** 307 | * Set the current logging priority. The value controls which messages are 308 | * logged. 309 | * 310 | * @param ctx a trace debugger context 311 | * @param priority the new logging priority 312 | */ 313 | void trdb_set_log_priority(struct trdb_ctx *ctx, int priority); 314 | 315 | /** 316 | * Set the disassembling context used in @p ctx. 317 | * 318 | * @param ctx a trace debugger context 319 | * @param dunit the configured disassembler 320 | */ 321 | void trdb_set_dunit(struct trdb_ctx *ctx, struct disassembler_unit *dunit); 322 | 323 | /** 324 | * Get the disassemble context used in @p ctx. 325 | * 326 | * @param ctx a trace debugger context 327 | * @return the current disassembler used 328 | */ 329 | struct disassembler_unit *trdb_get_dunit(struct trdb_ctx *ctx); 330 | 331 | /** 332 | * Get if we are always generating full address packets and don't do compression 333 | * by omitting sign bits. Otherwise we also generate differential address 334 | * packets and always compress the sign bits. 335 | * 336 | * @param ctx a trace debugger context 337 | * @return the address compression behaviour 338 | */ 339 | bool trdb_is_full_address(struct trdb_ctx *ctx); 340 | 341 | /** 342 | * Set if we want to always generate full address packets and don't do 343 | * compression by omitting sign bits. Otherwise we also generate differential 344 | * address packets and always compress the sign bits. 345 | * 346 | * @param ctx a trace debugger context 347 | * @param v address compression behaviour 348 | */ 349 | void trdb_set_full_address(struct trdb_ctx *ctx, bool v); 350 | 351 | /** 352 | * Get whether currently packets are not generated for ret instructions. If 353 | * enabled, this invokes the specific detection of ret instructions (which is 354 | * just an alias for a jalr instruction) and prevents the emission of a packet. 355 | * We can do this since if the decoder keeps track of the called functions and 356 | * thus the RAS (return address stack) it can infer where a ret instruction 357 | * points to. 358 | * 359 | * @param ctx a trace debugger context 360 | * @return generate additional packets 361 | */ 362 | bool trdb_get_implicit_ret(struct trdb_ctx *ctx); 363 | 364 | /** 365 | * Set whether a ret instruction should generate a packet. If enabled, this 366 | * invokes the specific detection of ret instructions (which is just an alias 367 | * for a jalr instruction) and prevents the emission of a packet. We can do this 368 | * since if the decoder keeps track of the called functions and thus the RAS 369 | * (return address stack) it can infer where a ret instruction points to. 370 | * 371 | * @param ctx a trace debugger context 372 | * @param implicit_ret true to prevent additional packets 373 | */ 374 | void trdb_set_implicit_ret(struct trdb_ctx *ctx, bool implicit_ret); 375 | 376 | /** 377 | * Get whether currently an extra packet is generated for an exception. This is 378 | * useful for the PULP platform since this allows us to figure out where the 379 | * vector table entries point to (which are initialized during runtime). 380 | * 381 | * @param ctx a trace debugger context 382 | * @return whether an extra packet is generated 383 | */ 384 | bool trdb_get_pulp_extra_packet(struct trdb_ctx *ctx); 385 | 386 | /** 387 | * Set whether we should generated an additional packet after an exception 388 | * packet. This is useful for the PULP platform since this allows us to figure 389 | * out where the vector table entries point to (which are initialized during 390 | * runtime). 391 | * 392 | * @param ctx a trace debugger context 393 | * @param extra_packet whether an extra packet should be generated 394 | */ 395 | void trdb_set_pulp_extra_packet(struct trdb_ctx *ctx, bool extra_packet); 396 | 397 | /** 398 | * Get whether full branch maps are compressed by omitting bits that can be 399 | * inferred using sign extension. 400 | * 401 | * @param ctx a trace debugger context 402 | * @return if we compress full branch maps 403 | */ 404 | bool trdb_get_compress_branch_map(struct trdb_ctx *ctx); 405 | 406 | /** 407 | * Set whether full branch maps should be compressed by omitting bits that can 408 | * be inferred using sign extension. 409 | * 410 | * @param ctx a trace debugger context 411 | * @param compress the new compression behaviour 412 | */ 413 | void trdb_set_compress_branch_map(struct trdb_ctx *ctx, bool compress); 414 | 415 | /** 416 | * Get the current number of bits of all the payloads which were produced 417 | * by calling trdb_compress_trace_step(). 418 | * 419 | * @param ctx a trace debugger context 420 | * @return number of bits of all payloads 421 | */ 422 | size_t trdb_get_payloadbits(struct trdb_ctx *ctx); 423 | 424 | /** 425 | * Get the current number of bits of required to store all packets which were 426 | * produced by calling trdb_compress_trace_step(). This includes all overhead 427 | * including quantization loss because pulp packets are always byte aligned. 428 | * 429 | * @param ctx a trace debugger context 430 | * @return number of bits of all pulp packets 431 | */ 432 | size_t trdb_get_pulpbits(struct trdb_ctx *ctx); 433 | 434 | /** 435 | * Get the current number of emitted packets which were produced by calling 436 | * trdb_compress_trace_step(). 437 | * 438 | * @param ctx a trace debugger context 439 | * @return number of emitted packets 440 | */ 441 | size_t trdb_get_packetcnt(struct trdb_ctx *ctx); 442 | 443 | /** 444 | * Get the current number of instructions that went through 445 | * trdb_compress_trace_step(). 446 | * 447 | * @param ctx a trace debugger context 448 | * @return number of instructions that were compressed 449 | */ 450 | size_t trdb_get_instrcnt(struct trdb_ctx *ctx); 451 | 452 | /** 453 | * Get the current number of instruction bits that went through 454 | * trdb_compress_trace_step(). This considers RVC instructions. 455 | * 456 | * @param ctx a trace debugger context 457 | * @return number of instruction bits 458 | */ 459 | size_t trdb_get_instrbits(struct trdb_ctx *ctx); 460 | 461 | /** 462 | * Get packet generation statistics of trdb_compress_trace_step() through a 463 | * trdb_packet_stats struct: 464 | * 465 | * @param ctx a trace debugger context 466 | * @param stats a packet statistics struct 467 | */ 468 | void trdb_get_packet_stats(struct trdb_ctx *ctx, 469 | struct trdb_packet_stats *stats); 470 | 471 | /** 472 | * Compute the number of equal (all ones or all zeros) leading bits in @p addr. 473 | * 474 | * @param addr the address 475 | * @return the number of leading equal bits in @p addr 476 | */ 477 | uint32_t trdb_sign_extendable_bits(addr_t addr); 478 | 479 | /** 480 | * Compress the given sequence of instruction to a sequence of packets. 481 | * 482 | * The packets should allow trdb_decompress_trace() to reconstruct the original 483 | * sequence of tr_instr. You can pass one instruction per call to it, simulating 484 | * step by step execution. The state of the compression (keeping the previously 485 | * fed instructions to make decisions on whether to emit a packet or not and 486 | * more) is held in @p ctx, which the caller is responsible for. The packet 487 | * information is written to @p packet. 488 | * 489 | * @param ctx trace debugger context/state 490 | * @param packet will be filled with data 491 | * @param instr the next instruction to compress 492 | * @return 0 or a positive number of generated packets on success, a negative 493 | * error code otherwise 494 | * @return -trdb_invalid if @p ctx, @p packet or @p instr is NULL 495 | * @return -trdb_bad_instr if an unsupported instruction was passed through @p 496 | * instr 497 | * @return -trdb_unimplemented if an unimplemented variable generated a packet 498 | */ 499 | int trdb_compress_trace_step(struct trdb_ctx *ctx, struct tr_packet *packet, 500 | struct tr_instr *instr); 501 | /** 502 | * A convenience wrapper for trdb_compress_trace_step() which interacts with 503 | * trdb_packet_head directly and handles allocation of packet. 504 | 505 | * Since the function allocates new entries for trdb_packet_head, the caller has 506 | * to deallocate them by calling trdb_free_packet_list(). Use the functions 507 | * provided by queue.h to handle trdb_packet_head entries. 508 | * 509 | * @param ctx trace debugger context/state 510 | * @param packet_list list to add packet to 511 | * @param instr the next instruction to compress 512 | * @return 0 or a positive number of generated packets on success, a negative 513 | * error code otherwise 514 | * @return -trdb_invalid if @p ctx, @p packet_list or @p instr is NULL 515 | * @return -trdb_bad_instr if an unsupported instruction was passed through @p 516 | * instr 517 | * @return -trdb_unimplemented if an unimplemented variable generated a packet 518 | * @return -trdb_nomem if out of memory 519 | */ 520 | int trdb_compress_trace_step_add(struct trdb_ctx *ctx, 521 | struct trdb_packet_head *packet_list, 522 | struct tr_instr *instr); 523 | 524 | /** 525 | * Generate the original instruction sequence from a list of tr_packet, given 526 | * the binary from which the instruction sequence was produced. 527 | * 528 | * @param c the context/state of the trace debugger 529 | * @param abfd the binary from which the trace was captured 530 | * @param packet_list the compressed instruction trace 531 | * @param instr_list list to which the reconstruction instructions will be 532 | * appended 533 | * @return 0 on success, a negative error code otherwise 534 | * @return -trdb_invalid if @p c, @p abfd, @p packet_list or @p instr_list is 535 | * NULL 536 | * @return -trdb_bad_vma if the vma does not point to any section 537 | * @return -trdb_nomem if out of memory 538 | * @return -trdb_section_empty if section contents could not be be loaded 539 | * @return -trdb_bad_instr if an instruction was encountered that could not be 540 | * decoded 541 | * @return -trdb_bad_config if the decoding assumptions do not hold because of 542 | * contradictionary data, e.g. assuming full_address=true when encountering a 543 | * F_BRANCH_DIFF packet 544 | */ 545 | int trdb_decompress_trace(struct trdb_ctx *c, bfd *abfd, 546 | struct trdb_packet_head *packet_list, 547 | struct trdb_instr_head *instr_list); 548 | 549 | /** 550 | * Outputs disassembled trace using fprintf_func in #disassembler_unit.dinfo. 551 | * 552 | * @param len length of trace 553 | * @param trace an array of instructions, only #iaddr and #instr are needed 554 | * @param dunit the configured disassembler 555 | */ 556 | void trdb_disassemble_trace(size_t len, struct tr_instr *trace, 557 | struct disassembler_unit *dunit); 558 | 559 | /** 560 | * Outputs disassembled trace using fprintf_func in #disassembler_unit.dinfo and 561 | * furthermore uses information from abfd to produce objdump-like output. The 562 | * exact output can be configured by calling trdb_set_disassemble_conf() with 563 | * the appropriate flags. 564 | * 565 | * @param c the context/state of the trace debugger 566 | * @param len length of trace 567 | * @param trace an array of instrutions 568 | * @param abfd the bfd that corresponds to the @p trace 569 | * @param dunit the configured disassembler 570 | */ 571 | void trdb_disassemble_trace_with_bfd(struct trdb_ctx *c, size_t len, 572 | struct tr_instr trace[len], bfd *abfd, 573 | struct disassembler_unit *dunit); 574 | 575 | /** 576 | * Output disassemble @p instr using fprintf_func in #disassembler_unit.dinfo. 577 | * 578 | * @param instr instruction, only #iaddr and #instr are needed 579 | * @param dunit the configured disassembler 580 | */ 581 | void trdb_disassemble_instr(struct tr_instr *instr, 582 | struct disassembler_unit *dunit); 583 | 584 | /** 585 | * Disassemble @p instr using fprint_func in #disassembler_unit.dinfo and 586 | * additionaly information from the @p abfd and settings in @p dunit to produce 587 | * more useful disassembly. Options such as intermixing sourcecode, showing file 588 | * and line numbers, resolving addresses to symbols etc. are available. See 589 | * trdb_disassemble_trace_with_bfd() and trdb_set_disassembly_conf() for more 590 | * information. 591 | * 592 | * @param c trace debugger context 593 | * @param instr instruction to disassemble, only #iaddr is relevant 594 | * @param abfd the bfd which contains @p instr 595 | * @param dunit the configured disassembler 596 | */ 597 | void trdb_disassemble_instr_with_bfd(struct trdb_ctx *c, struct tr_instr *instr, 598 | bfd *abfd, 599 | struct disassembler_unit *dunit); 600 | 601 | /** 602 | * Prints a list of tr_packet in a formatted manner to @p stream. 603 | * 604 | * @param stream output to write to 605 | * @param packet_list sequence of packets to print 606 | */ 607 | void trdb_dump_packet_list(FILE *stream, 608 | const struct trdb_packet_head *packet_list); 609 | 610 | /** 611 | * Prints a list of tr_instr in a formatted manner to @p stream. 612 | * 613 | * @param stream output to write to 614 | * @param instr_list sequence of instructions to print 615 | */ 616 | void trdb_dump_instr_list(FILE *stream, 617 | const struct trdb_instr_head *instr_list); 618 | 619 | /** 620 | * Log a single packet in a formatted manner using the library context @p c. 621 | * 622 | * @param c the context of the trace debugger 623 | * @param packet tr_packet to log 624 | */ 625 | void trdb_log_packet(struct trdb_ctx *c, const struct tr_packet *packet); 626 | 627 | /** 628 | * Prints a single packet in a formatted manner to @p stream. 629 | * 630 | * @param stream output to write to 631 | * @param packet tr_packet to print 632 | */ 633 | void trdb_print_packet(FILE *stream, const struct tr_packet *packet); 634 | 635 | /** 636 | * Log a single instruction in a formatted manner using the library context @p 637 | * c. 638 | * 639 | * @param c the context of the trace debugger 640 | * @param instr tr_instr to log 641 | */ 642 | void trdb_log_instr(struct trdb_ctx *c, const struct tr_instr *instr); 643 | 644 | /** 645 | * Prints a single instruction in a formatted manner to @p stream. 646 | * 647 | * @param stream output to write to 648 | * @param instr tr_instr to print 649 | */ 650 | void trdb_print_instr(FILE *stream, const struct tr_instr *instr); 651 | 652 | /** 653 | * Compare two instructions for memberwise equality; 654 | * 655 | * @param c the context of the trace debugger for logging 656 | * @param instr0 first instruction of the comparison 657 | * @param instr1 second instruction of the comparison 658 | * 659 | * @return whether both instruction are memberwise equal 660 | */ 661 | bool trdb_compare_instr(struct trdb_ctx *c, const struct tr_instr *instr0, 662 | const struct tr_instr *instr1); 663 | 664 | /** 665 | * Free the entries of a list of tr_packet, used to deallocate the list returned 666 | * by trdb_compress_trace(). 667 | * 668 | * @param packet_list list to free 669 | */ 670 | void trdb_free_packet_list(struct trdb_packet_head *packet_list); 671 | 672 | /** 673 | * Free the entries of a list of tr_instr, used to deallocate the list returned 674 | * by trdb_decompress_trace(). 675 | * 676 | * @param instr_list list to free 677 | */ 678 | void trdb_free_instr_list(struct trdb_instr_head *instr_list); 679 | 680 | /* struct packet0 { 681 | * uint32_t format : 2; // 00 682 | * uint32_t branches : 5; 683 | * uint32_t branch_map; //TODO: fix to 31 bits 684 | * addr_t address; 685 | * 686 | * 2 + 5 + (1 to 31) + (1 to 32) = 9 to 70 687 | * 688 | * struct packet1 { 689 | * uint32_t format : 2; // 01 690 | * uint32_t branches : 5; 691 | * uint32_t branch_map; 692 | * addr_t address; 693 | * }; 694 | * 2 + 5 + (1 to 31) + (1 to 32) = 9 to 70 695 | * 696 | * struct packet2 { 697 | * uint32_t format : 2; 698 | * addr_t address; 699 | * }; 700 | * 2 + (1 to 32) = 3 to 34 701 | * 702 | * struct packet3 { 703 | * uint32_t format : 2; 704 | * uint32_t subformat : 2; 705 | * uint32_t context; 706 | * uint32_t privilege : PRIVLEN; 707 | * bool branch : 1; 708 | * addr_t address : XLEN; 709 | * uint32_t ecause : CAUSELEN; 710 | * bool interrupt : 1; 711 | * uint32_t tval : XLEN; 712 | * }; 713 | * 714 | * 2 + 2 + 3 + 1 + 32 + 5 + 1 = 46 715 | */ 716 | 717 | #ifdef __cplusplus 718 | } 719 | #endif 720 | 721 | #endif 722 | -------------------------------------------------------------------------------- /include/trdb_sv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Trace Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | /** 20 | * @file trdb_sv.h 21 | * @author Robert Balas (balasr@student.ethz.ch) 22 | * @brief Functions of the golden model which are called from the simulator 23 | */ 24 | 25 | #ifndef __TRDB_SV_H__ 26 | #define __TRDB_SV_H__ 27 | 28 | #include "svdpi.h" 29 | 30 | /** 31 | * Allocate trdb library context. Ideally the simulator should hold the library 32 | * context handle but that functionality is still missing and so currently the 33 | * library does that. 34 | */ 35 | void trdb_sv_alloc(); 36 | 37 | /** 38 | * Configure address compression behaviour of trdb. Setting full_address to a 39 | * non-zero value disables address compression. 40 | */ 41 | void trdb_sv_set_full_address(int full_address); 42 | 43 | /** 44 | * Run the trace compression algorithm step by step. Each call is equivalent to 45 | * advancing one cycle in the RTL model. Normally we should pass a struct but 46 | * here we just flattened it, which makes the parameter list a bit unwieldy. 47 | * 48 | * @param ivalid if the provided data is valid 49 | * @param iexception if this instruction is an exception 50 | * @param interrupt whether this is an interrupt or exception 51 | * @param cause interrupt/exception cause 52 | * @param tval trap value 53 | * @param instr the uncompressed version of the instruction 54 | * @param compressed whether the instruction was originally compressed 55 | * @param packet_max_len number of bits in @p packet_bits 56 | * @param packet_bits the bits representing a serialized packet 57 | * @param packet_valid whether we generated a packet this call/cycle 58 | */ 59 | void trdb_sv_feed_trace(svLogic ivalid, svLogic iexception, svLogic interrupt, 60 | const svLogicVecVal *cause, const svLogicVecVal *tval, 61 | const svLogicVecVal *priv, const svLogicVecVal *iaddr, 62 | const svLogicVecVal *instr, svLogic compressed, 63 | int packet_max_len, svLogicVecVal *packet_bits, 64 | svLogic *packet_valid); 65 | 66 | /** 67 | * Free all resources of allocated by trdb_sv_alloc. 68 | * 69 | */ 70 | void trdb_sv_free(); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /internal/disassembly_private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Trace Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /** 21 | * @file disassembly_private.h 22 | * @author Robert Balas (balasr@student.ethz.ch) 23 | * @brief Disassembly helper function used internally 24 | */ 25 | #include 26 | #include 27 | #include "trace_debugger.h" 28 | 29 | /* define functions which help figure out what function we are dealing with */ 30 | #define DECLARE_INSN(code, match, mask) \ 31 | static const uint32_t match_##code = match; \ 32 | static const uint32_t mask_##code = mask; \ 33 | static bool is_##code##_instr(long instr) \ 34 | { \ 35 | return (instr & mask) == match; \ 36 | } 37 | 38 | #include "riscv_encoding.h" 39 | #undef DECLARE_INSN 40 | 41 | /* ABI names for x-registers */ 42 | #define X_RA 1 43 | #define X_SP 2 44 | #define X_GP 3 45 | #define X_TP 4 46 | #define X_T0 5 47 | #define X_T1 6 48 | #define X_T2 7 49 | #define X_T3 28 50 | 51 | #define OP_MASK_RD 0x1f 52 | #define OP_SH_RD 7 53 | #define MASK_RD (OP_MASK_RD << OP_SH_RD) 54 | 55 | #define OP_MASK_RS1 0x1f 56 | #define OP_SH_RS1 15 57 | #define MASK_RS1 (OP_MASK_RS1 << OP_SH_RS1) 58 | 59 | #define RV_X(x, s, n) (((x) >> (s)) & ((1 << (n)) - 1)) 60 | #define ENCODE_ITYPE_IMM(x) (RV_X(x, 0, 12) << 20) 61 | #define MASK_IMM ENCODE_ITYPE_IMM(-1U) 62 | 63 | static bool is_really_c_jalr_instr(long instr) 64 | { 65 | /* we demand that rd is nonzero */ 66 | return is_c_jalr_instr(instr) && ((instr & MASK_RD) != 0); 67 | } 68 | 69 | static bool is_really_c_jr_instr(long instr) 70 | { 71 | /* we demand that rd is nonzero */ 72 | return is_c_jr_instr(instr) && ((instr & MASK_RD) != 0); 73 | } 74 | 75 | static bool is_c_ret_instr(long instr) 76 | { 77 | return (instr & (MASK_C_JR | MASK_RD)) == (MATCH_C_JR | (X_RA << OP_SH_RD)); 78 | } 79 | 80 | static bool is_ret_instr(long instr) 81 | { 82 | return (instr & (MASK_JALR | MASK_RD | MASK_RS1 | MASK_IMM)) == 83 | (MATCH_JALR | (X_RA << OP_SH_RS1)); 84 | } 85 | 86 | enum trdb_ras { none = 0, ret = 1, call = 2, coret = 3 }; 87 | 88 | static enum trdb_ras is_jalr_funcall(insn_t instr) 89 | { 90 | /* According the the calling convention outlined in the risc-v spec we are 91 | * dealing with a function call if instr == jalr and rd=x1/x5=link. 92 | * Furthermore the following hints for the RAS (return address stack) are 93 | * encoded using rs and rd: 94 | * 95 | *(rd=!link && rs=link pop aka ret) 96 | * rd=link && rs!=link push 97 | * rd=link && rs=link && rd!=rs push and pop, used for coroutines 98 | * rd=link && rs=link && rd==rs push 99 | * 100 | * This is table 2.1 in riscv-spec-v2.2 101 | */ 102 | bool is_jalr = is_jalr_instr(instr); 103 | uint32_t rd = (instr & MASK_RD) >> OP_SH_RD; 104 | uint32_t rs = (instr & MASK_RS1) >> OP_SH_RS1; 105 | bool is_rd_link = (rd == X_RA) || (rd == X_T0); 106 | bool is_rs_link = (rs == X_RA) || (rs == X_T0); 107 | bool is_eq_link = is_rd_link && is_rs_link && rd == rs; 108 | 109 | if (is_jalr && !is_rd_link && is_rs_link) 110 | return ret; 111 | else if (is_jalr && is_rd_link && !is_rs_link) 112 | return call; 113 | else if (is_jalr && is_rd_link && is_rs_link && !is_eq_link) 114 | return coret; 115 | else if (is_jalr && is_rd_link && is_rs_link && is_eq_link) 116 | return ret; 117 | else 118 | return none; 119 | } 120 | 121 | static enum trdb_ras is_c_jalr_funcall(insn_t instr) 122 | { 123 | /* C.JALR expands to jalr x1=X_RA, rs1, 0 */ 124 | bool is_c_jalr = is_really_c_jalr_instr(instr); 125 | uint32_t rs = (instr & MASK_RD) >> OP_SH_RD; 126 | bool is_rs_link = (rs == X_RA) || (rs == X_T0); 127 | if (!is_c_jalr) 128 | return none; 129 | 130 | /* this is again derived from table 2.1 in riscv-spec-v2.2 */ 131 | if (is_rs_link && (X_RA != rs)) 132 | return coret; 133 | else 134 | return call; 135 | } 136 | 137 | static enum trdb_ras is_jal_funcall(insn_t instr) 138 | { 139 | /* if jal with rd=x1/x5 then we know it's a function call */ 140 | bool is_jal = is_jal_instr(instr); 141 | bool is_rd_link = (instr & MASK_RD) == (X_RA << OP_SH_RD) || 142 | (instr & MASK_RD) == (X_T0 << OP_SH_RD); 143 | if (is_jal && is_rd_link) 144 | return call; 145 | else 146 | return none; 147 | } 148 | 149 | static enum trdb_ras is_c_jal_funcall(insn_t instr) 150 | { 151 | /* C.JAL expands to jal x1, offset[11:1] */ 152 | bool is_c_jal = is_c_jal_instr(instr); 153 | if (is_c_jal) 154 | return call; 155 | else 156 | return none; 157 | } 158 | 159 | static enum trdb_ras get_instr_ras_type(insn_t instr) 160 | { 161 | enum trdb_ras type = none; 162 | if (is_jalr_instr(instr)) { 163 | type = is_jalr_funcall(instr); 164 | } else if (is_jal_instr(instr)) { 165 | type = is_jal_funcall(instr); 166 | } else if (is_really_c_jalr_instr(instr)) { 167 | type = is_c_jalr_funcall(instr); 168 | } else if (is_c_jal_instr(instr)) { 169 | #ifndef TRDB_ARCH64 170 | type = call; /* C.JAL expands to jal x1, offset [11:1], RV32 only */ 171 | #endif 172 | } else if (is_c_ret_instr(instr)) { 173 | type = ret; 174 | } 175 | return type; 176 | } 177 | 178 | /** 179 | * Computes the length of a the given RISC-V instruction instr. 180 | * 181 | * So far only up to 64 bit instructions are supported. 182 | * 183 | * @param instr raw instruction value up to 64 bit long 184 | * @return number of bytes which make up the instruction 185 | */ 186 | static inline unsigned int riscv_instr_len(uint64_t instr) 187 | { 188 | if ((instr & 0x3) != 0x3) /* RVC. */ 189 | return 2; 190 | if ((instr & 0x1f) != 0x1f) /* Base ISA and extensions in 32-bit space. */ 191 | return 4; 192 | if ((instr & 0x3f) == 0x1f) /* 48-bit extensions. */ 193 | return 6; 194 | if ((instr & 0x7f) == 0x3f) /* 64-bit extensions. */ 195 | return 8; 196 | /* Longer instructions not supported at the moment. */ 197 | return 2; 198 | } 199 | -------------------------------------------------------------------------------- /internal/kvec.h: -------------------------------------------------------------------------------- 1 | /* The MIT License 2 | 3 | Copyright (c) 2008, by Attractive Chaos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | /* 27 | An example: 28 | 29 | #include "kvec.h" 30 | int main() { 31 | kvec_t(int) array; 32 | kv_init(array); 33 | kv_push(int, array, 10); // append 34 | kv_a(int, array, 20) = 5; // dynamic 35 | kv_A(array, 20) = 4; // static 36 | kv_destroy(array); 37 | return 0; 38 | } 39 | */ 40 | 41 | /* 42 | 2008-09-22 (0.1.0): 43 | 44 | * The initial version. 45 | 46 | */ 47 | 48 | #ifndef AC_KVEC_H 49 | #define AC_KVEC_H 50 | 51 | #include 52 | 53 | #define kv_roundup32(x) \ 54 | (--(x), (x) |= (x) >> 1, (x) |= (x) >> 2, (x) |= (x) >> 4, \ 55 | (x) |= (x) >> 8, (x) |= (x) >> 16, ++(x)) 56 | 57 | #define kvec_nt(name, type) \ 58 | struct name { \ 59 | size_t n, m; \ 60 | type *a; \ 61 | } 62 | 63 | #define kvec_t(type) \ 64 | struct { \ 65 | size_t n, m; \ 66 | type *a; \ 67 | } 68 | #define kv_init(v) ((v).n = (v).m = 0, (v).a = 0) 69 | #define kv_destroy(v) free((v).a) 70 | #define kv_A(v, i) ((v).a[(i)]) 71 | #define kv_pop(v) ((v).a[--(v).n]) 72 | #define kv_size(v) ((v).n) 73 | #define kv_max(v) ((v).m) 74 | 75 | #define kv_resize(type, v, s) \ 76 | ((v).m = (s), (v).a = (type *)realloc((v).a, sizeof(type) * (v).m)) 77 | 78 | #define kv_copy(type, v1, v0) \ 79 | do { \ 80 | if ((v1).m < (v0).n) \ 81 | kv_resize(type, v1, (v0).n); \ 82 | (v1).n = (v0).n; \ 83 | memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \ 84 | } while (0) 85 | 86 | #define kv_push(type, v, x) \ 87 | do { \ 88 | if ((v).n == (v).m) { \ 89 | (v).m = (v).m ? (v).m << 1 : 2; \ 90 | (v).a = (type *)realloc((v).a, sizeof(type) * (v).m); \ 91 | } \ 92 | (v).a[(v).n++] = (x); \ 93 | } while (0) 94 | 95 | #define kv_pushp(type, v) \ 96 | (((v).n == (v).m) \ 97 | ? ((v).m = ((v).m ? (v).m << 1 : 2), \ 98 | (v).a = (type *)realloc((v).a, sizeof(type) * (v).m), 0) \ 99 | : 0), \ 100 | ((v).a + ((v).n++)) 101 | 102 | #define kv_a(type, v, i) \ 103 | (((v).m <= (size_t)(i) \ 104 | ? ((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \ 105 | (v).a = (type *)realloc((v).a, sizeof(type) * (v).m), 0) \ 106 | : (v).n <= (size_t)(i) ? (v).n = (i) + 1 : 0), \ 107 | (v).a[(i)]) 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /internal/trdb_private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /** 21 | * @file trdb_private.h 22 | * @author Robert Balas (balasr@student.ethz.ch) 23 | * @brief Software model for the hardware trace debugger. 24 | */ 25 | 26 | #include 27 | 28 | uint32_t branch_map_len(uint32_t branches); 29 | -------------------------------------------------------------------------------- /internal/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Trace Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /** 21 | * @file utils.h 22 | * @author Robert Balas (balasr@student.ethz.ch) 23 | * @brief Utility and logging functions/macros 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "trace_debugger.h" 31 | 32 | #ifdef NDEBUG 33 | # define TRDB_LEVEL 1 34 | # define TRDB_TRACE 0 35 | # define TRDB_VERBOSE_TESTS 0 36 | #else 37 | # ifdef VERBOSE 38 | # define TRDB_LEVEL 3 39 | # define TRDB_TRACE 1 40 | # define TRDB_VERBOSE_TESTS 1 41 | # else 42 | # define TRDB_LEVEL 1 43 | # define TRDB_TRACE 0 44 | # define TRDB_VERBOSE_TESTS 0 45 | # endif 46 | #endif 47 | 48 | /* Different non-portable way to calculate packed bits requires __uint128_t 49 | * and little endian (or add the htole128 function) 50 | */ 51 | static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, 52 | "Machine must be little endian"); 53 | 54 | /* static inline void __attribute__((always_inline, format(printf, 2, 3))) */ 55 | /* trdb_log_null(struct trdb_ctx *ctx, const char *format, ...) */ 56 | /* { */ 57 | /* } */ 58 | 59 | void trdb_log_null(struct trdb_ctx *ctx, const char *format, ...); 60 | 61 | #define trdb_log_cond(ctx, prio, arg...) \ 62 | do { \ 63 | if (trdb_get_log_priority(ctx) >= prio) \ 64 | trdb_log(ctx, prio, __FILE__, __LINE__, __FUNCTION__, ##arg); \ 65 | } while (0) 66 | 67 | #ifdef ENABLE_LOGGING 68 | # ifdef ENABLE_DEBUG 69 | # define dbg(ctx, arg...) trdb_log_cond(ctx, LOG_DEBUG, ##arg) 70 | # else 71 | # define dbg(ctx, arg...) trdb_log_null(ctx, ##arg) 72 | # endif 73 | # define info(ctx, arg...) trdb_log_cond(ctx, LOG_INFO, ##arg) 74 | # define err(ctx, arg...) trdb_log_cond(ctx, LOG_ERR, ##arg) 75 | #else 76 | # define dbg(ctx, arg...) trdb_log_null(ctx, ##arg) 77 | # define info(ctx, arg...) trdb_log_null(ctx, ##arg) 78 | # define err(ctx, arg...) trdb_log_null(ctx, ##arg) 79 | #endif 80 | 81 | #ifdef HAVE_SECURE_GETENV 82 | # ifdef HAVE___SECURE_GETENV 83 | # define secure_getenv __secure_getenv 84 | # else 85 | # error neither secure_getenv nor __secure_getenv is available 86 | # endif 87 | #else 88 | # define secure_getenv getenv 89 | #endif 90 | 91 | #define TRDB_EXPORT __attribute__((visibility("default"))) 92 | 93 | void trdb_log(struct trdb_ctx *ctx, int priority, const char *file, int line, 94 | const char *fn, const char *format, ...) 95 | __attribute__((format(printf, 6, 7))); 96 | 97 | /** 98 | * Write formatted string to stderr with the filename, linenumber and function 99 | * prepended. Only used for tests. 100 | */ 101 | #define LOG_ERRT(format, ...) \ 102 | do { \ 103 | if (TRDB_LEVEL > 0) \ 104 | fprintf(stderr, "tests: %s:%d:0: %s(): " format, __FILE__, \ 105 | __LINE__, __func__, ##__VA_ARGS__); \ 106 | } while (false) 107 | /* ## is gcc extension */ 108 | 109 | /** 110 | * Write formatted string to stderr with the filename, linenumber and function 111 | * prepended. Only used for tests. 112 | */ 113 | #define LOG_WARNT(format, ...) \ 114 | do { \ 115 | if (TRDB_LEVEL > 1) \ 116 | fprintf(stderr, "tests: %s:%d:0: %s(): " format, __FILE__, \ 117 | __LINE__, __func__, ##__VA_ARGS__); \ 118 | } while (false) 119 | 120 | /** 121 | * Write formatted string to stdout with the filename, linenumber and function 122 | * prepended. Only used for tests. 123 | */ 124 | #define LOG_INFOT(format, ...) \ 125 | do { \ 126 | if (TRDB_LEVEL > 2) \ 127 | fprintf(stdout, "tests: %s:%d:0: %s(): " format, __FILE__, \ 128 | __LINE__, __func__, ##__VA_ARGS__); \ 129 | } while (false) 130 | 131 | /** 132 | * Write formatted string to stdout with the filename, linenumber and function 133 | * prepended. Only used for tests. 134 | */ 135 | #define LOG_TRACET(format, ...) \ 136 | do { \ 137 | if (TRDB_TRACE) \ 138 | fprintf(stdout, "tests: %s:%d:0: %s(): " format, __FILE__, \ 139 | __LINE__, __func__, ##__VA_ARGS__); \ 140 | } while (false) 141 | 142 | /* TODO: make this typesafer */ 143 | /** 144 | * Return the size of an array in bytes. 145 | */ 146 | #define TRDB_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 147 | 148 | /** 149 | * Generate a mask with the first @p len bits set. @p len need to be smaller 150 | * or equal to 64. 151 | */ 152 | #define MASK_FROM(len) (len != 64 ? (((uint64_t)1 << len) - 1) : (uint64_t)-1) 153 | 154 | /** 155 | * Shift @p x by any value 0 <= shv <= XLEN to the right. 156 | */ 157 | #define SHR(x, shv) (shv != XLEN ? ((addr_t)x >> shv) : (addr_t)0) 158 | 159 | /** 160 | * Shift @p x by any value 0 <= shv <= XLEN to the left. 161 | */ 162 | #define SHL(x, shv) (shv != XLEN ? ((addr_t)x << shv) : (addr_t)0) 163 | 164 | /* bit manipulation utility functions */ 165 | 166 | uint32_t sign_extendable_bits(uint32_t addr); 167 | uint32_t sign_extendable_bits64(uint64_t addr); 168 | uint32_t sign_extendable_bits128(__uint128_t addr); 169 | uint32_t sext32(uint32_t val, uint32_t bit); 170 | __uint128_t sext128(__uint128_t val, __uint128_t bit); 171 | int clz_u128(__uint128_t u); 172 | -------------------------------------------------------------------------------- /src/dpi/trdb_sv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Trace Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /** 21 | * @file trdb_sv.c 22 | * @author Robert Balas (balasr@student.ethz.ch) 23 | * @brief Interface for the system verilog testbench 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "svdpi.h" 30 | /* #include "autogen_trdb_sv.h" */ 31 | #include "trdb_sv.h" 32 | #include "utils.h" 33 | #include "trace_debugger.h" 34 | #include "serialize.h" 35 | 36 | struct trdb_packet_head packets = TAILQ_HEAD_INITIALIZER(packets); 37 | 38 | /* TODO: send context to simulator per userdata methods */ 39 | 40 | struct trdb_ctx *ctx; 41 | int packetcnt = 0; 42 | 43 | /* We print even errors to stdout since the simulator doesn't interleave stderr 44 | * and stdout 45 | */ 46 | static void log_stdout_dpi(struct trdb_ctx *ctx, int priority, const char *file, 47 | int line, const char *fn, const char *format, 48 | va_list args) 49 | { 50 | (void)ctx; 51 | (void)priority; 52 | fprintf(stdout, "trdb-dpi: %s:%d:0: %s(): ", file, line, fn); 53 | vfprintf(stdout, format, args); 54 | } 55 | 56 | void trdb_sv_alloc() 57 | { 58 | ctx = trdb_new(); 59 | if (!ctx) { 60 | fprintf(stdout, "trdb-dpi: couldn't allocate trdb_ctx\n"); 61 | return; 62 | } 63 | trdb_set_log_fn(ctx, log_stdout_dpi); 64 | } 65 | 66 | void trdb_sv_free() 67 | { 68 | trdb_free_packet_list(&packets); 69 | free(ctx); 70 | } 71 | 72 | void trdb_sv_set_full_address(int full_address) 73 | { 74 | trdb_set_full_address(ctx, full_address); 75 | } 76 | 77 | void trdb_sv_set_implicit_ret(int implicit_ret) 78 | { 79 | trdb_set_implicit_ret(ctx, implicit_ret); 80 | } 81 | 82 | void trdb_sv_feed_trace(svLogic ivalid, svLogic iexception, svLogic interrupt, 83 | const svLogicVecVal *cause, const svLogicVecVal *tval, 84 | const svLogicVecVal *priv, const svLogicVecVal *iaddr, 85 | const svLogicVecVal *instr, svLogic compressed, 86 | int packet_max_len, svLogicVecVal *packet_bits, 87 | svLogic *packet_valid) 88 | { 89 | // Test if we got x or z 90 | if (ivalid > 1 || iexception > 1 || interrupt > 1 || cause->bval != 0 || 91 | tval->bval != 0 || priv->bval != 0 || iaddr->bval != 0 || 92 | instr->bval != 0 || compressed > 1) { 93 | err(ctx, 94 | "some values are 'x or 'z, implicitly converting to binary and continuing... \n"); 95 | } 96 | 97 | *packet_valid = 0; 98 | 99 | /* Note: aval and bval are uint32_t per standard */ 100 | struct tr_instr tr_instr = {.valid = ivalid, 101 | .exception = iexception, 102 | .interrupt = interrupt, 103 | .cause = cause->aval, 104 | .tval = tval->aval, 105 | .priv = priv->aval, 106 | .iaddr = iaddr->aval, /* FIXME: bad size */ 107 | .instr = instr->aval, 108 | .compressed = compressed}; 109 | 110 | /* upper bounded local buffer for serialization */ 111 | size_t bitcnt = 0; 112 | uint8_t buff[sizeof(struct tr_packet)] = {0}; 113 | svLogicVecVal tmp = {0}; 114 | struct tr_packet *latest_packet = NULL; 115 | 116 | int p = trdb_compress_trace_step_add(ctx, &packets, &tr_instr); 117 | 118 | if (!TAILQ_EMPTY(&packets)) { 119 | latest_packet = TAILQ_LAST(&packets, trdb_packet_head); 120 | } 121 | 122 | if (p == -1) 123 | err(ctx, "compression step failed\n"); 124 | if (p == 1) { 125 | if (trdb_pulp_serialize_packet(ctx, latest_packet, &bitcnt, 0, buff)) { 126 | err(ctx, "failed to serialize packet, continuing...\n"); 127 | } 128 | packetcnt++; 129 | // TODO: change that to debug 130 | info(ctx, "ID: %d\n", packetcnt); 131 | trdb_print_packet(stdout, latest_packet); 132 | 133 | /* SV_PACKED_DATA_NELEMS returns the number ov svLogicVecVal's, which 134 | * each have 4 bytes 135 | */ 136 | size_t total_bytes = SV_PACKED_DATA_NELEMS(packet_max_len) * 4; 137 | 138 | /* this is just a integer divions (ceiling) of bitcount/8 */ 139 | size_t packet_bytes = (bitcnt / 8 + (bitcnt % 8 != 0)); 140 | 141 | if ((size_t)packet_max_len / 8 < packet_bytes) { 142 | err(ctx, "packet size on the sv side is too small\n"); 143 | return; 144 | } 145 | 146 | /* fill in vector with packet and zero pad */ 147 | for (size_t i = 0; i < total_bytes; i++) { 148 | if (i < packet_bytes) 149 | tmp.aval = buff[i]; 150 | else 151 | tmp.aval = 0; 152 | tmp.bval = 0; 153 | 154 | svPutPartselLogic(packet_bits, tmp, i * 8, 8); 155 | } 156 | *packet_valid = 1; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/error.c: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Trace Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include "trace_debugger.h" 21 | 22 | const char *trdb_errstr(enum trdb_error_code err) 23 | { 24 | switch (err) { 25 | case trdb_ok: 26 | return "success"; 27 | 28 | case trdb_internal: 29 | return "internal error"; 30 | 31 | case trdb_invalid: 32 | return "invalid argument"; 33 | 34 | case trdb_bad_instr: 35 | return "unknown instruction"; 36 | 37 | case trdb_bad_packet: 38 | return "unknown packet"; 39 | 40 | case trdb_bad_config: 41 | return "bad configuration"; 42 | 43 | case trdb_bad_ras: 44 | return "bad internal return address stack"; 45 | case trdb_bad_cvs_header: 46 | return "bad cvs header"; 47 | 48 | case trdb_unimplemented: 49 | return "unimplemented"; 50 | 51 | case trdb_nomem: 52 | return "out of memory"; 53 | 54 | case trdb_file_open: 55 | return "cannot open file"; 56 | 57 | case trdb_file_size: 58 | return "cannot get file size"; 59 | 60 | case trdb_file_read: 61 | return "cannot read file"; 62 | 63 | case trdb_file_write: 64 | return "cannot write file"; 65 | 66 | case trdb_scan_file: 67 | return "cannot scan file"; 68 | 69 | case trdb_scan_state_invalid: 70 | return "scan state invalid, bad number of tokens"; 71 | 72 | case trdb_arch_support: 73 | return "architecture not supported"; 74 | 75 | case trdb_section_empty: 76 | return "section is empty"; 77 | 78 | case trdb_bad_vma: 79 | return "vma is pointing to no section"; 80 | } 81 | 82 | return "missing error string"; 83 | } 84 | 85 | enum trdb_error_code trdb_errcode(int status) 86 | { 87 | return status >= 0 ? trdb_ok : (enum trdb_error_code)(-status); 88 | } 89 | -------------------------------------------------------------------------------- /src/serialize.c: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "serialize.h" 28 | #include "trace_debugger.h" 29 | #include "trdb_private.h" 30 | #include "utils.h" 31 | 32 | /* pulp specific packet serialization */ 33 | int trdb_pulp_serialize_packet(struct trdb_ctx *c, struct tr_packet *packet, 34 | size_t *bitcnt, uint8_t align, uint8_t bin[]) 35 | { 36 | if (align >= 8) { 37 | err(c, "bad alignment value: %" PRId8 "\n", align); 38 | return -trdb_invalid; 39 | } 40 | 41 | union trdb_pack data = {0}; 42 | /* We put the number of bytes (without header) as the packet length. The 43 | * PULPPKTLEN, MSGTYPELEN and FORMATLEN are considered the header 44 | */ 45 | uint32_t bits_without_header = packet->length - FORMATLEN; 46 | uint32_t byte_len = 47 | bits_without_header / 8 + (bits_without_header % 8 != 0); 48 | if (byte_len >= 16) { // TODO: replace with pow or mask 49 | err(c, "bad packet length\n"); 50 | return -trdb_bad_packet; 51 | } 52 | 53 | switch (packet->format) { 54 | case F_BRANCH_FULL: { 55 | uint32_t len = branch_map_len(packet->branches); 56 | 57 | /* we need enough space to do the packing it in uint128 */ 58 | assert(128 > PULPPKTLEN + FORMATLEN + MSGTYPELEN + 5 + 31 + XLEN); 59 | /* TODO: assert branch map to overfull */ 60 | data.bits = byte_len | (packet->msg_type << PULPPKTLEN) | 61 | (packet->format << (PULPPKTLEN + MSGTYPELEN)) | 62 | (packet->branches << (PULPPKTLEN + MSGTYPELEN + FORMATLEN)); 63 | data.bits |= ((__uint128_t)packet->branch_map & MASK_FROM(len)) 64 | << (PULPPKTLEN + MSGTYPELEN + FORMATLEN + BRANCHLEN); 65 | 66 | *bitcnt = PULPPKTLEN + MSGTYPELEN + FORMATLEN + BRANCHLEN + len; 67 | 68 | /* if we have a full branch we don't necessarily need to emit address */ 69 | if (packet->branches > 0) { 70 | data.bits |= 71 | ((__uint128_t)packet->address 72 | << (PULPPKTLEN + MSGTYPELEN + FORMATLEN + BRANCHLEN + len)); 73 | if (trdb_is_full_address(c)) 74 | *bitcnt += XLEN; 75 | else 76 | *bitcnt += 77 | (XLEN - trdb_sign_extendable_bits(packet->address) + 1); 78 | } else { 79 | /* no address, but compress full branch map*/ 80 | if (trdb_get_compress_branch_map(c)) { 81 | *bitcnt -= len; 82 | uint32_t sext = 83 | trdb_sign_extendable_bits(packet->branch_map << 1); 84 | if (sext > 31) 85 | sext = 31; 86 | *bitcnt += (31 - sext + 1); 87 | } 88 | } 89 | data.bits <<= align; 90 | memcpy(bin, data.bin, 91 | (*bitcnt + align) / 8 + ((*bitcnt + align) % 8 != 0)); 92 | 93 | return 0; 94 | } 95 | 96 | case F_BRANCH_DIFF: { 97 | if (trdb_is_full_address(c)) { 98 | err(c, "F_BRANCH_DIFF packet encountered but full_address set\n"); 99 | return -trdb_bad_config; 100 | } 101 | uint32_t len = branch_map_len(packet->branches); 102 | 103 | /* we need enough space to do the packing it in uint128 */ 104 | assert(128 > PULPPKTLEN + FORMATLEN + MSGTYPELEN + 5 + 31 + XLEN); 105 | /* TODO: assert branch map to overfull */ 106 | data.bits = byte_len | (packet->msg_type << PULPPKTLEN) | 107 | (packet->format << (PULPPKTLEN + MSGTYPELEN)) | 108 | (packet->branches << (PULPPKTLEN + MSGTYPELEN + FORMATLEN)); 109 | data.bits |= ((__uint128_t)packet->branch_map & MASK_FROM(len)) 110 | << (PULPPKTLEN + MSGTYPELEN + FORMATLEN + BRANCHLEN); 111 | 112 | *bitcnt = PULPPKTLEN + MSGTYPELEN + FORMATLEN + BRANCHLEN + len; 113 | 114 | /* if we have a full branch we don't necessarily need to emit address */ 115 | if (packet->branches > 0) { 116 | data.bits |= 117 | ((__uint128_t)packet->address 118 | << (PULPPKTLEN + MSGTYPELEN + FORMATLEN + BRANCHLEN + len)); 119 | *bitcnt += (XLEN - trdb_sign_extendable_bits(packet->address) + 1); 120 | } else { 121 | /* no address, but compress full branch map*/ 122 | if (trdb_get_compress_branch_map(c)) { 123 | *bitcnt -= len; 124 | uint32_t sext = 125 | trdb_sign_extendable_bits(packet->branch_map << 1); 126 | if (sext > 31) 127 | sext = 31; 128 | *bitcnt += (31 - sext + 1); 129 | } 130 | } 131 | 132 | data.bits <<= align; 133 | memcpy(bin, data.bin, 134 | (*bitcnt + align) / 8 + ((*bitcnt + align) % 8 != 0)); 135 | 136 | return 0; 137 | } 138 | case F_ADDR_ONLY: 139 | assert(128 > PULPPKTLEN + MSGTYPELEN + FORMATLEN + XLEN); 140 | data.bits = byte_len | (packet->msg_type << PULPPKTLEN) | 141 | (packet->format << (PULPPKTLEN + MSGTYPELEN)) | 142 | ((__uint128_t)packet->address 143 | << (PULPPKTLEN + MSGTYPELEN + FORMATLEN)); 144 | 145 | *bitcnt = PULPPKTLEN + MSGTYPELEN + FORMATLEN; 146 | if (trdb_is_full_address(c)) 147 | *bitcnt += XLEN; 148 | else 149 | *bitcnt += (XLEN - sign_extendable_bits(packet->address) + 1); 150 | 151 | data.bits <<= align; 152 | /* this cuts off superfluous bits */ 153 | memcpy(bin, data.bin, 154 | (*bitcnt + align) / 8 + ((*bitcnt + align) % 8 != 0)); 155 | return 0; 156 | 157 | case F_SYNC: 158 | assert(PRIVLEN == 3); 159 | /* check for enough space to the packing */ 160 | assert(128 > PULPPKTLEN + 4 + PRIVLEN + 1 + XLEN + CAUSELEN + 1); 161 | /* TODO: for now we ignore the context field since we have 162 | * only one hart 163 | */ 164 | 165 | /* common part to all sub formats */ 166 | data.bits = 167 | byte_len | (packet->msg_type << PULPPKTLEN) | 168 | (packet->format << (PULPPKTLEN + MSGTYPELEN)) | 169 | (packet->subformat << (PULPPKTLEN + MSGTYPELEN + FORMATLEN)) | 170 | (packet->privilege << (PULPPKTLEN + MSGTYPELEN + 2 * FORMATLEN)); 171 | *bitcnt = PULPPKTLEN + MSGTYPELEN + 2 * FORMATLEN + PRIVLEN; 172 | 173 | /* to reduce repetition */ 174 | uint32_t suboffset = PULPPKTLEN + MSGTYPELEN + 2 * FORMATLEN + PRIVLEN; 175 | switch (packet->subformat) { 176 | case SF_START: 177 | data.bits |= ((__uint128_t)packet->branch << suboffset) | 178 | ((__uint128_t)packet->address << (suboffset + 1)); 179 | *bitcnt += 1 + XLEN; 180 | break; 181 | 182 | case SF_EXCEPTION: 183 | data.bits |= 184 | (packet->branch << suboffset) | 185 | ((__uint128_t)packet->address << (suboffset + 1)) | 186 | ((__uint128_t)packet->ecause << (suboffset + 1 + XLEN)) | 187 | ((__uint128_t)packet->interrupt 188 | << (suboffset + 1 + XLEN + CAUSELEN)); 189 | // going to be zero anyway in our case 190 | // | ((__uint128_t)packet->tval 191 | // << (PULPPKTLEN + 4 + PRIVLEN + 1 + XLEN + CAUSELEN + 1)); 192 | *bitcnt += (1 + XLEN + CAUSELEN + 1); 193 | break; 194 | 195 | case SF_CONTEXT: 196 | /* TODO: we still ignore the context field */ 197 | break; 198 | } 199 | 200 | data.bits <<= align; 201 | memcpy(bin, data.bin, 202 | (*bitcnt + align) / 8 + ((*bitcnt + align) % 8 != 0)); 203 | return 0; 204 | } 205 | return -trdb_bad_packet; 206 | } 207 | 208 | int trdb_pulp_read_single_packet(struct trdb_ctx *c, FILE *fp, 209 | struct tr_packet *packet, uint32_t *bytes) 210 | { 211 | uint8_t header = 0; 212 | union trdb_pack payload = {0}; 213 | if (!c || !fp || !packet) 214 | return -trdb_invalid; 215 | 216 | if (fread(&header, 1, 1, fp) != 1) { 217 | if (feof(fp)) 218 | return -trdb_bad_packet; 219 | else if (ferror(fp)) 220 | return -trdb_file_read; 221 | 222 | return -trdb_internal; /* does not happen */ 223 | } 224 | 225 | /* read packet length it bits (including header) */ 226 | uint8_t len = (header & MASK_FROM(PULPPKTLEN)) * 8 + 8; 227 | payload.bin[0] = header; 228 | /* compute how many bytes that is */ 229 | uint32_t byte_len = len / 8 + (len % 8 != 0 ? 1 : 0); 230 | /* we have to exclude the header byte */ 231 | if (fread((payload.bin + 1), 1, byte_len - 1, fp) != byte_len - 1) { 232 | if (feof(fp)) { 233 | err(c, "incomplete packet read\n"); 234 | return -trdb_bad_packet; 235 | } else if (ferror(fp)) 236 | return -trdb_file_read; 237 | 238 | return -trdb_internal; /* does not happen */ 239 | } 240 | /* since we succefully read a packet we can now set bytes */ 241 | *bytes = byte_len; 242 | /* make sure we start from a good state */ 243 | *packet = (struct tr_packet){0}; 244 | 245 | /* approxmation in multiple of 8*/ 246 | packet->length = 247 | (header & MASK_FROM(PULPPKTLEN)) * 8 + MSGTYPELEN + FORMATLEN; 248 | packet->msg_type = (payload.bits >>= PULPPKTLEN) & MASK_FROM(MSGTYPELEN); 249 | 250 | /* implicit sign extension at packet length' bit */ 251 | payload.bits = sext128(payload.bits, packet->length); 252 | 253 | switch (packet->msg_type) { 254 | /* we are dealing with a regular trace packet */ 255 | case W_TRACE: 256 | packet->format = (payload.bits >>= MSGTYPELEN) & MASK_FROM(FORMATLEN); 257 | payload.bits >>= FORMATLEN; 258 | 259 | uint32_t blen = 0; 260 | uint32_t lower_boundary = 0; 261 | 262 | switch (packet->format) { 263 | case F_BRANCH_FULL: 264 | packet->branches = payload.bits & MASK_FROM(BRANCHLEN); 265 | blen = branch_map_len(packet->branches); 266 | packet->branch_map = (payload.bits >>= BRANCHLEN) & MASK_FROM(blen); 267 | 268 | lower_boundary = MSGTYPELEN + FORMATLEN + BRANCHLEN + blen; 269 | 270 | if (trdb_is_full_address(c)) { 271 | packet->address = (payload.bits >>= blen) & MASK_FROM(XLEN); 272 | } else { 273 | packet->address = (payload.bits >>= blen) & MASK_FROM(XLEN); 274 | packet->address = 275 | sext32(packet->address, packet->length - lower_boundary); 276 | } 277 | return 0; 278 | case F_BRANCH_DIFF: 279 | if (trdb_is_full_address(c)) { 280 | err(c, 281 | "F_BRANCH_DIFF packet encountered but full_address set\n"); 282 | return -trdb_bad_config; 283 | } 284 | packet->branches = payload.bits & MASK_FROM(BRANCHLEN); 285 | blen = branch_map_len(packet->branches); 286 | packet->branch_map = (payload.bits >>= BRANCHLEN) & MASK_FROM(blen); 287 | lower_boundary = MSGTYPELEN + FORMATLEN + BRANCHLEN + blen; 288 | 289 | if (trdb_is_full_address(c)) { 290 | packet->address = (payload.bits >>= blen) & MASK_FROM(XLEN); 291 | } else { 292 | packet->address = (payload.bits >>= blen) & MASK_FROM(XLEN); 293 | packet->address = 294 | sext32(packet->address, packet->length - lower_boundary); 295 | } 296 | 297 | return 0; 298 | case F_ADDR_ONLY: 299 | if (trdb_is_full_address(c)) { 300 | packet->address = payload.bits & MASK_FROM(XLEN); 301 | } else { 302 | packet->address = sext32( 303 | payload.bits, packet->length - MSGTYPELEN - FORMATLEN); 304 | } 305 | return 0; 306 | case F_SYNC: 307 | packet->subformat = payload.bits & MASK_FROM(FORMATLEN); 308 | packet->privilege = 309 | (payload.bits >>= FORMATLEN) & MASK_FROM(PRIVLEN); 310 | if (packet->subformat == SF_CONTEXT) 311 | return -trdb_unimplemented; 312 | 313 | packet->branch = (payload.bits >>= PRIVLEN) & 1; 314 | packet->address = (payload.bits >>= 1) & MASK_FROM(XLEN); 315 | if (packet->subformat == SF_START) 316 | return 0; 317 | packet->ecause = (payload.bits >>= XLEN) & MASK_FROM(CAUSELEN); 318 | packet->interrupt = (payload.bits >>= CAUSELEN) & 1; 319 | if (packet->subformat == SF_EXCEPTION) 320 | return 0; 321 | } 322 | break; 323 | /* this is user defined payload, written through the APB */ 324 | case W_SOFTWARE: 325 | packet->userdata = (payload.bits >> MSGTYPELEN) & MASK_FROM(XLEN); 326 | return 0; 327 | /* timer data */ 328 | case W_TIMER: 329 | /*careful if TIMELEN=64*/ 330 | packet->time = (payload.bits >> MSGTYPELEN) & MASK_FROM(TIMELEN); 331 | return 0; 332 | default: 333 | err(c, "unknown message type in packet\n"); 334 | return -trdb_bad_packet; 335 | break; 336 | } 337 | return -trdb_bad_packet; 338 | } 339 | 340 | int trdb_pulp_read_all_packets(struct trdb_ctx *c, const char *path, 341 | struct trdb_packet_head *packet_list) 342 | { 343 | if (!c || !path || !packet_list) 344 | return -trdb_invalid; 345 | 346 | FILE *fp = fopen(path, "rb"); 347 | if (!fp) 348 | return -trdb_file_open; 349 | 350 | uint32_t total_bytes_read = 0; 351 | struct tr_packet *packet = NULL; 352 | struct tr_packet tmp = {0}; 353 | uint32_t bytes = 0; 354 | 355 | /* read the file and malloc entries into the given linked list head */ 356 | while (trdb_pulp_read_single_packet(c, fp, &tmp, &bytes) == 0) { 357 | packet = malloc(sizeof(*packet)); 358 | if (!packet) 359 | return -trdb_nomem; 360 | *packet = tmp; 361 | total_bytes_read += bytes; 362 | 363 | TAILQ_INSERT_TAIL(packet_list, packet, list); 364 | } 365 | dbg(c, "total bytes read: %" PRIu32 "\n", total_bytes_read); 366 | return 0; 367 | } 368 | 369 | int trdb_pulp_write_single_packet(struct trdb_ctx *c, struct tr_packet *packet, 370 | FILE *fp) 371 | { 372 | int status = 0; 373 | size_t bitcnt = 0; 374 | size_t bytecnt = 0; 375 | uint8_t bin[16] = {0}; 376 | if (!c || !fp || !packet) 377 | return -trdb_invalid; 378 | 379 | status = trdb_pulp_serialize_packet(c, packet, &bitcnt, 0, bin); 380 | if (status < 0) 381 | return status; 382 | 383 | bytecnt = bitcnt / 8 + (bitcnt % 8 != 0); 384 | if (fwrite(bin, 1, bytecnt, fp) != bytecnt) { 385 | if (feof(fp)) { 386 | /* TODO: uhhh */ 387 | } else if (ferror(fp)) 388 | return -trdb_file_write; 389 | } 390 | return 0; 391 | } 392 | 393 | int trdb_write_packets(struct trdb_ctx *c, const char *path, 394 | struct trdb_packet_head *packet_list) 395 | { 396 | int status = 0; 397 | if (!c || !path || !packet_list) 398 | return -trdb_invalid; 399 | 400 | FILE *fp = fopen(path, "wb"); 401 | if (!fp) { 402 | status = -trdb_file_open; 403 | goto fail; 404 | } 405 | 406 | uint8_t bin[sizeof(struct tr_packet)] = {0}; 407 | size_t bitcnt = 0; 408 | uint32_t alignment = 0; 409 | uint8_t carry = 0; 410 | size_t good = 0; 411 | size_t rest = 0; 412 | 413 | struct tr_packet *packet; 414 | /* TODO: do we need the rever version? I think we do*/ 415 | TAILQ_FOREACH_REVERSE (packet, packet_list, trdb_packet_head, list) { 416 | status = trdb_pulp_serialize_packet(c, packet, &bitcnt, alignment, bin); 417 | if (status < 0) { 418 | goto fail; 419 | } 420 | /* stitch two consecutive packets together */ 421 | bin[0] |= carry; 422 | rest = (bitcnt + alignment) % 8; 423 | good = (bitcnt + alignment) / 8; 424 | 425 | /* write as many bytes as we can i.e. withouth the potentially 426 | * intersecting ones 427 | */ 428 | if (fwrite(bin, 1, good, fp) != good) { 429 | status = -trdb_file_write; 430 | goto fail; 431 | } 432 | /* we keep that for the next packet */ 433 | carry = bin[good] & MASK_FROM(rest); 434 | alignment = rest; 435 | } 436 | /* done, write remaining carry */ 437 | if (!fwrite(&bin[good], 1, 1, fp)) { 438 | status = -trdb_file_write; 439 | } 440 | fail: 441 | if (fp) 442 | fclose(fp); 443 | return status; 444 | } 445 | 446 | int trdb_stimuli_to_trace_list(struct trdb_ctx *c, const char *path, 447 | struct trdb_instr_head *instrs, size_t *count) 448 | { 449 | int status = 0; 450 | FILE *fp = NULL; 451 | 452 | *count = 0; 453 | 454 | if (!c || !path || !instrs) { 455 | status = -trdb_invalid; 456 | goto fail; 457 | } 458 | struct tr_instr *sample = NULL; 459 | 460 | fp = fopen(path, "r"); 461 | if (!fp) { 462 | status = -trdb_file_open; 463 | goto fail; 464 | } 465 | size_t scnt = 0; 466 | 467 | int ret = 0; 468 | int valid = 0; 469 | int exception = 0; 470 | int interrupt = 0; 471 | uint32_t cause = 0; 472 | addr_t tval = 0; 473 | uint32_t priv = 0; 474 | addr_t iaddr = 0; 475 | insn_t instr = 0; 476 | int compressed = 0; 477 | 478 | while ((ret = fscanf(fp, 479 | "valid= %d exception= %d interrupt= %d cause= %" SCNx32 480 | " tval= %" SCNxADDR " priv= %" SCNx32 481 | " compressed= %d addr= %" SCNxADDR " instr= %" SCNxINSN 482 | " \n", 483 | &valid, &exception, &interrupt, &cause, &tval, &priv, 484 | &compressed, &iaddr, &instr)) != EOF) { 485 | // TODO: make this configurable so that we don't have to store so 486 | // much data 487 | /* if (!valid) { */ 488 | /* continue; */ 489 | /* } */ 490 | sample = malloc(sizeof(*sample)); 491 | if (!sample) { 492 | status = -trdb_nomem; 493 | goto fail; 494 | } 495 | 496 | *sample = (struct tr_instr){0}; 497 | sample->valid = valid; 498 | sample->exception = exception; 499 | sample->interrupt = interrupt; 500 | sample->cause = cause; 501 | sample->tval = tval; 502 | sample->priv = priv; 503 | sample->iaddr = iaddr; 504 | sample->instr = instr; 505 | sample->compressed = compressed; 506 | 507 | TAILQ_INSERT_TAIL(instrs, sample, list); 508 | 509 | scnt++; 510 | } 511 | 512 | if (ferror(fp)) { 513 | status = -trdb_scan_file; 514 | goto fail; 515 | } 516 | 517 | *count = scnt; 518 | out: 519 | if (fp) 520 | fclose(fp); 521 | 522 | return status; 523 | fail: 524 | // TODO: it's maybe better to not free the whole list, but just the part 525 | // where failed 526 | trdb_free_instr_list(instrs); 527 | goto out; 528 | } 529 | 530 | int trdb_stimuli_to_trace(struct trdb_ctx *c, const char *path, 531 | struct tr_instr **samples, size_t *count) 532 | { 533 | int status = 0; 534 | FILE *fp = NULL; 535 | 536 | *count = 0; 537 | 538 | if (!c || !path || !samples) 539 | return -trdb_invalid; 540 | 541 | fp = fopen(path, "r"); 542 | if (!fp) { 543 | status = -trdb_file_open; 544 | goto fail; 545 | } 546 | size_t scnt = 0; 547 | 548 | int ret = 0; 549 | int valid = 0; 550 | int exception = 0; 551 | int interrupt = 0; 552 | uint32_t cause = 0; 553 | addr_t tval = 0; 554 | uint32_t priv = 0; 555 | addr_t iaddr = 0; 556 | insn_t instr = 0; 557 | int compressed = 0; 558 | 559 | size_t size = 128; 560 | *samples = malloc(size * sizeof(**samples)); 561 | if (!*samples) { 562 | status = -trdb_nomem; 563 | goto fail; 564 | } 565 | 566 | while ((ret = fscanf(fp, 567 | "valid= %d exception= %d interrupt= %d cause= %" SCNx32 568 | " tval= %" SCNxADDR " priv= %" SCNx32 569 | " compressed= %d addr= %" SCNxADDR " instr= %" SCNxINSN 570 | " \n", 571 | &valid, &exception, &interrupt, &cause, &tval, &priv, 572 | &compressed, &iaddr, &instr)) != EOF) { 573 | // TODO: make this configurable so that we don't have to store so much 574 | // data 575 | /* if (!valid) { */ 576 | /* continue; */ 577 | /* } */ 578 | if (scnt >= size) { 579 | size = 2 * size; 580 | struct tr_instr *tmp = realloc(*samples, size * sizeof(**samples)); 581 | if (!tmp) { 582 | status = -trdb_nomem; 583 | goto fail; 584 | } 585 | *samples = tmp; 586 | } 587 | (*samples)[scnt] = (struct tr_instr){0}; 588 | (*samples)[scnt].valid = valid; 589 | (*samples)[scnt].exception = exception; 590 | (*samples)[scnt].interrupt = interrupt; 591 | (*samples)[scnt].cause = cause; 592 | (*samples)[scnt].tval = tval; 593 | (*samples)[scnt].priv = priv; 594 | (*samples)[scnt].iaddr = iaddr; 595 | (*samples)[scnt].instr = instr; 596 | (*samples)[scnt].compressed = compressed; 597 | scnt++; 598 | } 599 | /* initialize the remaining unitialized memory */ 600 | memset((*samples) + scnt, 0, size - scnt); 601 | 602 | if (ferror(fp)) { 603 | status = -trdb_scan_file; 604 | goto fail; 605 | } 606 | 607 | *count = scnt; 608 | out: 609 | if (fp) 610 | fclose(fp); 611 | return status; 612 | fail: 613 | free(*samples); 614 | samples = NULL; 615 | goto out; 616 | } 617 | 618 | int trdb_cvs_to_trace_list(struct trdb_ctx *c, const char *path, 619 | struct trdb_instr_head *instrs, size_t *count) 620 | { 621 | 622 | FILE *fp = NULL; 623 | int status = 0; 624 | struct tr_instr *sample = NULL; 625 | 626 | *count = 0; 627 | 628 | if (!c || !path || !instrs) 629 | status = -trdb_invalid; 630 | 631 | fp = fopen(path, "r"); 632 | if (!fp) { 633 | status = -trdb_file_open; 634 | goto fail; 635 | } 636 | size_t scnt = 0; 637 | 638 | int valid = 0; 639 | int exception = 0; 640 | int interrupt = 0; 641 | uint32_t cause = 0; 642 | addr_t tval = 0; 643 | uint32_t priv = 0; 644 | addr_t iaddr = 0; 645 | insn_t instr = 0; 646 | 647 | char *line = NULL; 648 | size_t len = 0; 649 | 650 | /* reading header line */ 651 | if (getline(&line, &len, fp) == -1) { 652 | status = -trdb_bad_cvs_header; 653 | goto fail; 654 | } 655 | 656 | if (!strcmp(line, "VALID,ADDRESS,INSN,PRIVILEGE," 657 | "EXCEPTION,ECAUSE,TVAL,INTERRUPT")) { 658 | status = -trdb_bad_cvs_header; 659 | goto fail; 660 | } 661 | 662 | /* parse data into tr_instr list */ 663 | while (getline(&line, &len, fp) != -1) { 664 | 665 | sample = malloc(sizeof(*sample)); 666 | if (!sample) { 667 | status = -trdb_nomem; 668 | goto fail; 669 | } 670 | *sample = (struct tr_instr){0}; 671 | 672 | int ele = 7; 673 | const char *tok; 674 | for (tok = strtok(line, ","); tok && *tok; tok = strtok(NULL, ",\n")) { 675 | 676 | if (ele < 0) { 677 | err(c, "reading too many tokens per line\n"); 678 | status = -trdb_scan_state_invalid; 679 | goto fail; 680 | } 681 | 682 | switch (ele) { 683 | case 7: 684 | sscanf(tok, "%d", &valid); 685 | sample->valid = valid; 686 | break; 687 | case 6: 688 | sscanf(tok, "%" SCNxADDR "", &iaddr); 689 | sample->iaddr = iaddr; 690 | break; 691 | case 5: 692 | sscanf(tok, "%" SCNxINSN "", &instr); 693 | sample->instr = instr; 694 | sample->compressed = ((instr & 3) != 3); 695 | break; 696 | case 4: 697 | sscanf(tok, "%" SCNx32 "", &priv); 698 | sample->priv = priv; 699 | break; 700 | case 3: 701 | sscanf(tok, "%d", &exception); 702 | sample->exception = exception; 703 | break; 704 | case 2: 705 | sscanf(tok, "%" SCNx32 "", &cause); 706 | sample->cause = cause; 707 | break; 708 | case 1: 709 | sscanf(tok, "%" SCNxADDR "", &tval); 710 | sample->tval = tval; 711 | break; 712 | case 0: 713 | sscanf(tok, "%d", &interrupt); 714 | sample->interrupt = interrupt; 715 | break; 716 | } 717 | ele--; 718 | } 719 | 720 | if (ele > 0) { 721 | err(c, "wrong number of tokens on line, still %d remaining\n", ele); 722 | status = -trdb_scan_state_invalid; 723 | goto fail; 724 | } 725 | 726 | TAILQ_INSERT_TAIL(instrs, sample, list); 727 | 728 | scnt++; 729 | } 730 | 731 | free(line); 732 | 733 | if (ferror(fp)) { 734 | status = -trdb_scan_file; 735 | goto fail; 736 | } 737 | 738 | *count = scnt; 739 | 740 | out: 741 | if (fp) 742 | fclose(fp); 743 | return status; 744 | fail: 745 | // TODO: it's maybe better to not free the whole list, but just the part 746 | // where failed 747 | free(sample); 748 | trdb_free_instr_list(instrs); 749 | goto out; 750 | } 751 | -------------------------------------------------------------------------------- /src/trdb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Trace Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /* 21 | * Author: Robert Balas (balasr@student.ethz.ch) 22 | * Description: trdb -- Trace Debugger Tools for RISC-V E-Trace, compress 23 | * and decompress instruction traces 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "config.h" 34 | #include "bfd.h" 35 | #include "utils.h" 36 | #include "trace_debugger.h" 37 | #include "disassembly.h" 38 | #include "serialize.h" 39 | 40 | #define TRDB_NUM_ARGS 1 41 | 42 | const char *argp_program_version = PACKAGE_STRING; 43 | const char *argp_program_bug_address = PACKAGE_BUGREPORT; 44 | 45 | static char doc[] = "trdb -- Trace Debugger Tools for RISC-V E-Trace"; 46 | static char args_doc[] = "TRACE-OR-PACKETS"; 47 | 48 | #define TRDB_OPT_DEMANGLE 1 49 | #define TRDB_OPT_NO_ALIASES 2 50 | #define TRDB_OPT_PREFIX_ADDRESSES 3 51 | #define TRDB_OPT_INLINES 4 52 | #define TRDB_OPT_FULL_ADDR 5 53 | #define TRDB_OPT_CVS 6 54 | 55 | static struct argp_option options[] = { 56 | {"verbose", 'v', 0, 0, "Produce verbose output"}, 57 | {"quiet", 'q', 0, 0, "Don't produce any output"}, 58 | /* {"silent", 's', 0, OPTION_ALIAS}, */ 59 | {"bfd", 'b', "ELF", 0, 60 | "ELF binary with which the traces/packets were produced"}, 61 | {"compress", 'c', 0, 0, "Take a stimuli file and compress to packets"}, 62 | {"cvs", TRDB_OPT_CVS, 0, 0, "Set TRACE-OR-PACKETS file type to cvs"}, 63 | {"binary-format", 'p', "FORMAT", 0, 64 | "Specify binary input/output format, by default like the pulp trace debugger"}, 65 | {"trace-file", 't', 0, 0, "Input file is a trace file"}, 66 | {"dump", 'h', 0, 0, "Dump trace or packets in human readable format"}, 67 | {"extract", 'x', 0, 0, "Take a packet file and produce a stimuli file"}, 68 | {"full-address", TRDB_OPT_FULL_ADDR, 0, 0, 69 | "Assume no sign-extension compression on addresses"}, 70 | {"disassemble", 'd', 0, 0, "disassemble the output"}, 71 | {"no-aliases", TRDB_OPT_NO_ALIASES, 0, 0, 72 | "Do not use aliases for disassembled instructions"}, 73 | {"demangle", TRDB_OPT_DEMANGLE, 0, 0, 74 | "Demangle symbols, sometimes helps readability"}, 75 | {"prefix-addresses", TRDB_OPT_PREFIX_ADDRESSES, 0, 0, 76 | "Print complete address alongside disassembly"}, 77 | {"file-offsets", 'F', 0, 0, 78 | "Include file offsets when displaying information"}, 79 | {"source", 'S', 0, 0, "Intermix source code with disassembly"}, 80 | {"function-context", 's', 0, 0, 81 | "Show when address coincides with function name entrypoint"}, 82 | {"line-numbers", 'l', 0, 0, "Include line numbers and filenames in output"}, 83 | {"inlines", TRDB_OPT_INLINES, 0, 0, 84 | "Print all inlines for source line (with -l)"}, 85 | {"output", 'o', "FILE", 0, "Write to FILE instead of stdout"}, 86 | {0}}; 87 | 88 | struct arguments { 89 | char *args[TRDB_NUM_ARGS]; 90 | bool silent, verbose, compress, has_elf, disassemble, decompress, 91 | trace_file, binary_output, human, full_address, cvs; 92 | uint32_t settings_disasm; 93 | char *binary_format; 94 | char *output_file; 95 | char *elf_file; 96 | }; 97 | 98 | static error_t parse_opt(int key, char *arg, struct argp_state *state) 99 | { 100 | struct arguments *arguments = state->input; 101 | switch (key) { 102 | case 'q': 103 | arguments->silent = true; 104 | break; 105 | case 'o': 106 | arguments->output_file = arg; 107 | break; 108 | case 'b': 109 | arguments->has_elf = true; 110 | arguments->elf_file = arg; 111 | break; 112 | case 'c': 113 | arguments->compress = true; 114 | break; 115 | case TRDB_OPT_CVS: 116 | arguments->cvs = true; 117 | break; 118 | case 'p': 119 | arguments->binary_output = true; 120 | arguments->binary_format = arg; 121 | break; 122 | case 't': 123 | arguments->trace_file = true; 124 | break; 125 | case 'h': 126 | arguments->human = true; 127 | break; 128 | case 'd': 129 | arguments->disassemble = true; 130 | break; 131 | case 'x': 132 | arguments->decompress = true; 133 | break; 134 | case TRDB_OPT_FULL_ADDR: 135 | arguments->full_address = true; 136 | break; 137 | case TRDB_OPT_NO_ALIASES: 138 | arguments->settings_disasm |= TRDB_NO_ALIASES; 139 | break; 140 | case TRDB_OPT_DEMANGLE: 141 | arguments->settings_disasm |= TRDB_DO_DEMANGLE; 142 | break; 143 | case TRDB_OPT_PREFIX_ADDRESSES: 144 | arguments->settings_disasm |= TRDB_PREFIX_ADDRESSES; 145 | break; 146 | case 'F': 147 | arguments->settings_disasm |= TRDB_DISPLAY_FILE_OFFSETS; 148 | break; 149 | case 'S': 150 | arguments->settings_disasm |= TRDB_SOURCE_CODE; 151 | break; 152 | case 's': 153 | arguments->settings_disasm |= TRDB_FUNCTION_CONTEXT; 154 | break; 155 | case 'l': 156 | arguments->settings_disasm |= TRDB_LINE_NUMBERS; 157 | break; 158 | case TRDB_OPT_INLINES: 159 | arguments->settings_disasm |= TRDB_INLINES; 160 | break; 161 | case ARGP_KEY_ARG: 162 | if (state->arg_num >= TRDB_NUM_ARGS) 163 | argp_usage(state); 164 | arguments->args[state->arg_num] = arg; 165 | break; 166 | case ARGP_KEY_END: 167 | if (state->arg_num < TRDB_NUM_ARGS) 168 | argp_usage(state); 169 | break; 170 | default: 171 | return ARGP_ERR_UNKNOWN; 172 | } 173 | return 0; 174 | } 175 | 176 | static struct argp argp = {options, parse_opt, args_doc, doc}; 177 | 178 | static int compress_trace(struct trdb_ctx *c, FILE *output_fp, 179 | struct arguments *); 180 | 181 | static int decompress_packets(struct trdb_ctx *c, FILE *output_fp, bfd *abfd, 182 | struct arguments *arguments); 183 | 184 | static int disassemble_trace(struct trdb_ctx *c, FILE *output_fp, bfd *abfd, 185 | struct arguments *); 186 | 187 | static int dump_packets(struct trdb_ctx *c, FILE *output_fp, 188 | struct arguments *arguments); 189 | 190 | static int dump_trace(struct trdb_ctx *c, FILE *output_fp, 191 | struct arguments *arguments); 192 | 193 | int main(int argc, char *argv[argc + 1]) 194 | { 195 | int status = EXIT_SUCCESS; 196 | struct arguments arguments; 197 | /* set default */ 198 | arguments = (struct arguments){0}; 199 | arguments.silent = false; 200 | arguments.verbose = false; 201 | arguments.compress = false; 202 | arguments.cvs = false; 203 | arguments.binary_output = false; 204 | arguments.human = false; 205 | arguments.full_address = false; 206 | arguments.has_elf = false; 207 | arguments.disassemble = false; 208 | arguments.decompress = false; 209 | arguments.settings_disasm = 0; 210 | arguments.output_file = "-"; 211 | arguments.binary_format = ""; 212 | 213 | argp_parse(&argp, argc, argv, 0, 0, &arguments); 214 | 215 | FILE *output_fp = NULL; 216 | bfd *abfd = NULL; 217 | 218 | struct trdb_ctx *ctx = trdb_new(); 219 | 220 | if (!ctx) { 221 | fprintf(stderr, "creating trdb library context failed\n"); 222 | status = EXIT_FAILURE; 223 | goto fail; 224 | } 225 | 226 | /* general settings */ 227 | trdb_set_full_address(ctx, arguments.full_address); 228 | trdb_set_compress_branch_map(ctx, false); 229 | /* trdb_set_implicit_ret(ctx, true); */ 230 | /* trdb_set_pulp_extra_packet(ctx, true); */ 231 | 232 | /* prepare output */ 233 | if (arguments.output_file[0] != '-') { 234 | output_fp = fopen(arguments.output_file, "w"); 235 | if (!output_fp) { 236 | fprintf(stderr, "fopen: %s", strerror(errno)); 237 | status = EXIT_FAILURE; 238 | goto fail; 239 | } 240 | } else { 241 | output_fp = stdout; 242 | } 243 | 244 | /* commands that can use a bfd */ 245 | bfd_init(); 246 | if (arguments.has_elf) { 247 | abfd = bfd_openr(arguments.elf_file, NULL); 248 | if (!(abfd && bfd_check_format(abfd, bfd_object))) { 249 | bfd_perror("bfd_openr or bfd_check_format"); 250 | status = EXIT_FAILURE; 251 | goto fail; 252 | } 253 | } 254 | 255 | if (arguments.decompress) { 256 | status = decompress_packets(ctx, output_fp, abfd, &arguments); 257 | } else if (arguments.compress) { 258 | status = compress_trace(ctx, output_fp, &arguments); 259 | } else if (arguments.human) { 260 | if (arguments.trace_file) 261 | status = dump_trace(ctx, output_fp, &arguments); 262 | else 263 | status = dump_packets(ctx, output_fp, &arguments); 264 | } else if (arguments.trace_file) { 265 | status = disassemble_trace(ctx, output_fp, abfd, &arguments); 266 | } else { 267 | /* by default we decompress */ 268 | status = decompress_packets(ctx, output_fp, abfd, &arguments); 269 | } 270 | 271 | fail: 272 | trdb_free(ctx); 273 | if (output_fp) 274 | fclose(output_fp); 275 | if (abfd) 276 | bfd_close(abfd); 277 | return status; 278 | } 279 | 280 | static int compress_trace(struct trdb_ctx *c, FILE *output_fp, 281 | struct arguments *arguments) 282 | { 283 | int status = EXIT_SUCCESS; 284 | 285 | struct trdb_packet_head packet_list; 286 | TAILQ_INIT(&packet_list); 287 | /* read stimuli file and convert to internal data structure */ 288 | struct tr_instr *tmp = NULL; 289 | struct tr_instr **samples = &tmp; 290 | struct trdb_instr_head instr_list; 291 | TAILQ_INIT(&instr_list); 292 | 293 | int success = 0; 294 | size_t samplecnt = 0; 295 | if (arguments->cvs) { 296 | success = trdb_cvs_to_trace_list(c, arguments->args[0], &instr_list, 297 | &samplecnt); 298 | } else { 299 | success = 300 | trdb_stimuli_to_trace(c, arguments->args[0], samples, &samplecnt); 301 | } 302 | 303 | if (success < 0) { 304 | fprintf(stderr, "reading stimuli file failed: %s\n", 305 | trdb_errstr(trdb_errcode(success))); 306 | status = EXIT_FAILURE; 307 | goto fail; 308 | } 309 | 310 | struct tr_instr *instr; 311 | 312 | /* step by step compression */ 313 | if (arguments->cvs) { 314 | TAILQ_FOREACH (instr, &instr_list, list) { 315 | int step = trdb_compress_trace_step_add(c, &packet_list, instr); 316 | if (step < 0) { 317 | fprintf(stderr, "compress trace failed: %s\n", 318 | trdb_errstr(trdb_errcode(status))); 319 | status = EXIT_FAILURE; 320 | goto fail; 321 | } 322 | } 323 | } else { 324 | for (size_t i = 0; i < samplecnt; i++) { 325 | int step = 326 | trdb_compress_trace_step_add(c, &packet_list, &(*samples)[i]); 327 | if (step < 0) { 328 | fprintf(stderr, "compress trace failed: %s\n", 329 | trdb_errstr(trdb_errcode(status))); 330 | status = EXIT_FAILURE; 331 | goto fail; 332 | } 333 | } 334 | } 335 | /* we either produce binary or human readable output */ 336 | if (arguments->binary_output) { 337 | struct tr_packet *packet; 338 | TAILQ_FOREACH (packet, &packet_list, list) { 339 | status = trdb_pulp_write_single_packet(c, packet, output_fp); 340 | if (status < 0) { 341 | fprintf(stderr, "failed to serialize packets: %s\n", 342 | trdb_errstr(trdb_errcode(status))); 343 | status = EXIT_FAILURE; 344 | goto fail; 345 | } 346 | } 347 | } else { 348 | /* write to file or stdout */ 349 | trdb_dump_packet_list(output_fp, &packet_list); 350 | } 351 | fail: 352 | free(*samples); 353 | trdb_free_packet_list(&packet_list); 354 | trdb_free_instr_list(&instr_list); 355 | 356 | return status; 357 | } 358 | 359 | static int decompress_packets(struct trdb_ctx *c, FILE *output_fp, bfd *abfd, 360 | struct arguments *arguments) 361 | { 362 | int status = EXIT_SUCCESS; 363 | const char *path = arguments->args[0]; 364 | struct disassemble_info dinfo; 365 | struct disassembler_unit dunit; 366 | struct trdb_packet_head packet_list; 367 | TAILQ_INIT(&packet_list); 368 | struct trdb_instr_head instr_list; 369 | TAILQ_INIT(&instr_list); 370 | 371 | if (!abfd) { 372 | fprintf(stderr, "need to provide a binary (--bfd) for decompression\n"); 373 | status = EXIT_FAILURE; 374 | goto fail; 375 | } 376 | 377 | /* by default we parse assuming data is generated from PULP */ 378 | if ((status = trdb_pulp_read_all_packets(c, path, &packet_list)) < 0) { 379 | fprintf(stderr, "failed to parse packets: %s\n", 380 | trdb_errstr(trdb_errcode(status))); 381 | status = EXIT_FAILURE; 382 | goto fail; 383 | } 384 | 385 | /* reconstruct the original instruction sequence */ 386 | status = trdb_decompress_trace(c, abfd, &packet_list, &instr_list); 387 | if (status < 0) { 388 | fprintf(stderr, "decompressing trace failed: %s\n", 389 | trdb_errstr(trdb_errcode(status))); 390 | fprintf(stderr, "possible causes: corrupt packets or wrong bfd\n"); 391 | fprintf(stderr, "continuing anyway...\n"); 392 | } 393 | 394 | /* setup the disassembler to consider data from the bfd */ 395 | dinfo = (struct disassemble_info){0}; 396 | dunit = (struct disassembler_unit){0}; 397 | 398 | dunit.dinfo = &dinfo; 399 | status = trdb_alloc_dinfo_with_bfd(c, abfd, &dunit); 400 | if (status < 0) { 401 | fprintf(stderr, "failed to configure bfd: %s\n", 402 | trdb_errstr(trdb_errcode(status))); 403 | status = EXIT_FAILURE; 404 | goto fail; 405 | } 406 | 407 | /* configure disassemble output */ 408 | trdb_set_disassembly_conf(&dunit, arguments->settings_disasm); 409 | dinfo.fprintf_func = (fprintf_ftype)fprintf; 410 | dinfo.stream = output_fp; 411 | 412 | struct tr_instr *instr; 413 | TAILQ_FOREACH (instr, &instr_list, list) { 414 | if (arguments->disassemble) 415 | trdb_disassemble_instr_with_bfd(c, instr, abfd, &dunit); 416 | /* trdb_disassemble_instr(instr, &dunit); */ 417 | else 418 | trdb_print_instr(output_fp, instr); 419 | } 420 | 421 | fail: 422 | /* trdb_free_dinfo_with_bfd(c, abfd, &dunit); */ 423 | trdb_free_packet_list(&packet_list); 424 | trdb_free_instr_list(&instr_list); 425 | return status; 426 | } 427 | 428 | static int disassemble_trace(struct trdb_ctx *c, FILE *output_fp, bfd *abfd, 429 | struct arguments *arguments) 430 | { 431 | int status = EXIT_SUCCESS; 432 | 433 | struct disassemble_info dinfo; 434 | struct disassembler_unit dunit; 435 | 436 | /* read stimuli file and convert to internal data structure */ 437 | struct tr_instr *tmp; 438 | struct tr_instr **samples = &tmp; 439 | size_t samplecnt = 0; 440 | int success = 0; 441 | 442 | success = trdb_stimuli_to_trace(c, arguments->args[0], samples, &samplecnt); 443 | if (success < 0) { 444 | fprintf(stderr, "reading stimuli file failed: %s\n", 445 | trdb_errstr(trdb_errcode(status))); 446 | status = EXIT_FAILURE; 447 | goto fail; 448 | } 449 | 450 | dinfo = (struct disassemble_info){0}; 451 | dunit = (struct disassembler_unit){0}; 452 | dunit.dinfo = &dinfo; 453 | 454 | /* setup the disassembler to consider data from the bfd */ 455 | if (abfd) { 456 | if (trdb_alloc_dinfo_with_bfd(c, abfd, &dunit)) { 457 | fprintf(stderr, "failed to configure bfd: %s\n", 458 | trdb_errstr(trdb_errcode(status))); 459 | status = EXIT_FAILURE; 460 | goto fail; 461 | } 462 | /* configure disassemble output */ 463 | dinfo.fprintf_func = (fprintf_ftype)fprintf; 464 | dinfo.stream = output_fp; 465 | 466 | } else { 467 | /* if we can't use a bfd assume its pulp riscv */ 468 | init_disassemble_info(&dinfo, stdout, (fprintf_ftype)fprintf); 469 | trdb_init_disassemble_info_for_pulp(&dinfo); 470 | dunit.disassemble_fn = 471 | disassembler(bfd_arch_riscv, false, bfd_mach_riscv32, NULL); 472 | } 473 | 474 | trdb_disassemble_trace(samplecnt, *samples, &dunit); 475 | 476 | fail: 477 | if (abfd) 478 | trdb_free_dinfo_with_bfd(c, abfd, &dunit); 479 | free(*samples); 480 | return status; 481 | } 482 | 483 | static int dump_packets(struct trdb_ctx *c, FILE *output_fp, 484 | struct arguments *arguments) 485 | { 486 | int status = EXIT_SUCCESS; 487 | 488 | if (!strcmp(arguments->binary_format, "")) { 489 | fprintf( 490 | stderr, 491 | "provide a --binary-format (-p) argument to interpret this file\n"); 492 | return EXIT_FAILURE; 493 | } 494 | 495 | const char *path = arguments->args[0]; 496 | struct trdb_packet_head packet_list; 497 | TAILQ_INIT(&packet_list); 498 | 499 | if (!strcmp(arguments->binary_format, "pulp")) { 500 | if ((status = trdb_pulp_read_all_packets(c, path, &packet_list)) < 0) { 501 | fprintf(stderr, "failed to parse packets: %s\n", 502 | trdb_errstr(trdb_errcode(status))); 503 | status = EXIT_FAILURE; 504 | goto fail; 505 | } 506 | trdb_dump_packet_list(output_fp, &packet_list); 507 | 508 | } else { 509 | fprintf(stderr, "binary format %s not supported/recognized\n", 510 | arguments->binary_format); 511 | status = EXIT_FAILURE; 512 | goto fail; 513 | } 514 | 515 | fail: 516 | trdb_free_packet_list(&packet_list); 517 | return status; 518 | } 519 | 520 | static int dump_trace(struct trdb_ctx *c, FILE *output_fp, 521 | struct arguments *arguments) 522 | { 523 | int status = EXIT_SUCCESS; 524 | 525 | const char *path = arguments->args[0]; 526 | struct trdb_instr_head instr_list; 527 | size_t instrcnt = 0; 528 | TAILQ_INIT(&instr_list); 529 | 530 | if (arguments->cvs) { 531 | status = trdb_cvs_to_trace_list(c, path, &instr_list, &instrcnt); 532 | if (status < 0) { 533 | fprintf(stderr, "failed to parse cvs file: %s\n", 534 | trdb_errstr(trdb_errcode(status))); 535 | status = EXIT_FAILURE; 536 | goto fail; 537 | } 538 | trdb_dump_instr_list(output_fp, &instr_list); 539 | } else { 540 | fprintf(stderr, "dump trace for non cvs file not implemented yet"); 541 | status = EXIT_FAILURE; 542 | goto fail; 543 | } 544 | 545 | fail: 546 | trdb_free_instr_list(&instr_list); 547 | return status; 548 | } 549 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * trdb - Trace Debugger Software for the PULP platform 3 | * 4 | * Copyright (C) 2024 Robert Balas 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /* 21 | * Author: Robert Balas (balasr@student.ethz.ch) 22 | * Description: Small utility functions 23 | */ 24 | #include 25 | #include 26 | #include 27 | #include "utils.h" 28 | 29 | void trdb_log_null(struct trdb_ctx *ctx, const char *format, ...) 30 | { 31 | (void)ctx; 32 | (void)format; 33 | } 34 | 35 | uint32_t sign_extendable_bits(uint32_t addr) 36 | { 37 | if (addr == 0 || addr == UINT32_MAX) 38 | return 32; 39 | int clz = __builtin_clz(addr); 40 | int clo = __builtin_clz(~addr); 41 | return clz > clo ? clz : clo; 42 | } 43 | 44 | uint32_t sign_extendable_bits64(uint64_t addr) 45 | { 46 | if (addr == 0 || addr == UINT64_MAX) 47 | return 64; 48 | int clz = __builtin_clzll(addr); 49 | int clo = __builtin_clzll(~addr); 50 | return clz > clo ? clz : clo; 51 | } 52 | 53 | uint32_t sign_extendable_bits128(__uint128_t addr) 54 | { 55 | int clz = clz_u128(addr); 56 | int clo = clz_u128(~addr); 57 | return clz > clo ? clz : clo; 58 | } 59 | 60 | /* Sign extend a value @p val with @p bits to 32 bits. */ 61 | uint32_t sext32(uint32_t val, uint32_t bit) 62 | { 63 | if (bit == 0) 64 | return 0; 65 | 66 | if (bit == 32) 67 | return val; 68 | 69 | int m = 1U << (bit - 1); 70 | 71 | val = val & ((1U << bit) - 1); 72 | return (val ^ m) - m; 73 | } 74 | 75 | __uint128_t sext128(__uint128_t val, __uint128_t bit) 76 | { 77 | if (bit == 0) 78 | return 0; 79 | 80 | if (bit == 128) 81 | return val; 82 | 83 | __uint128_t m = (__uint128_t)1 << (bit - 1); 84 | 85 | val = val & (((__uint128_t)1 << bit) - 1); 86 | return (val ^ m) - m; 87 | } 88 | 89 | int clz_u128(__uint128_t u) 90 | { 91 | uint64_t hi = u >> 64; 92 | uint64_t lo = u; 93 | int retval[3] = {__builtin_clzll(hi), __builtin_clzll(lo) + 64, 128}; 94 | int idx = !hi + ((!lo) & (!hi)); 95 | return retval[idx]; 96 | } 97 | -------------------------------------------------------------------------------- /test/test_trdb.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | ../trdb --full-address --binary-format pulp --compress -o my_packets ../data/trdb_stimuli 4 | ../trdb --full-address --binary-format pulp --dump my_packets 5 | ../trdb --full-address --binary-format pulp --bfd ../data/interrupt --extract my_packets 6 | ../trdb --full-address -dSsl --binary-format pulp --bfd ../data/interrupt --extract my_packets 7 | rm my_packets 8 | -------------------------------------------------------------------------------- /test/test_trdb_diff.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | ../trdb --binary-format pulp --compress -o my_packets ../data/trdb_stimuli 4 | ../trdb --binary-format pulp --dump my_packets 5 | ../trdb --binary-format pulp --bfd ../data/interrupt --extract my_packets 6 | ../trdb -dSsl --binary-format pulp --bfd ../data/interrupt --extract my_packets 7 | rm my_packets 8 | -------------------------------------------------------------------------------- /test/workaround.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is just a temporary workaround to run the test binary. We are 3 | * missing some struct definition, which makes pointer dereferencing invalid. 4 | * Ideally we would use accesor functions in test.c or use an internal header to 5 | * solve this issue. 6 | */ 7 | 8 | /* Configuration state of the trace debugger, used to guide the compression and 9 | * decompression routine 10 | */ 11 | struct trdb_config { 12 | /* addressing mode */ 13 | bool arch64; 14 | 15 | /* TODO: Unused, inspect full-address, iaddress-lsb-p, implicit-except, 16 | * set-trace 17 | */ 18 | uint64_t resync_max; 19 | /* bool iaddress_lsb_p; */ 20 | /* bool implicit_except; */ 21 | /* bool set_trace; */ 22 | 23 | /* collect as much information as possible, but slower execution */ 24 | bool full_statistics; 25 | 26 | /* set to true to always use absolute addresses in packets, this is used for 27 | * decompression and compression 28 | */ 29 | bool full_address; 30 | /* do the sign extension of addresses like PULP. Only relevant if 31 | * full_address = false 32 | */ 33 | bool use_pulp_sext; 34 | /* Don't regard ret's as unpredictable discontinuity */ 35 | bool implicit_ret; 36 | /* Use additional packets to jump over vector table */ 37 | bool pulp_vector_table_packet; 38 | /* whether we compress full branch maps */ 39 | bool compress_full_branch_map; 40 | 41 | /* TODO: move this */ 42 | /* set to true to always diassemble to most general representation */ 43 | bool no_aliases; 44 | }; 45 | 46 | struct trdb_stats { 47 | size_t payloadbits; 48 | size_t packetbits; 49 | size_t pulpbits; 50 | size_t instrbits; 51 | size_t instrs; 52 | size_t packets; 53 | size_t zo_addresses; /**< all zero or all ones addresses */ 54 | size_t zo_branchmaps; /**< all zero or all ones branchmaps */ 55 | size_t addr_only_packets; 56 | size_t exception_packets; 57 | size_t start_packets; 58 | size_t diff_packets; 59 | size_t abs_packets; 60 | size_t bmap_full_packets; 61 | size_t bmap_full_addr_packets; 62 | uint32_t sext_bits[64]; 63 | }; 64 | 65 | struct trdb_ctx { 66 | /* specific settings for compression/decompression */ 67 | struct trdb_config config; 68 | /* state used for compression */ 69 | struct trdb_compress *cmp; 70 | /* state used for decompression */ 71 | struct trdb_decompress *dec; 72 | /* state used for disassembling */ 73 | struct tr_instr *dis_instr; 74 | struct disassembler_unit *dunit; 75 | /* compression statistics */ 76 | struct trdb_stats stats; 77 | /* desired logging level and custom logging hook*/ 78 | int log_priority; 79 | void (*log_fn)(struct trdb_ctx *ctx, int priority, const char *file, 80 | int line, const char *fn, const char *format, va_list args); 81 | }; 82 | --------------------------------------------------------------------------------