From cf98ba14dc260458f757fa46419575cf69f45a44 Mon Sep 17 00:00:00 2001 From: Ruslan Bukin Date: Thu, 10 Oct 2019 13:19:21 +0000 Subject: [PATCH] Import OpenCSD -- an ARM CoreSight Trace Decode library. Git ID a1961c91b02a92f3c6ed8b145c636ac4c5565aca Sponsored by: DARPA, AFRL --- HOWTO.md | 41 +- README.md | 58 +- decoder/build/linux/makefile | 144 +++-- decoder/build/linux/makefile.dev | 62 ++ decoder/build/linux/rctdl_c_api_lib/makefile | 44 +- .../build/linux/ref_trace_decode_lib/makefile | 46 +- .../ref_trace_decode_lib.sln | 21 + .../ref_trace_decode_lib.vcxproj | 4 +- .../ref_trace_decode_lib.vcxproj.filters | 12 +- decoder/docs/build_libs.md | 89 ++- decoder/docs/doxygen_config.dox | 341 ++++++---- decoder/docs/prog_guide/cs_trace_hw.jpg | Bin 0 -> 78065 bytes .../docs/prog_guide/decode_data_path_resp.jpg | Bin 0 -> 29840 bytes decoder/docs/prog_guide/dt_components.jpg | Bin 0 -> 59125 bytes decoder/docs/prog_guide/lib_usage.jpg | Bin 0 -> 36058 bytes decoder/docs/prog_guide/memacc_objs.jpg | Bin 0 -> 42596 bytes .../prog_guide/prog_guide_generic_pkts.md | 400 ++++++++++++ decoder/docs/prog_guide/prog_guide_main.md | 597 ++++++++++++++++++ decoder/docs/test_progs.md | 61 +- decoder/include/common/ocsd_code_follower.h | 6 + decoder/include/common/ocsd_dcd_tree.h | 22 +- decoder/include/common/ocsd_error_logger.h | 4 +- decoder/include/common/ocsd_msg_logger.h | 25 +- decoder/include/common/trc_gen_elem.h | 11 +- decoder/include/i_dec/trc_i_decode.h | 1 + decoder/include/i_dec/trc_idec_arminst.h | 11 + decoder/include/interfaces/trc_error_log_i.h | 6 +- decoder/include/mem_acc/trc_mem_acc_base.h | 3 +- decoder/include/mem_acc/trc_mem_acc_bufptr.h | 2 +- decoder/include/mem_acc/trc_mem_acc_cache.h | 149 +++++ decoder/include/mem_acc/trc_mem_acc_cb.h | 25 +- decoder/include/mem_acc/trc_mem_acc_file.h | 4 +- decoder/include/mem_acc/trc_mem_acc_mapper.h | 5 +- .../include/opencsd/c_api/ocsd_c_api_types.h | 1 + decoder/include/opencsd/c_api/opencsd_c_api.h | 19 +- .../include/opencsd/etmv4/trc_cmp_cfg_etmv4.h | 20 +- .../opencsd/etmv4/trc_etmv4_stack_elem.h | 12 +- .../opencsd/etmv4/trc_pkt_decode_etmv4i.h | 13 +- .../opencsd/etmv4/trc_pkt_elem_etmv4i.h | 29 +- .../opencsd/etmv4/trc_pkt_types_etmv4.h | 139 ++-- decoder/include/opencsd/ocsd_if_types.h | 42 +- .../include/{ => opencsd}/ocsd_if_version.h | 8 +- decoder/include/opencsd/trc_gen_elem_types.h | 8 +- decoder/include/pkt_printers/pkt_printer_t.h | 2 +- decoder/source/c_api/ocsd_c_api.cpp | 13 +- decoder/source/etmv3/trc_pkt_decode_etmv3.cpp | 3 +- decoder/source/etmv4/trc_cmp_cfg_etmv4.cpp | 2 + decoder/source/etmv4/trc_etmv4_stack_elem.cpp | 14 +- .../source/etmv4/trc_pkt_decode_etmv4i.cpp | 188 +++--- decoder/source/etmv4/trc_pkt_elem_etmv4i.cpp | 359 ++++++----- .../source/etmv4/trc_pkt_proc_etmv4i_impl.cpp | 366 +++++++---- .../source/etmv4/trc_pkt_proc_etmv4i_impl.h | 75 ++- decoder/source/i_dec/trc_i_decode.cpp | 63 +- decoder/source/i_dec/trc_idec_arminst.cpp | 167 ++++- decoder/source/mem_acc/trc_mem_acc_bufptr.cpp | 2 +- decoder/source/mem_acc/trc_mem_acc_cache.cpp | 176 ++++++ decoder/source/mem_acc/trc_mem_acc_cb.cpp | 4 +- decoder/source/mem_acc/trc_mem_acc_file.cpp | 2 +- decoder/source/mem_acc/trc_mem_acc_mapper.cpp | 75 ++- decoder/source/ocsd_code_follower.cpp | 1 + decoder/source/ocsd_dcd_tree.cpp | 48 +- decoder/source/ocsd_error.cpp | 4 +- decoder/source/ocsd_error_logger.cpp | 2 +- decoder/source/ocsd_version.cpp | 2 +- decoder/source/ptm/trc_pkt_decode_ptm.cpp | 9 +- decoder/source/trc_core_arch_map.cpp | 12 + decoder/source/trc_frame_deformatter.cpp | 39 +- decoder/source/trc_gen_elem.cpp | 25 +- decoder/source/trc_printable_elem.cpp | 15 +- decoder/tests/auto-fdo/autofdo.md | 523 +++++++++++++++ decoder/tests/auto-fdo/record.sh | 68 ++ .../build/linux/c_api_pkt_print_test/makefile | 21 +- .../build/linux/echo_test_dcd_lib/makefile | 26 +- .../tests/build/linux/mem_buffer_eg/makefile | 90 +++ .../build/linux/snapshot_parser_lib/makefile | 22 +- .../tests/build/linux/trc_pkt_lister/makefile | 27 +- .../mem-buffer-eg/mem-buffer-eg.vcxproj | 154 +++++ .../mem-buffer-eg.vcxproj.filters | 22 + .../c_api_echo_test/ext_dcd_echo_test.c | 2 +- decoder/tests/run_pkt_decode_tests.bash | 78 +++ .../source/ss_to_dcdtree.cpp | 33 +- decoder/tests/source/c_api_pkt_print_test.c | 97 ++- decoder/tests/source/mem_buff_demo.cpp | 416 ++++++++++++ decoder/tests/source/trc_pkt_lister.cpp | 58 +- 84 files changed, 4795 insertions(+), 995 deletions(-) create mode 100644 decoder/build/linux/makefile.dev create mode 100644 decoder/docs/prog_guide/cs_trace_hw.jpg create mode 100644 decoder/docs/prog_guide/decode_data_path_resp.jpg create mode 100644 decoder/docs/prog_guide/dt_components.jpg create mode 100644 decoder/docs/prog_guide/lib_usage.jpg create mode 100644 decoder/docs/prog_guide/memacc_objs.jpg create mode 100644 decoder/docs/prog_guide/prog_guide_generic_pkts.md create mode 100644 decoder/docs/prog_guide/prog_guide_main.md create mode 100644 decoder/include/mem_acc/trc_mem_acc_cache.h rename decoder/include/{ => opencsd}/ocsd_if_version.h (88%) create mode 100644 decoder/source/mem_acc/trc_mem_acc_cache.cpp create mode 100644 decoder/tests/auto-fdo/autofdo.md create mode 100755 decoder/tests/auto-fdo/record.sh create mode 100644 decoder/tests/build/linux/mem_buffer_eg/makefile create mode 100644 decoder/tests/build/win-vs2015/mem-buffer-eg/mem-buffer-eg.vcxproj create mode 100644 decoder/tests/build/win-vs2015/mem-buffer-eg/mem-buffer-eg.vcxproj.filters create mode 100755 decoder/tests/run_pkt_decode_tests.bash create mode 100644 decoder/tests/source/mem_buff_demo.cpp diff --git a/HOWTO.md b/HOWTO.md index dfaf9de6452..b16294a317e 100644 --- a/HOWTO.md +++ b/HOWTO.md @@ -21,10 +21,10 @@ supplemented with modifications to the CoreSight framework and drivers to be usable by the Perf core. The remaining out of tree patches are being upstreamed incrementally. -From there compiling the perf tools with `make -C tools/perf` will yield a -`perf` executable that will support CoreSight trace collection. Note that if -traces are to be decompressed *off* target, there is no need to download and -compile the openCSD library (on the target). +From there compiling the perf tools with `make -C tools/perf CORESIGHT=1` will +yield a `perf` executable that will support CoreSight trace collection. Note +that if traces are to be decompressed *off* target, there is no need to download +and compile the openCSD library (on the target). Before launching a trace run a sink that will collect trace data needs to be identified. All CoreSight blocks identified by the framework are registed in @@ -306,7 +306,7 @@ and needs to be installed on a system prior to compilation. Information about the status of the openCSD library on a system is given at compile time by the perf tools build script: - linaro@t430:~/linaro/linux-kernel$ make VF=1 -C tools/perf + linaro@t430:~/linaro/linux-kernel$ make CORESIGHT=1 VF=1 -C tools/perf Auto-detecting system features: ... dwarf: [ on ] ... dwarf_getlocations: [ on ] @@ -376,8 +376,8 @@ build script where to get the header file and libraries, namely CSINCLUDES and CSLIBS: linaro@t430:~/linaro/linux-kernel$ export CSINCLUDES=~/linaro/coresight/my-opencsd/decoder/include/ - linaro@t430:~/linaro/linux-kernel$ export CSLIBS=~/linaro/coresight/my-opencsd/decoder/lib/linux64-rel/ - linaro@t430:~/linaro/linux-kernel$ make VF=1 -C tools/perf + linaro@t430:~/linaro/linux-kernel$ export CSLIBS=~/linaro/coresight/my-opencsd/decoder/lib/builddir/ + linaro@t430:~/linaro/linux-kernel$ make CORESIGHT=1 VF=1 -C tools/perf This will have the effect of compiling and linking against the provided library. Since the system's openCSD library is in the loader's search patch the @@ -611,37 +611,12 @@ will add the --dump option to the end of the command line and run Generating coverage files for Feedback Directed Optimization: AutoFDO --------------------------------------------------------------------- -Below is an example of using ARM ETM for autoFDO. The updates to the perf -support for this is experimental and available on the 'autoFDO' branch of -the [perf-opencsd github repository][1]. - -It also requires autofdo (https://github.com/google/autofdo) and gcc version 5. The bubble -sort example is from the AutoFDO tutorial (https://gcc.gnu.org/wiki/AutoFDO/Tutorial). - - $ gcc-5 -O3 sort.c -o sort_optimized - $ taskset -c 2 ./sort_optimized - Bubble sorting array of 30000 elements - 5910 ms - - $ perf record -e cs_etm/@20070000.etr/u --per-thread taskset -c 2 ./sort - Bubble sorting array of 30000 elements - 12543 ms - [ perf record: Woken up 35 times to write data ] - [ perf record: Captured and wrote 69.640 MB perf.data ] - - $ perf inject -i perf.data -o inj.data --itrace=il64 --strip - $ create_gcov --binary=./sort --profile=inj.data --gcov=sort.gcov -gcov_version=1 - $ gcc-5 -O3 -fauto-profile=sort.gcov sort.c -o sort_autofdo - $ taskset -c 2 ./sort_autofdo - Bubble sorting array of 30000 elements - 5806 ms +See autofdo.md (@ref AutoFDO) for details and scripts. The Linaro CoreSight Team ------------------------- - Mike Leach -- Tor Jeremiassen -- Chunyan Zang - Mathieu Poirier diff --git a/README.md b/README.md index 44b7c6807c3..c3f238ff194 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,11 @@ Releases will appear on the master branch in the git repository with an appropri CoreSight Trace Component Support. ---------------------------------- -_Current Version 0.8.2_ +_Current Version 0.12.0_ ### Current support: -- ETMv4 (v4.1) instruction trace - packet processing and packet decode. +- ETMv4 (v4.4) instruction trace - packet processing and packet decode. - PTM (v1.1) instruction trace - packet processing and packet decode. - ETMv3 (v3.5) instruction trace - packet processing and packet decode. - ETMv3 (v3.5) data trace - packet processing. @@ -73,12 +73,20 @@ Run `doxygen` on the `./doxygen_config.dox` file located in the `./docs` directo This will produce the documentation in the `./docs/html` directory. The doxygen configuration also includes the `*.md` files as part of the documentation. +Application Programming using the Library +----------------------------------------- -Building the Library --------------------- +See the [programmers guide](@ref prog_guide) for details on usage of the library in custom applications. +(`./docs/prog_guide/prog_guide_main.md`). + + +Building and Installing the Library +----------------------------------- See [build_libs.md](@ref build_lib) in the `./docs` directory for build details. +The linux build makefile now contains options to install the library for a linux environment. + How the Library is used in Linux `perf` --------------------------------------- @@ -88,6 +96,15 @@ with the standard linux perfomance analysis tool `perf`. See [HOWTO.md](@ref howto_perf) for details. +How to use the Library, perf and Trace for AutoFDO +-------------------------------------------------- +Capturing trace using perf and decoding using the library can +generate profiles for AutoFDO. + +See [autofdo.md](@ref AutoFDO) for details and scripts. + +(`./tests/auto-fdo/autofdo.md`). + Version and Modification Information ==================================== @@ -116,6 +133,39 @@ Version and Modification Information Library output naming changed from 'cstraced' to 'opencsd'. - _Version 0.8.1_: Minor updates: Use install tool to copy headers. Changes to HOWTO for perf usage. - _Version 0.8.2_: Bugfix: C++ init errors fixed for CLANG build process. +- _Version 0.8.3_: Bugfix: ETMv4 decoder issues fixed. +- _Version 0.8.4_: build: makefile updates and improvements to get build process compatible with Debian packaging. +- _Version 0.9.0_: Performance improvements for perf: Additional info in instruction range output packet. Caching memory accesses. + Added Programmers guide to documentation. +- _Version 0.9.1_: Bugfix: Crash during decode when first memory access is to address where no image provided. +- _Version 0.9.2_: Bugfix: ETMv4: Incorrect Exception number output for Genric exception packets. + AutoFDO: update documentation for AutoFDO usage and add in "record.sh" script +- _Version 0.9.3_: Bugfix: Test snapshot library not handling 'offset' parameters in dump file sections. + Install: ocsd_if_version.h moved to opencsd/include to allow installation on OS & use in compiling client apps. +- _Version 0.10.0_: __Updates__: Add additional information about the last instruction to the generic output packet. + __Docs__: update docs for updated output packet. + __Bugfix__: typecast removed from OCSD_VER_NUM in ocsd_if_version.h to allow use in C pre-processor. + __Bugfix__: ETMV4: Interworking ISA change between A32-T32 occasionally missed during instruction decode. +- _Version 0.10.1_: __Updates__: Build update - allow multi-thread make (make -j). + __Docs__: Minor update to AutoFDO documentation. +- _Version 0.11.0_: __Update__: ETM v4 decoder updated to support ETM version up to v4.4 + __Update__: Memory access callback function - added new callback signature to provide TraceID to client when requesting memory. + __Update__: Created new example program to demonstrate using memory buffer in APIs. + __Bugfix__: Typos in docs and source. + __Bugfix__: Memory accessor - validate callback return values. +- _Version 0.11.1_: __Update__: build:- change -fpic to -fPIC to allow Debian build on sparc. + __Bugfix__: build:- remove unused variable +- _Version 0.11.2_: __Update__: docs:- HOWTO.md update to match new perf build requirements. + __Bugfix__: Minor spelling typos fixed. +- _Version 0.12.0_: __Update__: Frame deformatter - TPIU FSYNC and HSYNC support added. + __Update__: ETM v4: Bugfix & clarification on Exception trace handling. Where exception occurs at a branch target before any instructions + have been executed, the preferred return address is also the target address of the branch instruction. This case now includes as specific flag in + the packet. Additionally any context change associated with this target address was being applied incorrectly. + __Update__: Core / Architecture mapping to core names as used by test programs / snapshots updated to include additional recent ARM cores. + __Update__: Docs: Update to reflect new exception flag. Update test program example to reflect latest output. + __Bugfix__: ETM v4: Valid trace info packet was not handled correctly (0x01, 0x00). + __Bugfix__: ETM v4: Error messaging on commit stack overflow. + Licence Information =================== diff --git a/decoder/build/linux/makefile b/decoder/build/linux/makefile index 330829b7708..6032c2cf728 100644 --- a/decoder/build/linux/makefile +++ b/decoder/build/linux/makefile @@ -33,9 +33,9 @@ # DEBUG=1 create a debug build # -# Set project root - relative to build directory +# Set project root - relative to build makefile ifeq ($(OCSD_ROOT),) -OCSD_ROOT := $(shell pwd | sed 's,/build/linux.*,,') +OCSD_ROOT := $(shell echo $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) | sed 's,/build/linux.*,,') export OCSD_ROOT endif @@ -45,14 +45,6 @@ export LIB_BASE_NAME LIB_CAPI_NAME=$(LIB_BASE_NAME)_c_api export LIB_CAPI_NAME -# determine base architecture, heavily borrowed from the Linux kernel v4.4's -# tools/perf/config/Makefile.arch -# For example, to compile for arm64 on a X86 PC, you can issue the command: -# "export ARCH=arm64" -ifndef ARCH -ARCH := $(shell uname -m 2>/dev/null || echo not) -endif - # source root directories export OCSD_LIB_ROOT=$(OCSD_ROOT)/lib @@ -60,71 +52,61 @@ export OCSD_INCLUDE=$(OCSD_ROOT)/include export OCSD_SOURCE=$(OCSD_ROOT)/source export OCSD_TESTS=$(OCSD_ROOT)/tests - export LIB_UAPI_INC_DIR=opencsd # tools export MASTER_CC=$(CROSS_COMPILE)gcc -export MASTER_CPP=$(CROSS_COMPILE)g++ +export MASTER_CXX=$(CROSS_COMPILE)g++ export MASTER_LINKER=$(CROSS_COMPILE)g++ export MASTER_LIB=$(CROSS_COMPILE)ar export INSTALL=install + # installation directory -INSTALL_LIB_DIR=/usr/lib/ -export INSTALL_INCLUDE_DIR=/usr/include/ +PREFIX ?=/usr +LIB_PATH ?= lib +INSTALL_LIB_DIR=$(PREFIX)/$(LIB_PATH) +INSTALL_BIN_DIR=$(PREFIX)/bin +export INSTALL_INCLUDE_DIR=$(PREFIX)/include/ # compile flags -MASTER_CC_FLAGS := -c -Wall -DLINUX -MASTER_CPP_FLAGS := -c -Wall -DLINUX -std=c++11 -MASTER_LINKER_FLAGS := -Wl,-z,defs -MASTER_LIB_FLAGS := rcs +CFLAGS += $(CPPFLAGS) -c -Wall -DLINUX -Wno-switch -fPIC +CXXFLAGS += $(CPPFLAGS) -c -Wall -DLINUX -Wno-switch -fPIC -std=c++11 +LDFLAGS += -Wl,-z,defs +ARFLAGS ?= rcs # debug variant ifdef DEBUG -MASTER_CC_FLAGS += -g -O0 -DDEBUG -MASTER_CPP_FLAGS += -g -O0 -DDEBUG +CFLAGS += -g -O0 -DDEBUG +CXXFLAGS += -g -O0 -DDEBUG BUILD_VARIANT=dbg else -MASTER_CC_FLAGS += -g -O2 -DNDEBUG -MASTER_CPP_FLAGS += -g -O2 -DNDEBUG +CFLAGS += -g -O2 -DNDEBUG +CXXFLAGS += -g -O2 -DNDEBUG BUILD_VARIANT=rel endif - -# platform bit size variant -ifeq ($(ARCH),x86) - MFLAG:="-m32" - BIT_VARIANT=32 -else ifeq ($(ARCH),x86_64) - MFLAG:="-m64" - BIT_VARIANT=64 -else ifeq ($(ARCH),arm) - BIT_VARIANT=-arm -else ifeq ($(ARCH),arm64) - BIT_VARIANT=-arm64 -else ifeq ($(ARCH),aarch64) - BIT_VARIANT=-arm64 -else ifeq ($(ARCH),aarch32) - BIT_VARIANT=-arm -endif - -MASTER_CC_FLAGS += $(MFLAG) -MASTER_CPP_FLAGS += $(MFLAG) -MASTER_LINKER_FLAGS += $(MFLAG) - # export build flags -export MASTER_CC_FLAGS -export MASTER_CPP_FLAGS -export MASTER_LINKER_FLAGS -export MASTER_LIB_FLAGS +export CFLAGS +export CXXFLAGS +export LDFLAGS +export ARFLAGS -# target directories -export PLAT_DIR=linux$(BIT_VARIANT)/$(BUILD_VARIANT) +# target directories - fixed for default packaging build +PLAT_DIR ?= builddir +export PLAT_DIR export LIB_TARGET_DIR=$(OCSD_LIB_ROOT)/$(PLAT_DIR) export LIB_TEST_TARGET_DIR=$(OCSD_TESTS)/lib/$(PLAT_DIR) export BIN_TEST_TARGET_DIR=$(OCSD_TESTS)/bin/$(PLAT_DIR) +# Fish version out of header file (converting from hex) +getver:=printf "%d" $$(awk '/\#define VARNAME/ { print $$3 }' $(OCSD_ROOT)/include/opencsd/ocsd_if_version.h) +export SO_MAJOR_VER := $(shell $(subst VARNAME,OCSD_VER_MAJOR,$(getver))) +SO_MINOR_VER := $(shell $(subst VARNAME,OCSD_VER_MINOR,$(getver))) +SO_PATCH_VER := $(shell $(subst VARNAME,OCSD_VER_PATCH,$(getver))) +export SO_VER := $(SO_MAJOR_VER).$(SO_MINOR_VER).$(SO_PATCH_VER) + + ########################################################### # build targets @@ -132,10 +114,19 @@ all: libs tests libs: $(LIB_BASE_NAME)_lib $(LIB_CAPI_NAME)_lib -install: libs - $(INSTALL) --mode=644 $(LIB_TARGET_DIR)/lib$(LIB_BASE_NAME).so $(INSTALL_LIB_DIR)/ - $(INSTALL) --mode=644 $(LIB_TARGET_DIR)/lib$(LIB_CAPI_NAME).so $(INSTALL_LIB_DIR)/ +install: libs tests + mkdir -p $(INSTALL_LIB_DIR) $(INSTALL_INCLUDE_DIR) $(INSTALL_BIN_DIR) + cp -d $(LIB_TARGET_DIR)/lib$(LIB_BASE_NAME).so $(INSTALL_LIB_DIR)/ + cp -d $(LIB_TARGET_DIR)/lib$(LIB_BASE_NAME).so.$(SO_MAJOR_VER) $(INSTALL_LIB_DIR)/ + $(INSTALL) --mode=644 $(LIB_TARGET_DIR)/lib$(LIB_BASE_NAME).so.$(SO_VER) $(INSTALL_LIB_DIR)/ + cp -d $(LIB_TARGET_DIR)/lib$(LIB_CAPI_NAME).so $(INSTALL_LIB_DIR)/ + cp -d $(LIB_TARGET_DIR)/lib$(LIB_CAPI_NAME).so.$(SO_MAJOR_VER) $(INSTALL_LIB_DIR)/ + $(INSTALL) --mode=644 $(LIB_TARGET_DIR)/lib$(LIB_CAPI_NAME).so.$(SO_VER) $(INSTALL_LIB_DIR)/ + $(INSTALL) --mode=644 $(LIB_TARGET_DIR)/lib$(LIB_BASE_NAME).a $(INSTALL_LIB_DIR)/ + $(INSTALL) --mode=644 $(LIB_TARGET_DIR)/lib$(LIB_CAPI_NAME).a $(INSTALL_LIB_DIR)/ cd $(OCSD_ROOT)/build/linux/rctdl_c_api_lib && make install_inc + $(INSTALL) --mode=755 $(BIN_TEST_TARGET_DIR)/trc_pkt_lister $(INSTALL_BIN_DIR)/ + ################################ # build OpenCSD trace decode library @@ -148,7 +139,7 @@ $(LIB_TARGET_DIR)/lib$(LIB_BASE_NAME).a: $(LIB_BASE_NAME)_all # single command builds both .a and .so targets in sub-makefile $(LIB_BASE_NAME)_all: mkdir -p $(LIB_TARGET_DIR) - cd $(OCSD_ROOT)/build/linux/ref_trace_decode_lib && make + cd $(OCSD_ROOT)/build/linux/ref_trace_decode_lib && $(MAKE) ################################ # build OpenCSD trace decode C API library @@ -161,36 +152,49 @@ $(LIB_TARGET_DIR)/lib$(LIB_CAPI_NAME).a: $(LIB_CAPI_NAME)_all # single command builds both .a and .so targets in sub-makefile $(LIB_CAPI_NAME)_all: $(LIB_BASE_NAME)_lib mkdir -p $(LIB_TARGET_DIR) - cd $(OCSD_ROOT)/build/linux/rctdl_c_api_lib && make + cd $(OCSD_ROOT)/build/linux/rctdl_c_api_lib && $(MAKE) ################################# # build tests .PHONY: tests tests: libs - cd $(OCSD_ROOT)/tests/build/linux/echo_test_dcd_lib && make - cd $(OCSD_ROOT)/tests/build/linux/snapshot_parser_lib && make - cd $(OCSD_ROOT)/tests/build/linux/trc_pkt_lister && make - cd $(OCSD_ROOT)/tests/build/linux/c_api_pkt_print_test && make + cd $(OCSD_ROOT)/tests/build/linux/echo_test_dcd_lib && $(MAKE) + cd $(OCSD_ROOT)/tests/build/linux/snapshot_parser_lib && $(MAKE) + cd $(OCSD_ROOT)/tests/build/linux/trc_pkt_lister && $(MAKE) + cd $(OCSD_ROOT)/tests/build/linux/c_api_pkt_print_test && $(MAKE) + cd $(OCSD_ROOT)/tests/build/linux/mem_buffer_eg && $(MAKE) + +# +# build docs +.PHONY: docs +docs: + (cd $(OCSD_ROOT)/docs; doxygen doxygen_config.dox) + ############################################################# # clean targets # -clean: clean_libs clean_tests +clean: clean_libs clean_tests clean_docs -.PHONY: clean_libs clean_tests +.PHONY: clean_libs clean_tests clean_docs clean_install clean_libs: - cd $(OCSD_ROOT)/build/linux/ref_trace_decode_lib && make clean - cd $(OCSD_ROOT)/build/linux/rctdl_c_api_lib && make clean + cd $(OCSD_ROOT)/build/linux/ref_trace_decode_lib && $(MAKE) clean + cd $(OCSD_ROOT)/build/linux/rctdl_c_api_lib && $(MAKE) clean clean_tests: - cd $(OCSD_ROOT)/tests/build/linux/echo_test_dcd_lib && make clean - cd $(OCSD_ROOT)/tests/build/linux/snapshot_parser_lib && make clean - cd $(OCSD_ROOT)/tests/build/linux/trc_pkt_lister && make clean - cd $(OCSD_ROOT)/tests/build/linux/c_api_pkt_print_test && make clean + cd $(OCSD_ROOT)/tests/build/linux/echo_test_dcd_lib && $(MAKE) clean + cd $(OCSD_ROOT)/tests/build/linux/snapshot_parser_lib && $(MAKE) clean + cd $(OCSD_ROOT)/tests/build/linux/trc_pkt_lister && $(MAKE) clean + cd $(OCSD_ROOT)/tests/build/linux/c_api_pkt_print_test && $(MAKE) clean + cd $(OCSD_ROOT)/tests/build/linux/mem_buffer_eg && $(MAKE) clean + -rmdir $(OCSD_TESTS)/lib + +clean_docs: + -rm -r $(OCSD_ROOT)/docs/html clean_install: - rm -f $(INSTALL_LIB_DIR)/lib$(LIB_BASE_NAME).so - rm -f $(INSTALL_LIB_DIR)/lib$(LIB_CAPI_NAME).so - rm -rf $(INSTALL_INCLUDE_DIR)/$(LIB_UAPI_INC_DIR) + -rm $(INSTALL_LIB_DIR)/lib$(LIB_BASE_NAME).* + -rm $(INSTALL_LIB_DIR)/lib$(LIB_CAPI_NAME).* + -rm -r $(INSTALL_INCLUDE_DIR)/$(LIB_UAPI_INC_DIR) diff --git a/decoder/build/linux/makefile.dev b/decoder/build/linux/makefile.dev new file mode 100644 index 00000000000..7c02328db2a --- /dev/null +++ b/decoder/build/linux/makefile.dev @@ -0,0 +1,62 @@ +######################################################## +# Copyright 2018 ARM Limited. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +################################################################################# + +## Set up some addtional parameters for development environment builds. ## + +## define arch/build sub-dirs for non installed dev builds +ifndef ARCH +ARCH := $(shell dpkg-architecture -q DEB_HOST_GNU_CPU || echo not) +endif + +# platform bit size variant +ifeq ($(ARCH),x86) + MFLAG:="-m32" + BIT_VARIANT=32 +else ifeq ($(ARCH),x86_64) + MFLAG:="-m64" + BIT_VARIANT=64 +else ifeq ($(ARCH),arm) + BIT_VARIANT=-arm +else ifeq ($(ARCH),arm64) + BIT_VARIANT=-arm64 +else ifeq ($(ARCH),aarch64) + BIT_VARIANT=-arm64 +else ifeq ($(ARCH),aarch32) + BIT_VARIANT=-arm +endif + +CXXFLAGS += $(MFLAG) +CFLAGS += $(MFLAG) +LDFLAGS += $(MFLAG) + +PLAT_DIR=linux$(BIT_VARIANT)/$(BUILD_VARIANT) + +# include the main makefile +include makefile diff --git a/decoder/build/linux/rctdl_c_api_lib/makefile b/decoder/build/linux/rctdl_c_api_lib/makefile index fb54d3ad8c2..a0bd5a345f2 100644 --- a/decoder/build/linux/rctdl_c_api_lib/makefile +++ b/decoder/build/linux/rctdl_c_api_lib/makefile @@ -30,14 +30,10 @@ # OpenCSD - makefile for C API wrapper library # -CPP := $(MASTER_CPP) +CXX := $(MASTER_CXX) LINKER := $(MASTER_LINKER) LIB := $(MASTER_LIB) -CPP_FLAGS := $(MASTER_CPP_FLAGS) -fpic -Wno-switch -LIB_FLAGS := $(MASTER_LIB_FLAGS) -LINKER_FLAGS := $(MASTER_LINKER_FLAGS) -shared - LIB_NAME = lib$(LIB_CAPI_NAME) SO_LIB_DEPS= -L$(LIB_TARGET_DIR) -l$(LIB_BASE_NAME) @@ -45,7 +41,7 @@ BUILD_DIR=./$(PLAT_DIR) VPATH= $(OCSD_SOURCE)/c_api -CPP_INCLUDES= \ +CXX_INCLUDES= \ -I$(OCSD_INCLUDE) \ -I$(OCSD_SOURCE)/c_api @@ -55,14 +51,32 @@ OBJECTS=$(BUILD_DIR)/ocsd_c_api.o \ INST_INC_SRC=$(OCSD_INCLUDE)/$(LIB_UAPI_INC_DIR) INST_INC_DST=$(INSTALL_INCLUDE_DIR)/$(LIB_UAPI_INC_DIR) -all: build_dir $(OBJECTS) - mkdir -p $(LIB_TARGET_DIR) - $(LIB) $(LIB_FLAGS) $(LIB_TARGET_DIR)/$(LIB_NAME).a $(OBJECTS) - $(LINKER) $(LINKER_FLAGS) -o $(LIB_TARGET_DIR)/$(LIB_NAME).so -Wl,-soname,$(LIB_NAME).so $(OBJECTS) $(SO_LIB_DEPS) +all: links -build_dir: +links: $(LIB_TARGET_DIR)/$(LIB_NAME).so.$(SO_MAJOR_VER) $(LIB_TARGET_DIR)/$(LIB_NAME).so +.PHONY: links + +LIBS:= $(LIB_TARGET_DIR)/$(LIB_NAME).a $(LIB_TARGET_DIR)/$(LIB_NAME).so.$(SO_VER) + +$(LIB_TARGET_DIR): + mkdir -p $(LIB_TARGET_DIR) + +$(BUILD_DIR): mkdir -p $(BUILD_DIR) +$(LIB_TARGET_DIR)/$(LIB_NAME).a: $(OBJECTS) | $(BUILD_DIR) $(LIB_TARGET_DIR) + $(LIB) $(ARFLAGS) $(LIB_TARGET_DIR)/$(LIB_NAME).a $(OBJECTS) + +$(LIB_TARGET_DIR)/$(LIB_NAME).so.$(SO_VER): $(OBJECTS) | $(BUILD_DIR) $(LIB_TARGET_DIR) + $(LINKER) $(LDFLAGS) -shared -o $(LIB_TARGET_DIR)/$(LIB_NAME).so.$(SO_VER) -Wl,-soname,$(LIB_NAME).so.$(SO_MAJOR_VER) $(OBJECTS) $(SO_LIB_DEPS) + +$(LIB_TARGET_DIR)/$(LIB_NAME).so.$(SO_MAJOR_VER): $(LIBS) | $(LIB_TARGET_DIR) + ( cd $(LIB_TARGET_DIR); ln -sf $(LIB_NAME).so.$(SO_VER) $(LIB_NAME).so.$(SO_MAJOR_VER) ) + +$(LIB_TARGET_DIR)/$(LIB_NAME).so: $(LIB_TARGET_DIR)/$(LIB_NAME).so.$(SO_MAJOR_VER) | $(LIB_TARGET_DIR) + ( cd $(LIB_TARGET_DIR); ln -sf $(LIB_NAME).so.$(SO_MAJOR_VER) $(LIB_NAME).so ) + + ##### build rules ## object dependencies @@ -71,8 +85,8 @@ DEPS := $(OBJECTS:%.o=%.d) -include $(DEPS) ## object compile -$(BUILD_DIR)/%.o : %.cpp - $(CPP) $(CPP_FLAGS) $(CPP_INCLUDES) -MMD $< -o $@ +$(BUILD_DIR)/%.o : %.cpp | $(BUILD_DIR) + $(CXX) $(CXXFLAGS) $(CXX_INCLUDES) -MMD $< -o $@ #### clean @@ -81,13 +95,15 @@ clean: rm -f $(OBJECTS) rm -f $(DEPS) rm -f $(LIB_TARGET_DIR)/$(LIB_NAME).a - rm -f $(LIB_TARGET_DIR)/$(LIB_NAME).so + rm -f $(LIB_TARGET_DIR)/$(LIB_NAME).so* + -rmdir $(BUILD_DIR) #### install the necessary include files for the c-api library on linux install_inc: $(INSTALL) -d --mode=0755 $(INST_INC_DST)/ $(INSTALL) --mode=0644 $(INST_INC_SRC)/trc_gen_elem_types.h $(INST_INC_DST)/ $(INSTALL) --mode=0644 $(INST_INC_SRC)/ocsd_if_types.h $(INST_INC_DST)/ + $(INSTALL) --mode=0644 $(INST_INC_SRC)/ocsd_if_version.h $(INST_INC_DST)/ $(INSTALL) --mode=0644 $(INST_INC_SRC)/trc_pkt_types.h $(INST_INC_DST)/ $(INSTALL) -d --mode=0755 $(INST_INC_DST)/ptm $(INSTALL) --mode=0644 $(INST_INC_SRC)/ptm/trc_pkt_types_ptm.h $(INST_INC_DST)/ptm/ diff --git a/decoder/build/linux/ref_trace_decode_lib/makefile b/decoder/build/linux/ref_trace_decode_lib/makefile index a356066d009..373e8248bd6 100644 --- a/decoder/build/linux/ref_trace_decode_lib/makefile +++ b/decoder/build/linux/ref_trace_decode_lib/makefile @@ -30,14 +30,10 @@ # OpenCSD - makefile for main trace decode library # -CPP := $(MASTER_CPP) +CXX := $(MASTER_CXX) LINKER := $(MASTER_LINKER) LIB := $(MASTER_LIB) -CPP_FLAGS := $(MASTER_CPP_FLAGS) -fpic -Wno-switch -LIB_FLAGS := $(MASTER_LIB_FLAGS) -LINKER_FLAGS := $(MASTER_LINKER_FLAGS) -shared - LIB_NAME= lib$(LIB_BASE_NAME) BUILD_DIR=./$(PLAT_DIR) @@ -52,7 +48,7 @@ VPATH= $(OCSD_SOURCE) \ $(OCSD_SOURCE)/pkt_printers -CPP_INCLUDES= \ +CXX_INCLUDES= \ -I$(OCSD_INCLUDE) \ -I$(OCSD_SOURCE) @@ -82,7 +78,8 @@ MEMACCOBJ= $(BUILD_DIR)/trc_mem_acc_mapper.o \ $(BUILD_DIR)/trc_mem_acc_bufptr.o \ $(BUILD_DIR)/trc_mem_acc_file.o \ $(BUILD_DIR)/trc_mem_acc_base.o \ - $(BUILD_DIR)/trc_mem_acc_cb.o + $(BUILD_DIR)/trc_mem_acc_cb.o \ + $(BUILD_DIR)/trc_mem_acc_cache.o STMOBJ= $(BUILD_DIR)/trc_pkt_elem_stm.o \ $(BUILD_DIR)/trc_pkt_proc_stm.o \ @@ -114,14 +111,32 @@ OBJECTS=$(BUILD_DIR)/ocsd_code_follower.o \ $(PTMOBJ) \ $(PKTPRNTOBJ) -all: build_dir $(OBJECTS) - mkdir -p $(LIB_TARGET_DIR) - $(LIB) $(LIB_FLAGS) $(LIB_TARGET_DIR)/$(LIB_NAME).a $(OBJECTS) - $(LINKER) $(LINKER_FLAGS) -o $(LIB_TARGET_DIR)/$(LIB_NAME).so -Wl,-soname,$(LIB_NAME).so $(OBJECTS) +all: links -build_dir: +links: $(LIB_TARGET_DIR)/$(LIB_NAME).so.$(SO_MAJOR_VER) $(LIB_TARGET_DIR)/$(LIB_NAME).so +.PHONY: links + +LIBS:= $(LIB_TARGET_DIR)/$(LIB_NAME).a $(LIB_TARGET_DIR)/$(LIB_NAME).so.$(SO_VER) + +$(LIB_TARGET_DIR): + mkdir -p $(LIB_TARGET_DIR) + +$(BUILD_DIR): mkdir -p $(BUILD_DIR) +$(LIB_TARGET_DIR)/$(LIB_NAME).a: $(OBJECTS) | $(BUILD_DIR) $(LIB_TARGET_DIR) + $(LIB) $(ARFLAGS) $(LIB_TARGET_DIR)/$(LIB_NAME).a $(OBJECTS) + +$(LIB_TARGET_DIR)/$(LIB_NAME).so.$(SO_VER): $(OBJECTS) | $(BUILD_DIR) $(LIB_TARGET_DIR) + $(LINKER) $(LDFLAGS) -shared -o $(LIB_TARGET_DIR)/$(LIB_NAME).so.$(SO_VER) -Wl,-soname,$(LIB_NAME).so.$(SO_MAJOR_VER) $(OBJECTS) + +$(LIB_TARGET_DIR)/$(LIB_NAME).so.$(SO_MAJOR_VER): $(LIBS) | $(LIB_TARGET_DIR) + ( cd $(LIB_TARGET_DIR); ln -sf $(LIB_NAME).so.$(SO_VER) $(LIB_NAME).so.$(SO_MAJOR_VER) ) + +$(LIB_TARGET_DIR)/$(LIB_NAME).so: $(LIB_TARGET_DIR)/$(LIB_NAME).so.$(SO_MAJOR_VER) | $(LIB_TARGET_DIR) + ( cd $(LIB_TARGET_DIR); ln -sf $(LIB_NAME).so.$(SO_MAJOR_VER) $(LIB_NAME).so ) + + ##### build rules ## object dependencies @@ -130,8 +145,8 @@ DEPS := $(OBJECTS:%.o=%.d) -include $(DEPS) ## object compile -$(BUILD_DIR)/%.o : %.cpp - $(CPP) $(CPP_FLAGS) $(CPP_INCLUDES) -MMD $< -o $@ +$(BUILD_DIR)/%.o : %.cpp | $(BUILD_DIR) + $(CXX) $(CXXFLAGS) $(CXX_INCLUDES) -MMD $< -o $@ #### clean @@ -140,4 +155,5 @@ clean: rm -f $(OBJECTS) rm -f $(DEPS) rm -f $(LIB_TARGET_DIR)/$(LIB_NAME).a - rm -f $(LIB_TARGET_DIR)/$(LIB_NAME).so + rm -f $(LIB_TARGET_DIR)/$(LIB_NAME).so* + -rmdir $(BUILD_DIR) diff --git a/decoder/build/win-vs2015/ref_trace_decode_lib/ref_trace_decode_lib.sln b/decoder/build/win-vs2015/ref_trace_decode_lib/ref_trace_decode_lib.sln index 94b561a3984..4f3ddebeb32 100644 --- a/decoder/build/win-vs2015/ref_trace_decode_lib/ref_trace_decode_lib.sln +++ b/decoder/build/win-vs2015/ref_trace_decode_lib/ref_trace_decode_lib.sln @@ -28,6 +28,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ext_dcd_echo_test", "..\..\ {533F929A-A73B-46B6-9D5F-FFCD62F734E3} = {533F929A-A73B-46B6-9D5F-FFCD62F734E3} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mem-buffer-eg", "..\..\..\tests\build\win-vs2015\mem-buffer-eg\mem-buffer-eg.vcxproj", "{BC090130-2C53-4CF6-8AD4-37BF72B8D01A}" + ProjectSection(ProjectDependencies) = postProject + {7F500891-CC76-405F-933F-F682BC39F923} = {7F500891-CC76-405F-933F-F682BC39F923} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -128,6 +133,22 @@ Global {46219A32-8178-41C1-B3B1-B5A6E547515F}.Release-dll|Win32.Build.0 = Release|Win32 {46219A32-8178-41C1-B3B1-B5A6E547515F}.Release-dll|x64.ActiveCfg = Release|x64 {46219A32-8178-41C1-B3B1-B5A6E547515F}.Release-dll|x64.Build.0 = Release|x64 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Debug|Win32.ActiveCfg = Debug|Win32 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Debug|Win32.Build.0 = Debug|Win32 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Debug|x64.ActiveCfg = Debug|x64 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Debug|x64.Build.0 = Debug|x64 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Debug-dll|Win32.ActiveCfg = Debug|Win32 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Debug-dll|Win32.Build.0 = Debug|Win32 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Debug-dll|x64.ActiveCfg = Debug|x64 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Debug-dll|x64.Build.0 = Debug|x64 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Release|Win32.ActiveCfg = Release|Win32 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Release|Win32.Build.0 = Release|Win32 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Release|x64.ActiveCfg = Release|x64 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Release|x64.Build.0 = Release|x64 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Release-dll|Win32.ActiveCfg = Release|Win32 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Release-dll|Win32.Build.0 = Release|Win32 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Release-dll|x64.ActiveCfg = Release|x64 + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A}.Release-dll|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/decoder/build/win-vs2015/ref_trace_decode_lib/ref_trace_decode_lib.vcxproj b/decoder/build/win-vs2015/ref_trace_decode_lib/ref_trace_decode_lib.vcxproj index 123fc006110..095f27b56ac 100644 --- a/decoder/build/win-vs2015/ref_trace_decode_lib/ref_trace_decode_lib.vcxproj +++ b/decoder/build/win-vs2015/ref_trace_decode_lib/ref_trace_decode_lib.vcxproj @@ -337,6 +337,7 @@ + @@ -374,8 +375,8 @@ - + @@ -420,6 +421,7 @@ + diff --git a/decoder/build/win-vs2015/ref_trace_decode_lib/ref_trace_decode_lib.vcxproj.filters b/decoder/build/win-vs2015/ref_trace_decode_lib/ref_trace_decode_lib.vcxproj.filters index cceb92bd83f..1ef87207089 100644 --- a/decoder/build/win-vs2015/ref_trace_decode_lib/ref_trace_decode_lib.vcxproj.filters +++ b/decoder/build/win-vs2015/ref_trace_decode_lib/ref_trace_decode_lib.vcxproj.filters @@ -320,9 +320,6 @@ Header Files\stm - - Header Files - Header Files\pkt_printers @@ -356,6 +353,12 @@ Header Files + + Header Files\mem_acc + + + Header Files + @@ -484,5 +487,8 @@ Source Files\etmv4 + + Source Files\mem_acc + \ No newline at end of file diff --git a/decoder/docs/build_libs.md b/decoder/docs/build_libs.md index b7c8536ca5f..dc7d85da940 100644 --- a/decoder/docs/build_libs.md +++ b/decoder/docs/build_libs.md @@ -6,47 +6,102 @@ Building and using the Library {#build_lib} Platform Support ---------------- -The current makefiles and build projects support building the library on Linux and Windows, -x86 or x64 hosts. - -Support is expected for ARM linux and baremetal, AArch32 and AArch64 platforms. +The current makefiles and build projects support building the library on: + - Linux and Windows, x86 or x64 hosts. + - ARM linux - AArch32 and AArch64 + - ARM aarch32 and aarch64 libs, x-compiled on x86/64 hosts. +In addition to building the library from the project, the library may be installed into the standard +`/usr/lib/` area in Linux, and will soon be available as a package from Linux Distros. Building the Library -------------------- -The library and test programs are built from the library `./build/` directory. +The library and test programs are built from the library `./build/` directory, where + is either 'linux' or 'win-vs2015' See [`./docs/test_progs.md`](@ref test_progs) for further information on use of the test programs. -### Linux x86/x64 ### +### Linux x86/x64/ARM ### -Go to the `./build/linux/` and run `make` in that directory. +Libraries are built into a . This is used as the final output directory for the +libraries in `decoder/lib/`, and also as a sub-directory of the build process for +intermediate files - `decoder/build/linux/ref_trace_decode_lib/`. -Options to pass to the makefile are:- -- `LINUX64=1` : build the 64 bit version of the library +For a standard build, go to the `./build/linux/` and run `make` in that directory. + +This will set to `builddir` for all build variants of the library. Using this only one variant of the library can be built at any one time. + +For development, alternatively use `make -f makefile.dev` + +This will set to `linux/` and therefore build libraries into the +`decoder/lib/linux/` directories, allowing multiple variants of the library +to be present during development. + +e.g. + +`./lib/linux64/rel` will contain the linux 64 bit release libraries. + +`./lib/linux-arm64/dbg` will contain the linux aarch 64 debug libraries for ARM. + +Options to pass to both makefiles are:- - `DEBUG=1` : build the debug version of the library. -Libraries are delivered to the `./lib/linux/` directories. -e.g. `./lib/linux64/rel` will contain the linux 64 bit release libraries. +Options to pass to makefile.dev are:- +- ARCH= : sets the bit variant in the delivery directories. Set if cross compilation for ARCH + other than host. Otherwise ARCH is auto-detected. + can be x86, x86_64, arm, arm64, aarch64, aarch32 -The following libraries are built:- -- `libcstraced.so` : shared library containing the main C++ based decoder library -- `libcstraced_c_api.so` : shared library containing the C-API wrapper library. Dependent on `libcstraced.so` +For cross compilation, set the environment variable `CROSS_COMPILE` to the name path/prefix for the +compiler to use. The following would set the environment to cross-compile for ARM + + export PATH=$PATH:~/work/gcc-x-aarch64-6.2/bin + export ARCH=arm64 + export CROSS_COMPILE=aarch64-linux-gnu- + +The makefile will scan the `ocsd_if_version.h` to get the library version numbers and use these +in the form Major.minor.patch when naming the output .so files. + +Main C++ library names: +- `libcstraced.so.M.m.p` : shared library containing the main C++ based decoder library +- `libcstrace.so.M` : symbolic link name to library - major version only. +- `libcstrace.so` : symbolic link name to library - no version. + +C API wrapper library names: +- `libcstraced_c_api.so.M.m.p` : shared library containing the C-API wrapper library. Dependent on `libcstraced.so.M` +- `libcstraced_c_api.so.M` : symbolic link name to library - major version only. +- `libcstraced_c_api.so` : symbolic link name to library - no version. + +Static versions of the libraries: - `libcstraced.a` : static library containing the main C++ based decoder library. - `libcstraced_c_api.a` : static library containing the C-API wrapper library. -Test programs are delivered to the `./tests/bin/linux/` directories. +Test programs are delivered to the `./tests/bin/` directories. The test programs are built to used the .so versions of the libraries. - `trc_pkt_lister` - dependent on `libcstraced.so`. - `simple_pkt_print_c_api` - dependent on `libcstraced_c_api.so` & hence `libcstraced.so`. The test program build for `trc_pkt_lister` also builds an auxiliary library used by this program for test purposes only. -This is the `libsnapshot_parser.a` library, delivered to the `./tests/lib/linux/` directories. +This is the `libsnapshot_parser.a` library, delivered to the `./tests/lib/` directories. -### Windows ### +__Installing on Linux__ + +The libraries can be installed on linux using the `make install` command. This will usually require root privileges. Installation will be the version in the `./lib/` directory, according to options chosen. + +e.g. ` make -f makefile.dev DEBUG=1 install` + +will install from `./lib/linux64/dbg` + +The libraries `libopencsd` and `libopencsd_c_api` are installed to `/usr/lib`. + +Sufficient header files to build using the C-API library will be installed to `/usr/include/opencsd`. + +The installation can be removed using `make clean_install`. No additional options are necessary. + + +### Windows (x86/x64) ### Use the `.\build\win\ref_trace_decode_lib\ref_trace_decode_lib.sln` file to load a solution which contains all library and test build projects. diff --git a/decoder/docs/doxygen_config.dox b/decoder/docs/doxygen_config.dox index acc6137f426..0ca0cf7349b 100644 --- a/decoder/docs/doxygen_config.dox +++ b/decoder/docs/doxygen_config.dox @@ -1,4 +1,4 @@ -# Doxyfile 1.8.8 +# Doxyfile 1.8.12 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -38,7 +38,7 @@ PROJECT_NAME = "OpenCSD - CoreSight Trace Decode Library" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 0.5 +PROJECT_NUMBER = 0.10.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -46,10 +46,10 @@ PROJECT_NUMBER = 0.5 PROJECT_BRIEF = -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. PROJECT_LOGO = @@ -60,7 +60,7 @@ PROJECT_LOGO = OUTPUT_DIRECTORY = ./. -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where @@ -93,14 +93,14 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the @@ -145,7 +145,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. @@ -215,9 +215,9 @@ MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO @@ -286,7 +286,7 @@ OPTIMIZE_OUTPUT_VHDL = NO # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # -# Note For files without extension you can use no_extension as a placeholder. +# Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. @@ -303,10 +303,19 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES @@ -346,13 +355,20 @@ SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first +# tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent @@ -411,7 +427,7 @@ LOOKUP_CACHE_SIZE = 0 # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -421,35 +437,35 @@ LOOKUP_CACHE_SIZE = 0 EXTRACT_ALL = YES -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local methods, +# This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are +# included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. @@ -474,21 +490,21 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be +# (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these +# documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. @@ -502,7 +518,7 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also +# names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. @@ -511,12 +527,19 @@ INTERNAL_DOCS = NO CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the +# their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -544,14 +567,14 @@ INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. +# name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that +# name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. @@ -596,27 +619,25 @@ SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. @@ -641,8 +662,8 @@ ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES @@ -706,7 +727,7 @@ CITE_BIB_FILES = QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. @@ -714,7 +735,7 @@ QUIET = NO WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. @@ -731,12 +752,18 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated @@ -760,22 +787,25 @@ WARN_LOGFILE = # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with -# spaces. +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = ../include \ ../include/interfaces \ - ../include/etmv3 \ - ../include/etmv4 \ - ../include/ptm \ - ../include/c_api \ - ../include/stm \ + ../include/opencsd/etmv3 \ + ../include/opencsd/etmv4 \ + ../include/opencsd/ptm \ + ../include/opencsd/c_api \ + ../include/opencsd/stm \ ../include/mem_acc \ ../../README.md \ . \ ../../HOWTO.md \ ../include/common \ - ../include + ./prog_guide \ + ../include/opencsd \ + ../include \ + ../tests/auto-fdo/autofdo.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -788,12 +818,17 @@ INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. FILE_PATTERNS = *.c \ *.cc \ @@ -904,7 +939,7 @@ EXAMPLE_RECURSIVE = NO # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = +IMAGE_PATH = prog_guide # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -920,6 +955,10 @@ IMAGE_PATH = # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. INPUT_FILTER = @@ -929,11 +968,15 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for +# INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. @@ -993,7 +1036,7 @@ REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. @@ -1040,13 +1083,13 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES -# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: http://clang.llvm.org/) for more accurate parsing at the # cost of reduced performance. This can be particularly helpful with template # rich C++ code for which doxygen's built-in parser lacks the necessary type # information. # Note: The availability of this option depends on whether or not doxygen was -# compiled with the --with-libclang option. +# generated with the -Duse-libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO @@ -1089,7 +1132,7 @@ IGNORE_PREFIX = # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES @@ -1155,10 +1198,10 @@ HTML_STYLESHEET = # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. +# standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. -# Note: The order of the extra stylesheet files is of importance (e.g. the last -# stylesheet in the list overrules the setting of the previous ones in the +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1175,7 +1218,7 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to +# will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 @@ -1206,8 +1249,9 @@ HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES @@ -1303,28 +1347,28 @@ GENERATE_HTMLHELP = NO CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1438,7 +1482,7 @@ DISABLE_INDEX = NO # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has @@ -1466,7 +1510,7 @@ ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1495,7 +1539,7 @@ FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. @@ -1581,7 +1625,7 @@ SERVER_BASED_SEARCH = NO # external search engine pointed to by the SEARCHENGINE_URL option to obtain the # search results. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library # Xapian (see: http://xapian.org/). # @@ -1594,7 +1638,7 @@ EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will return the search results when EXTERNAL_SEARCH is enabled. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library # Xapian (see: http://xapian.org/). See the section "External Indexing and # Searching" for details. @@ -1632,7 +1676,7 @@ EXTRA_SEARCH_MAPPINGS = # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = NO @@ -1663,7 +1707,7 @@ LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex -# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1681,9 +1725,12 @@ COMPACT_LATEX = NO PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. To get the times font for -# instance you can specify -# EXTRA_PACKAGES=times +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1698,9 +1745,9 @@ EXTRA_PACKAGES = # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, # $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string, -# for the replacement values of the other commands the user is refered to -# HTML_HEADER. +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = @@ -1716,6 +1763,17 @@ LATEX_HEADER = LATEX_FOOTER = +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output # directory. Note that the files will be copied as-is; there are no commands or @@ -1734,7 +1792,7 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES to get a +# the PDF file directly from the LaTeX files. Set this option to YES, to get a # higher quality PDF documentation. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1775,11 +1833,19 @@ LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. @@ -1794,7 +1860,7 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1831,11 +1897,21 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for # classes and files. # The default value is: NO. @@ -1879,7 +1955,7 @@ MAN_LINKS = NO # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. @@ -1893,7 +1969,7 @@ GENERATE_XML = NO XML_OUTPUT = xml -# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. @@ -1906,7 +1982,7 @@ XML_PROGRAMLISTING = YES # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. @@ -1920,7 +1996,7 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the # program listings (including syntax highlighting and cross-referencing # information) to the DOCBOOK output. Note that enabling this will significantly # increase the size of the DOCBOOK output. @@ -1933,10 +2009,10 @@ DOCBOOK_PROGRAMLISTING = NO # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen -# Definitions (see http://autogen.sf.net) file that captures the structure of -# the code including all documentation. Note that this feature is still -# experimental and incomplete at the moment. +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sf.net) file that captures the +# structure of the code including all documentation. Note that this feature is +# still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -1945,7 +2021,7 @@ GENERATE_AUTOGEN_DEF = NO # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. @@ -1953,7 +2029,7 @@ GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. @@ -1961,9 +2037,9 @@ GENERATE_PERLMOD = NO PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely # formatted so it can be parsed by a human reader. This is useful if you want to -# understand what is going on. On the other hand, if this tag is set to NO the +# understand what is going on. On the other hand, if this tag is set to NO, the # size of the Perl module output will be much smaller and Perl will parse it # just the same. # The default value is: YES. @@ -1983,14 +2059,14 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names -# in the source code. If set to NO only conditional compilation will be +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. # The default value is: NO. @@ -2006,7 +2082,7 @@ MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO -# If the SEARCH_INCLUDES tag is set to YES the includes files in the +# If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -2082,20 +2158,21 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES all external class will be listed in the -# class index. If set to NO only the inherited external classes will be listed. +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. # The default value is: NO. ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in -# the modules index. If set to NO, only the current project's groups will be +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. EXTERNAL_GROUPS = YES -# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in # the related pages index. If set to NO, only the current project's pages will # be listed. # The default value is: YES. @@ -2112,14 +2189,14 @@ PERL_PATH = /usr/bin/perl # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram # (in HTML and LaTeX) for classes with base or super classes. Setting the tag to # NO turns the diagrams off. Note that this option also works with HAVE_DOT # disabled, but it is recommended to install and use dot, since it yields more # powerful graphs. # The default value is: YES. -CLASS_DIAGRAMS = NO +CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see: @@ -2137,7 +2214,7 @@ MSCGEN_PATH = DIA_PATH = -# If set to YES, the inheritance and collaboration graphs will hide inheritance +# If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2210,7 +2287,7 @@ COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. @@ -2262,7 +2339,8 @@ INCLUDED_BY_GRAPH = YES # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2273,7 +2351,8 @@ CALL_GRAPH = YES # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2296,11 +2375,15 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, jpg, gif and svg. +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2348,10 +2431,14 @@ DIAFILE_DIRS = # PlantUML is not used or called during a preprocessing step. Doxygen will # generate a warning when it encounters a \startuml command in this case and # will not generate output for the diagram. -# This tag requires that the tag HAVE_DOT is set to YES. PLANTUML_JAR_PATH = +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized @@ -2388,7 +2475,7 @@ MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. @@ -2405,7 +2492,7 @@ DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot # files that are used to generate the various graphs. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. diff --git a/decoder/docs/prog_guide/cs_trace_hw.jpg b/decoder/docs/prog_guide/cs_trace_hw.jpg new file mode 100644 index 0000000000000000000000000000000000000000..af88f51455f442059469fe69f46480ade38fbc5a GIT binary patch literal 78065 zcmcG#c~lek|2G;%ML-OTfPfGc6%{cmTML-cq9P*39R(p@Yn3HpY6XchWE5l}i!$Jg(RFa3jd?cNi-H)P+DqsNYipEwzD=9{zUVq)XsFC`{jPG(=ZntmfAGb=kM z_lG+_-o01w)6a#EN*(lgS{S5l|>_6?AM>6>zUH0D^_J3)YHPNp53l^9xF#V@p^X4ZLFB9tp z3s-GjWV7>->FJBMtG8WWyzHyn535?t+`JCMc3*$jWp2O5TfG+fr)mFt%l@A=EbV`( zW&fjL|3|wNq$MWvh`}?lCIKWIU;ikV^#A@Pbdb-HR1ZZYsNM%M$0$9*&3Iq*#?Bc)pt zXJ;k*&c0{9B~q!i*f>)HlANhK3Jwq<6ujC&uN}!rb6mIcGgP zw9TJ zd!Aq7jeH7v7dBDx+o{%|50WgIBW2RaNPVu5%zh=7QkHz>Y!-nqiR{m!F_tgtJOyPwDffcousDI1blOGgXtktkcB)?Q;1XB#BxP?a_FZ&0d7 z#2vC}E*X{U>k-pifjbdqMAeGH;bFXEj$|=CMjgr8-ox@b9of@F*)!S^M31*yg~# zUT2m5`p6YuG&fiL~EZKXiYIcWL$}$;?Kxuq|~WEpJ++ zaMO1hU*Vgh>4vcydKSa6mi1oQtO$Ga_YGJ3T4VkZRhH!abjp=WYb(mQG*4_--~EvE zdPbMH|H1|7VMYX!A5Dj#T zvc!9_TUHBhf*yH@R_hP4meDV+`61p(Hsw%(xuKOk*qPp`!vO(dCOhDv`c~GBmxCeC zPN8?y;kilmT}k=o5PF$DnWfgDg_tAk+hp)&>y;VcDGmJ3G z@jI{e%e>tKSxU7 zTE)+i>dM+W#210=z#wF`PSQYanWBYUcv9H04?Sb>iNiNFp*vySp3aK1+ygIrE2g(I zG>fWMm>fj7x4@9+A^M+c=Sb>npe+!oueyw9r40&LVB<1*}4G$i!H9nrAZDuMhMCb0)cNT{>#)Th7W0D_% z$5Dftn|n`wJo6AN3eD4)ir-8JRCpcd>A5|hBRfP(;-Cl92ThwPa3xW*pE^l(d}4wy z-siPko}mc+{W6GxH%H3nZ0Jk;+2NzcAu~xJZ4~xSw3AI;Q1u) zSzXa-X#0zcLu~%;^us9?ZvRaC57cx@jXn?Yi|!LTqq=6=7e=1&OExR0NTN`-DDxbFlSQqP=*G;~w+)~7>oX?ulwf}G>(3cEIH zJl%G^o(>~xN18>p?AxHTT9gl%33h5`UN=|Wn+E#l{YZT)ra(e>{?(EK^SiK_WiKJMwx`fKP;l`QWbPHO7YfnHD8N_s-fSJ~_| z#XEUaPZEzz_tR5y1>0Xp%i?~B7djxvc@^yMa9v0@wX)_JE3v{~Gx&j`7}Q&!aaz&9 z9ZigwKET&Aj5nvDysLAh8%3?kB}x2j(Nb^5LKILJSFl(2c+Zit8=^~Low`0-aPqW< zk)i*KvYfrBL+p!FguJ$^OS8@}wd4gkra3ha$X(+epGw5aY8|$0n{G#E%^b;*qZFFK zZ~HR2PvRW5Vrz{*dYkdVqJZ_A&Iuclk3V!N<}Ni1k(M3W{r}G^{4J$M);rhXaK}WVU z(a0~6BdB}(9LZL2NXuAJ`E$~l=cm|!TGT4a@89M+lsW#v)<10aAis5ZEkdJTb8&owo(&D`NOE?|F%bGexG50OR z`nJb4RSBW$IZ_VDN`0J{!f--AwHI{0dHy@;DEiq)G-YZCN4@l(NElW(M_S~`nTO`- zv(Y~FcHrpi9;(nMvR>k1tCn38+Og9#?H~GaR{Gd?V3dZMCtO~0kt}heTm#9P#vGwD zGuj_c=F&cO1_BSzBSGDPUg`wfy|6rU+S0#gZ>Iow5Lb7Vmh~vkA2driT{`A;;J#&T zW>MM6$Y7VI(!2KszWtqhZU@=yd0rx+nc=^RUO|&RNzWOacZ_QCXcxt_Pw=S~N5mJnE`a;fH$ObzGjQ_VzZ$PR@`{Mu8xC4;OSRmw&`zVQVYTlS z#qh5K_B214I*cW?!ZyQu8nqihPxW87?GP;%T`+7HVAlp0NPc0H#9%0TDW4M7A)qE2yIZ|ml2ABpi)XGd- z)GHXS&%)=UG6O@i39|DU6Rbu2#I6By;WEFLqg|C*H$N)dW%e^qQ6ulOYc8d zoQyvlc~UtZ5%uKZlen4sdn(GILk#=jIZ{gy^o`}V2}b%cl%MqYB+A@@mS{XfC0C_l zMxK9gntQs6dqW3aM6!Z91<*6Ljl6}n9+8X57Gn_~%e4GV+!0yCM@+@;V%nyvK7kkW zDN2{zyBgh8Fg0ZGWV}^BeT3#c-yw9bhyR=mzgi$B=Ut`!tj-78gL*92}ILY+n=PN_rpe^*hWx)|QrStGHR#X!U^ zDLK;$*@LM-X3{i#OfGr*)o(p58~{1Wp`d5W;{BWs)FWPP^*9->6g@!$)Y8l(3#b|u zNrf95yRfyYPsyckH2G&jvQBhA7JT4~E+_{scbr7BvL2z=D<%p@t_v>4!Q!ZH!78{+ zOTIP5{8n&o4_!PwVmND8wH({3N@WDSMuOWZr1)7*8JymFFrKP`avN&8*+FXjjs|dy z7#g)ojBuYCF>FMCpquV~2!{05dTAMxr{g^9#nim81a%0w8wqX$79=_|^1>5T$7)eZ zYB}~b4CD(P#uQL{oWL|CNh>pDm$!Bf)>W&AqPzcNGBNA%#~kUVsE*YFU8C5j(NrY< zlVsSc=W7(G1Y_l#wZm+;_8HL<&`Vw6CZ*ZI3+x+ox4a?C@o2{$pMbY#Bx7sz4Yw-J z_!h)iX1tyJ%JNd!-5xQoZe~)h;W@C+G|#eb3Xa5S$PP@JD_r+eU?`ld4Np%DB|Ee0 zoA3RzBY48Pw&6_(Bz{Y7EuM$+GysV)M)T;zm!l&Qu#WpV%1z?xSS%Ia;n||8J}b#D zV)-{G0o4QLt99j1LZ6TE?AP|5E@YVVGtQsqxPnI-<@iPgI&!4HhG)uT%o9*e)7BvF zQx-je;MXCpkSNF>@r8yKS$MctO^2T84cbzRID{^?J&pyA{I1dHS<OZF5})AFo%7GKJ=^kdHs}*?SliRCgSnNdxKy8?;f|19$YGZf)g{ zuBB$}2ET(?{DDKC`SvK2j^^;$S(0K4lv|Fi?85@ox+&OG7<1!uzU|3gP7zGK1+GH;T85p` z>ngH!9ESioH@)*bQ^e73T^Ek)_V-QS|7vYv|3}5y7Uv%)@0lGAC_334$$WbHb>@lh zO;n7xmMzWDx` z2cj5}!a_o!;EtSbpqknSr9+kkvZUOAZ15#)L#xt( z&_D(?)GXmf_L=r!D$3LXi{qW{@l`-R!|bys^(Y!t&USBOz&F~SjtzU%<|;Kx$@HI69w+hBJc77Tmn_ZlD6u)h@if`$^>R&cYD61I%tMj>MXXc^q0 zp6dY5Dao~*7n+1o)Ud7K;4G62RO!5{2@w^4#X~eYz)*gooB>d~j72exj z=~hhMe1bd0%UVTNt6|LHa$%}6N8tW^)L)j1FUO+c3-_PKJDn?f!5&c+NT@k9i_Y5A zBVnDjLn-^^{M9{+O?wjmmvQvo6YDogHa=bZ)4xfiH*1PXCO^reEdh6M=zG zk*Q^xi+FzzZ4hqn@FQ3)Nx#pdU5i#|ObzQe3$bswQ-GrnIfMEk2$mWNR1GLN;nL71 zo-L2E=rb@m3RQooGtebkW5tuydUgIi7Tz3;L`ul|{g?&%hN9qXi>H_~{*$HV&N^gG zLNyrwRzV8Qokp`9hXy(cAo4ar&&!b-BKI3uV&_dt<293k^?YZpraqf8`7`Q(`~cKo z-}KU!iGC)FDbrLk!L;K3X1HwDg9kv+xp$6K{ScQG4o(5KTz%codS#~XNs(b5rHy3; z40sVNY7G>JzIh66fVNRGfzfD672flg&_8Mt{WpsGJtJ-)$jI}lS8&`ZQwd&Eq9y06 zAw7xw%_G4DAa{5r^eRThXy1$XAa@R;hw*QKB@hAU<=81QQzvkS`IKbp(HxWuP%x zH3T(LC}IXV%LmTQktP>Fc7h#qq;F)+qMNskW!iNQ;Zp2Z&hk0ZLpDchp#O%BC0A=M zX;s5z#`|PF*sqL3{|(jF$m+1Iuinm)-jlZfkc<3+d$6zLZ+O=sM>hk#JuFBa`uF$P z);ZEwu#v0h-9@@}F_IB!OB3DTPnrC?LfC_RK`0e*yae>j*`hhpOY8sgbZ9~7xuBm4 zTEgbG3%AJ=c^2%Tmf59z4bP5U6fLUjuAwevv&6Hh?G=^GhW@fEYkUhY-+3H&uOK3` z1WMX;y#Mm$Pp9ascRO$S^wWQ}IiSuEE`1KB8vl*2!yI}!rkpO{%S!t|2r~G;dB%M*A{(%~OE4j9X*H6m;9o33e>7}cWNS7Od;!p;N>de@;@eDw6=uFvEQ8)w6m z|4OncdjFE)2tH6KZ|1syW+=h?(p(p#en@#M;KXqPQ;cO`jE1oor1zhH)E&4}1v&DT z;JP&1ve|=GQTW#wvIRZhGEH(DC^da zOocy2=ScheaVIog?COs0E|25bA!LbYY20jalVLN)>|2*PONiXdIURsw<;uDmv_$m< zwWgyeb#GW)*+j9OsMwNnG!0$slQ&0t9ZPzYi}}xy(q4cE(d=S7UyREfxdc9JWM~a{{fCUV!`2!3uCc?0R35!n0z><2}c!`Z)IN6xEaRv+97i+t=I) zEzpc)`xM*?i#!1LwOuovm}!$OM7bIVG1rb5d!;o2b&A>=Dt;uZq3mJD+E=|kbTc$- zzo@2fI%;1Sc;#?@n6al>mIWb=15+Ti2IEP_XyD@ z(_Iy0v@Vd8_kuh6;&9f$jH>ug27Z zQnY%%#(sqJc@oME$}2i%!TaBy z8&_(Uimo6dVZbusdeGfiA#l+smknU6U)RDZ;8(_H_!iI;B`BPkFkiJnxN(>fS8Rh` zBDX?hcEABN#XX;2%d(9-7j-5A&8K94=uO%lnvLenkrw3i2tA0==OKRWkc;^cMkho< zZu=3GDF&7}e+A3Zr+31Q?Tp29r1K&Yy4ix8tq_(!VKepbV5x-VqGadulM)Whk(!0> zeUpJZuUtf&bbEI<5`6PClw0hBRvXT9eivESvRo$$XIiPmPnrSW0NFsb?1nDMczBM~ z>be|FAh%Hm%T)L7Xt*YE^5H%A&z?qxJ5+FA0b&-Rw-KsF-p*UVc~3CFKUEb0`upfA!zH}a=~k9ucLbAS&e_TyrjWrz2u1^5mm5+yJ&QHv7le*%=U_wW7Nf_jo<}hD(*#fT2VH> zdV)qZmM8GPMl$46&s@`=F?r_r8dTbjJHpbbi*TrRrX|6m#q56dN?5s+sAm$4Kv^NE};dV9M9Jc|` z^(C64mMz1+K!BTI=n4WK<+z?B*ku%;rKAUv)h7d^)JIrq;|^>mx*NHkm~gn8Vmr!= zqZz*xZJAbHs~%TY$)`l7X*4fR7j8Zhxu}+hZpfM( zf~)uTfE$<^S?<+#-ji{EN+zo)jJB?(t{%xOFMabYE}|^Ea5TUluRtolujhR&pBAM# zAebk?%s;0d4{hA;EO7vAPxsuk-I;tPah~3E3NUzax+%`0ILdOq7;r&@)$XnI9A1?} zE{(+g$3w{BIGg~F_lau2W7@Z|^fLC+R%$M9>1Vn*TNVPpYlJGsTvy9$WGk?M!arKb z*_floBQL-?C9==~vy~r5FBDt$3CZBOZh@bck_pu)35SlWB=ZB4VDT|DT{XcVgGbTe z=b%$R&6(iq{S-UQl8+W^L*WmP3oE(WA*z&P+gE}tx*t1Jcx*W93V+mJ*)mLqdHJ+; z_z+N6$JfvozR?m=3+kcQ!ZA0dSx)RM+&slV$7K#YO97<}e@l7A&91CrnBi}Q^LnT> z3Y)v9(}!xs9NtuYy*7Ux*>U3PaCu3*=O`b+(o?D7`e9i~X~>1vULfiijAVSn_` z$78NrV6K>|psL=LXe{{YA=--YRwrka*A~474$gff>c@7z0Kcs6WqI!fy*|k%Lz1;X z7EtFPR%R4d@~=EXg9w)?L`$KJ70-j^Q`Lw_KLBS|X=DqrqiBfzYc0!`FHIpYW#3T& z+1}-hoUXBkB{VCzPnU+Tm1<QPo8*q4SEEYN)?q{WF25P&Z|w%3}hhg5{Kcn@P3+qfgb9F z&MD29evE~JufSsuv54N_jP{3`W^z30Ow{2<$|8XoJlJv=W6_rpJ=%yi8JxkxVY{%c zPX6Pa_^*^|ikLD`zP~dp58Tm*i!W)Jl+5F_EwAMZhZJba!QQeS&*{|T&ao^8S;Bxr z0jqJEPj}#HwMan@g3moG!;Yg55m#UZnyebh4)jBM#IrGNz~C7zeJYucy0rkyF;hg| zJi}7o588*e`L$MDB1Ea>pcledmwpj%IfoDtE{#0k| zl(E25;0`OJjo)+3*_~|V4&>u?#7|qFgVXT0&_dysHZ6~YHsPDmJE5IP2{A{|z9;M> zEzwJRK?`I7wg(9oGqShQwS2S4iBR`6pISyfz6F+Jwn6$EKYVI;-QEqxs2Mp>O~K@D zI7?H_ynMNM+e`U3#&VEpup@rLCW+^Y<{j*=_(9h)Tl@_EK-3IsTNv{Y^C>mZPos2+ z&oms){Lud``4PnAzt^@=YChD*Tq*&hI&l~F)fshWBP=C^!BSXgkVaJUyOVgCRb#XFg{2SB24!S-?dxu(Vd-DLIllq(w5FJCL7T`Ch((ZU ziLz!}Jk9PUs)YwNx(tl00p_!3Bv*B5fdMMM33{gGs;(7S$jnC!VJJh*Sj?_W2xt>- z!4)vEm)`el?;qZb+^ok}Bb1vjUesC!Ftm{B2cBb6Lh>HtW6?Q&j#EKSsqq1HPL^j= z-_Bk9JNu4=^Cg-97wR;d%QO?zMVJp7fS7715^6TXPH;)}4D*B4v`xD!(22t*Js+bl z3zza7vNn4sFw~HkvDjD)U4RtHZGZ*t5-+rTiMl*uJF7>U0d$>C!gWR?+h(clvTWKm zGvfqP#9z7#L#+V|z(B3ynW&&`6dcfbFZ*U+J6XnfhTK^h~8S$ASPI zSjwH91TZcG!_XqleWseh-E*sew?w!en-AZ+t4Z}`*#Xy+VyS9a|MRpj*I+sp0Zb;T zWdr`w_6FLm!r(R~L0EeOPpWyO(8sai{xg}P_#q4Z)4s$Cx0di>pZ?EbKuF)`)YUB0 zlfz6LK$3G4rVURCUyo~z4oaRq8=e4)APXptdvo?fLD_^to-6RUqDtLGplBjsV)dpR zyHXU{l_bkk!r{CeXue>#wu79l;7!>*V=IsD6$CtRkk`_xdSMXj9L3YTcZxGltz1-{B_N!M2!i-jvr z4UHAjb42kz=zYXl4c#KbD)GtPoRw&gS~*#N>G{BD{md*YBWZbU6JrldG998gL#q43 zgfM~xXju8vfMR>#b~QDZKv04h#H0-{!*;6}IsPNpKmLILlVgT89NFxq!dv%MWQCV{ zVL_fCSoH;Pj%ADHwa!EXJ%T6|xf=TxIi6PzQhWK+o`7Vgm*z-FmEK9L3~0VLkv#A> zl_t=1QhXWOVptEdUmqaM^=W=CudcqC zmqlrbV`Z;)Hi_%+bD7HE-EC&rWdB@bv(i-tN{O?TK!x=d41H^N1XcSs)eG zN#AJWa3W>n0viq?^?4ix`laD4xL05-0+{?zaBU)VG>^|9<}P=#`6f_oi-aI;ke=E&ul%UW|p%-spjr%;nr zSl6!8l8H!yjeuSnPjMkOZxfBEjdfF0Gm;T#N;sg3^k!gbBF`TD7ol6XolM%EvJ08H zA>61$Sz$0Yqei!o-Ls>iUf9R-6&xanQBU!5z(O$ZG(y+!L`#tZtwR>a726HhXW&b~ zm1u#M>hK<=5cPxR!R|SFlIFmq)F?9oR}rYYBD8sSH!;sSeyCRe6TyO5HtY*Z>G4D8 zGG@fOJm1%U;AX_zg?RUHAi6UK8E>Tw)Q1x8%^c}0^!}1Q1I>dU5p2?M3Qb2MPZ`Sy zqnNscUENHCo&<-=Kt^3R?sQ7_lg=#6zXrn}E(vNu&)4Ic4V+u94xY;VdR zuCIt{@h!jdxAP->m{&t@;a*qP&`Pu-d&~tT$k7c^Rn}Ck;RqVs=FhuaT%zs;CRC#HeSF^ai@Ik{*rHR=n=2$!G4BLp?P-@>oM%(RX!kFZF{*j7`9K zh9#PJtPZAJ$5)`TFvMZ%E#jc}M^n`zGt9M55T$Lh?@l{<0 zzezLY*V@mq=@wYBH1rJ7`$uw#>-JiNoPWVas!k6su|pRa zsbM*f!wPof=T{;-K=pKvWW~pJ*Q?3bps)FgBT}Wq^sf&JnxT64YaG*fd>yQtyjU@= z@UGv2#S`;~ys(mwh&8}1j;r`-@bgG)ExKFv_6V_DQVFq{CmmkfRb$DN=v5~A%G^)9 z42qqQ0>;C>w-F0QmjLIt9TG;`RQ=o74<@Hguv| z(1jgRQ>{m5{jvpKl_!Ts6(4Rt89$zE|Lv^L|5AvVfx}QNxCCar zpB_C;ynRW%U5Bj_k!gLZ?NrtP_$7d?9Jc-BJ*s9+x�*^l$ji|DRv-!Tj9y|L2z= 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 literal 0 HcmV?d00001 diff --git a/decoder/docs/prog_guide/decode_data_path_resp.jpg b/decoder/docs/prog_guide/decode_data_path_resp.jpg new file mode 100644 index 0000000000000000000000000000000000000000..eb7edb9e40e3fe404e03dc4dac3c16e913c277d2 GIT binary patch literal 29840 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 literal 0 HcmV?d00001 diff --git a/decoder/docs/prog_guide/lib_usage.jpg b/decoder/docs/prog_guide/lib_usage.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6baaa12344b3bab0ec3228f963ff497ae844afb8 GIT binary patch literal 36058 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 literal 0 HcmV?d00001 diff --git a/decoder/docs/prog_guide/prog_guide_generic_pkts.md b/decoder/docs/prog_guide/prog_guide_generic_pkts.md new file mode 100644 index 00000000000..9f69aacad44 --- /dev/null +++ b/decoder/docs/prog_guide/prog_guide_generic_pkts.md @@ -0,0 +1,400 @@ +OpenCSD Library - Generic Trace Packet Descriptions {#generic_pkts} +=================================================== + +@brief Interpretation of the Generic Trace output packets. + +Generic Trace Packets - Collection. +----------------------------------- + +### Packet interface ### + +The generic trace packets are the fully decoded output from the trace library. + +These are delivered to the client application in the form of a callback function. Packets from all trace sources +will use the same single callback function, with the CoreSight Trace ID provided to identify the source. + +The callback is in the form of an interface class ITrcGenElemIn, which has a single function: + +~~~{.cpp} +virtual ocsd_datapath_resp_t TraceElemIn( const ocsd_trc_index_t index_sop, + const uint8_t trc_chan_id, + const OcsdTraceElement &elem + ) = 0; +~~~ + +The client program will create derived class providing this interface to collect trace packets from the library. + +The parameters describe the output packet and source channel: +|Parameter | Description | +|:--------------------------------|:------------------------------------------------------------------------| +| `ocsd_trc_index_t index_sop` | Index of the first byte of the trace packet that generated this output. | +| `uint8_t trc_chan_id` | The source CoreSight Trace ID. | +| `OcsdTraceElement &elem` | The packet class - wraps the `ocsd_generic_trace_elem` structure. | + +_Note_ : `index_sop` may be the same for multiple output packets. This is due to an one byte atom packet which +can represent multiple atoms and hence multiple ranges. + +The C-API provides a similarly specified callback function definition, with an additional opaque `void *` pointer +that the client application may use. + +~~~{.c} +/** function pointer type for decoder outputs. all protocols, generic data element input */ +typedef ocsd_datapath_resp_t (* FnTraceElemIn)( const void *p_context, + const ocsd_trc_index_t index_sop, + const uint8_t trc_chan_id, + const ocsd_generic_trace_elem *elem); +~~~ + +### The Packet Structure ### + +~~~{.c} +typedef struct _ocsd_generic_trace_elem { + ocsd_gen_trc_elem_t elem_type; /* Element type - remaining data interpreted according to this value */ + ocsd_isa isa; /* instruction set for executed instructions */ + ocsd_vaddr_t st_addr; /* start address for instruction execution range / inaccessible code address / data address */ + ocsd_vaddr_t en_addr; /* end address (exclusive) for instruction execution range. */ + ocsd_pe_context context; /* PE Context */ + uint64_t timestamp; /* timestamp value for TS element type */ + uint32_t cycle_count; /* cycle count for explicit cycle count element, or count for element with associated cycle count */ + ocsd_instr_type last_i_type; /* Last instruction type if instruction execution range */ + ocsd_instr_subtype last_i_subtype; /* sub type for last instruction in range */ + + //! per element flags + union { + struct { + uint32_t last_instr_exec:1; /* 1 if last instruction in range was executed; */ + uint32_t last_instr_sz:3; /* size of last instruction in bytes (2/4) */ + uint32_t has_cc:1; /* 1 if this packet has a valid cycle count included (e.g. cycle count included as part of instruction range packet, always 1 for pure cycle count packet.*/ + uint32_t cpu_freq_change:1; /* 1 if this packet indicates a change in CPU frequency */ + uint32_t excep_ret_addr:1; /* 1 if en_addr is the preferred exception return address on exception packet type */ + uint32_t excep_data_marker:1; /* 1 if the exception entry packet is a data push marker only, with no address information (used typically in v7M trace for marking data pushed onto stack) */ + uint32_t extended_data:1; /* 1 if the packet extended data pointer is valid. Allows packet extensions for custom decoders, or additional data payloads for data trace. */ + uint32_t has_ts:1; /* 1 if the packet has an associated timestamp - e.g. SW/STM trace TS+Payload as a single packet */ + uint32_t last_instr_cond:1; /* 1 if the last instruction was conditional */ + uint32_t excep_ret_addr_br_tgt:1; /* 1 if exception return address (en_addr) is also the target of a taken branch addr from the previous range. */ + }; + uint32_t flag_bits; + }; + + //! packet specific payloads + union { + uint32_t exception_number; /* exception number for exception type packets */ + trace_event_t trace_event; /* Trace event - trigger etc */ + trace_on_reason_t trace_on_reason; /* reason for the trace on packet */ + ocsd_swt_info_t sw_trace_info; /* software trace packet info */ + uint32_t num_instr_range; /* number of instructions covered by range packet (for T32 this cannot be calculated from en-st/i_size) */ + + }; + + const void *ptr_extended_data; /* pointer to extended data buffer (data trace, sw trace payload) / custom structure */ + +} ocsd_generic_trace_elem; +~~~ + +The packet structure contains multiple fields and flag bits. The validity of any of these fields or flags +is dependent on the `elem_type` member. The client program must not assume that field values will persist +between packets, and must process all valid data during the callback function. + +The packet reference guide below defines the fields valid for each packet type. + +-------------------------------------------------------------------------------------------------- + +Generic Trace Packets - Packet Reference. +----------------------------------------- + +This section contains reference descriptions of each of the generic trace packets types define as part of the +`ocsd_gen_trc_elem_t` enum value that appears as the first `elem_type` field in the packet structure. + +The descriptions will include information on which fields in the packets are always valid, optional and any protocol specific information. + +The tags used in the reference are:- +- __packet fields valid__ : fields that are always valid and filled for this packet type. +- __packet fields optional__ : fields that _may_ be filled for this packet type. + The form `flag -> field` indicates a flag that may be set and the value that is valid if the flag is true +- __protocol specific__ : indicates type or fields may be source protocol specific. + +_Note_: while most of the packets are not protocol specific, there are some protocol differences that mean +certain types and fields will differ slightly across protocols. These differences are highlighted in the +reference. + +### OCSD_GEN_TRC_ELEM_NO_SYNC ### +__packet fields valid__: None + +Element output before the decoder has synchronised with the input stream, or synchronisation is lost. + +### OCSD_GEN_TRC_ELEM_INSTR_RANGE ### +__packet fields valid__: `isa, st_addr, en_addr, last_i_type, last_i_subtype, last_instr_exec, last_instr_sz, num_instr_range, last_instr_cond` + +__packet fields optional__: `has_cc -> cycle_count,` + +__protocol specific__ : ETMv3, PTM + +This should be the most common packet output for full trace decode. Represents a range of instructions of +a single `isa`, executed by the PE. Instruction byte range is from `st_addr` (inclusive) to `en_addr` (exclusive). +The total number of instructions executed for the range is given in `num_instr_range`. + +Information on the last instruction in the range is provided. `last_i_type` shows if the last instruction +was a branch or otherwise - which combined with `last_instr_exec` determines if the branch was taken. +The last instruction size in bytes is given, to allow clients to quickly determine the address of the last +instruction by subtraction from `en_addr`. This value can be 2 or 4 bytes in the T32 instruction set. + +__ETMv3, PTM__ : These protocols can output a cycle count directly as part of the trace packet that generates +the trace range. In this case `has_cc` will be 1 and `cycle_count` will be valid. + + +### OCSD_GEN_TRC_ELEM_ADDR_NACC ### +__packet fields valid__: `st_addr` + +Trace decoder found address in trace that cannot be accessed in the mapped memory images. +`st_addr` is the address that cannot be found. + +Decoder will wait for new address to appear in trace before attempting to restart decoding. + + +### OCSD_GEN_TRC_ELEM_UNKNOWN ### +__packet fields valid__: None + +Decoder saw invalid packet for protocol being processed. Likely incorrect protocol settings, or corrupted +trace data. + +### OCSD_GEN_TRC_ELEM_TRACE_ON ### +__packet fields valid__: trace_on_reason + +__packet fields optional__: `has_cc -> cycle_count,` + +__protocol specific__ : ETMv3, PTM + +Notification that trace has started / is synced after a discontinuity or at start of trace decode. + +__ETMv3, PTM__ : These protocols can output a cycle count directly as part of the trace packet that generates +the trace on indicator. In this case `has_cc` will be 1 and `cycle_count` will be valid. + + +### OCSD_GEN_TRC_ELEM_EO_TRACE ### +__packet fields valid__: None + +Marker for end of trace data. Sent once for each CoreSight ID channel. + +### OCSD_GEN_TRC_ELEM_PE_CONTEXT ### +__packet fields valid__: context + +__packet fields optional__: `has_cc -> cycle_count,` + +__protocol specific__ : ETMv3, PTM + +This packet indicates an update to the PE context - which may be the initial context in a trace stream, or a +change since the trace started. + +The context is contained in a `ocsd_pe_context` structure. + +~~~{.c} +typedef struct _ocsd_pe_context { + ocsd_sec_level security_level; /* security state */ + ocsd_ex_level exception_level; /* exception level */ + uint32_t context_id; /* context ID */ + uint32_t vmid; /* VMID */ + struct { + uint32_t bits64:1; /* 1 if 64 bit operation */ + uint32_t ctxt_id_valid:1; /* 1 if context ID value valid */ + uint32_t vmid_valid:1; /* 1 if VMID value is valid */ + uint32_t el_valid:1; /* 1 if EL value is valid (ETMv4 traces current EL, other protocols do not) */ + }; +} ocsd_pe_context; +~~~ + +__ETMv3, PTM__ : These protocols can output a cycle count directly as part of the trace packet that generates +the PE context. In this case `has_cc` will be 1 and `cycle_count` will be valid. + +__ETMv3__ : From ETM 3.5 onwards, exception_level can be set to `ocsd_EL2` when tracing through hypervisor code. +On all other occasions this will be set to `ocsd_EL_unknown`. + + +### OCSD_GEN_TRC_ELEM_ADDR_UNKNOWN ### +__packet fields optional__: `has_cc -> cycle_count,` + +__protocol specific__: ETMv3 + +This packet will only be seen when decoding an ETMv3 protocol source. This indicates that the decoder +is waiting for a valid address in order to process trace correctly. + +The packet can have a cycle count associated with it which the client must account for when tracking cycles used. +The packet will be sent once when unknown address occurs. Further `OCSD_GEN_TRC_ELEM_CYCLE_COUNT` packets may follow + before the decode receives a valid address to continue decode. + + +### OCSD_GEN_TRC_ELEM_EXCEPTION ### +__packet fields valid__: `exception_number` + +__packet fields optional__: `has_cc -> cycle_count, excep_ret_addr -> en_addr, excep_data_marker, excep_ret_addr_br_tgt` + +__protocol specific__: ETMv4, ETMv3, PTM + +All protocols will include the exception number in the packet. + +__ETMv4__ : This protocol may provide the preferred return address for the exception - this is the address of +the instruction that could be executed on exception return. This address appears in `en_addr` if `excep_ret_addr` = 1. + +Additionally, this address could also represent the target address of a branch, if the exception occured at the branch target, before any further instructions were execute. If htis is the case then the excep_ret_addr_br_tgt flag will be set. This makes explicit what was previously only implied by teh packet ordered. This information could be used for clients such as perf that branch source/target address pairs. + +__ETMv3__ : This can set the `excep_data_marker` flag. This indicates that the exception packet is a marker +to indicate exception entry in a 7M profile core, for the purposes of tracking data. This will __not__ provide +an exception number in this case. + +__PTM__ : Can have an associated cycle count (`has_cc == 1`), and may provide preferred return address in `en_addr` +if `excep_ret_addr` = 1. + +### OCSD_GEN_TRC_ELEM_EXCEPTION_RET ### +__packet fields valid__: None + +Marker that a preceding branch was an exception return. + +### OCSD_GEN_TRC_ELEM_TIMESTAMP ### +__packet fields valid__: `timestamp` + +__packet fields optional__: `has_cc -> cycle_count,` + +__protocol specific__: ETMv4, PTM + +The timestamp packet explicitly provides a timestamp value for the trace stream ID in the callback interface. + +__PTM__ : This can have an associated cycle count (`has_cc == 1`). For this protocol, the cycle count __is__ part +of the cumulative cycle count for the trace session. + +__ETMv4__ : This can have an associated cycle count (`has_cc == 1`). For this protocl, the cycle coun represents +the number of cycles between the previous cycle count packet and this timestamp packet, but __is not__ part of +the cumulative cycle count for the trace session. + + +### OCSD_GEN_TRC_ELEM_CYCLE_COUNT ### +__packet fields valid__: `has_cc -> cycle_count` + +Packet contains a cycle count value. A cycle count value represents the number of cycles passed since the +last cycle count value seen. The cycle count value may be associated with a specific packet or instruction +range preceding the cycle count packet. + +Cycle count packets may be added together to build a cumulative count for the trace session. + +### OCSD_GEN_TRC_ELEM_EVENT ### +__packet fields valid__: `trace_event` + +This is a hardware event injected into the trace by the ETM/PTM hardware resource programming. See the +relevent trace hardware reference manuals for the programming of these events. + +The `trace_event` is a `trace_event_t` structure that can have an event type - and an event number. + +~~~{.c} +typedef struct _trace_event_t { + uint16_t ev_type; /* event type - unknown (0) trigger (1), numbered event (2)*/ + uint16_t ev_number; /* event number if numbered event type */ +} trace_event_t; +~~~ + +The event types depend on the trace hardware:- + +__ETMv4__ : produces numbered events. The event number is a bitfield of up to four events that occurred. +Events 0-3 -> bits 0-3. The bitfield allows a single packet to represent multiple different events occurring. + +_Note_: The ETMv4 specification has further information on timing of events and event packets. Event 0 +is also considered a trigger event in ETMv4 hardware, but is not explicitly represented as such in the OCSD protocol. + +__PTM__, __ETMv3__ : produce trigger events. Event number always set to 0. + + +### OCSD_GEN_TRC_ELEM_SWTRACE ### +__packet fields valid__: `sw_trace_info` + +__packet fields optional__: `has_ts -> timestamp`, ` extended_data -> ptr_extended_data` + +The Software trace packet always has a filled in `sw_trace_info` field to describe the current master and channel ID, +plus the packet type and size of any payload data. + +SW trace packets that have a payload will use the extended_data flag and pointer to deliver this data. + +SW trace packets that include timestamp information will us the `has_ts` flag and fill in the timestamp value. + + +### OCSD_GEN_TRC_ELEM_CUSTOM ### +__packet fields optional__: `extended_data -> ptr_extended_data`,_any others_ + +Custom protocol decoders can use this packet type to provide protocol specific information. + +Standard fields may be used for similar purposes as defined above, or the extended data pointer can reference +other data. + +-------------------------------------------------------------------------------------------------- + +Generic Trace Packets - Notes on interpretation. +------------------------------------------------ + +The interpretation of the trace output should always be done with reference to the underlying protocol +specifications. + +While the output packets are in general protocol agnostic, there are some inevitable +differences related to the underlying protocol that stem from the development of the trace hardware over time. + +### OCSD ranges and Trace Atom Packets ### +The most common raw trace packet in all the protocols is the Atom packet, and this packet is the basis for most of +the `OCSD_GEN_TRC_ELEM_INSTR_RANGE` packets output from the library. A trace range will be output for each atom +in the raw trace stream - the `last_instr_exec` flag taking the value of the Atom - 1 for E, 0 for N. + +`OCSD_GEN_TRC_ELEM_INSTR_RANGE` packets can also be generated for non-atom packets, where flow changes - e.g. +exceptions. + + +### Multi feature OCSD output packets ### +Where a raw trace packet contains additional information on top of the basic packet data, then this additional +information will be added to the OCSD output packet and flagged accordingly (in the `flag_bits` union in the +packet structure). + +Typically this will be atom+cycle count packets in ETMv3 and PTM protocols. For efficiency and to retain +the coupling between the information an `OCSD_GEN_TRC_ELEM_INSTR_RANGE` packet will be output in this case +with a `has_cc` flag set and the `cycle_count` value filled. + +ETMv3 and PTM can add a cycle count to a number of packets, or explicitly emit a cycle count only packet. By +contrast ETMv4 only emits cycle count only packets. + +Clients processing the library output must be aware of these optional additions to the base packet. The +OCSD packet descriptions above outline where the additional information can occur. + +### Cycle counts ### + +Cycle counts are cumulative, and represent cycles since the last cycle count output. +Explicit cycle count packets are associated with the previous range event, otherwise where a +packet includes a cycle count as additional information, then the count is associated with that +specific packet - which will often be a range packet. + +The only exception to this is where the underlying protocol is ETMv4, and a cycle count is included +in a timestamp packet. Here the cycle count represents that number of cycles since the last cycle count +packet that occurred before the timestamp packet was emitted. This cycle count is not part of the cumulative +count. See the ETMv4 specification for further details. + + +### Correlation - timestamps and cycle counts ### + +Different trace streams can be correlated using either timestamps, or timestamps plus cycle counts. + +Both timestamps and cycle counts are enabled by programming ETM control registers, and it is also possible +to control the frequency that timestamps appear, or the threshold at which cycle count packets are emitted by +additional programming. + +The output of timestamps and cycle counts increases the amount of trace generated, very significantly when cycle +counts are present, so the choice of generating these elements needs to be balanced against the requirement +for their use. + +Decent correlation can be gained by the use of timestamps alone - especially if the source is programmed to +produce them more frequently than the default timestamp events. More precise correllation can be performed if +the 'gaps' between timestamps can be resolved using cycle counts. + +Correlation is performed by identifying the same/close timestamp values in two separate trace streams. Cycle counts +if present can then be used to resolve the correlation with additional accuracy. + + + + + + + + + + + diff --git a/decoder/docs/prog_guide/prog_guide_main.md b/decoder/docs/prog_guide/prog_guide_main.md new file mode 100644 index 00000000000..87afbf0225c --- /dev/null +++ b/decoder/docs/prog_guide/prog_guide_main.md @@ -0,0 +1,597 @@ +OpenCSD Library - Programmers Guide {#prog_guide} +=================================== + +@brief A guide to programming the OpenCSD library. + +Introduction and review of Coresight Hardware +--------------------------------------------- + +The OpenCSD trace decode library is designed to allow programmers to decode ARM CoreSight trace +data. This guide will describe the various stages of configuring and programming a decoder instance +for a given CoreSight system. + +The diagram below shows a typical Coresight trace hardware arrangement + +![Example CoreSight Trace Capture Hardware](cs_trace_hw.jpg) + +The design shown has four Cortex cores, each with an ETM, along with a system STM all of which generate trace into the +trace funnel. The output of the funnel is fed into a trace sink, which might be an ETB or ETR, saving the trace +which is multiplexed into CoreSight trace frames in the trace sink memory. The colours represent the sources +of trace data, each of which will be tagged with a CoreSight Trace ID. + +### CoreSight Trace ID ### +The CoreSight Trace ID - also referred to as the Trace Source Channel ID - is a unique 8 bit number programmed +into each trace source in a system (ETM,PTM,STM) which identifies the source to both the hardware components +downstream and the software trace decoders. This ID is used + +Overview of Configuration and Decode +------------------------------------ + +The OpenCSD library will take the trace data from the trace sink, and when correctly configured and programmed, will +demultiplex and decode each of the trace sources. + +The library supports ETMV3, PTM, ETMv4 and STM trace protocols. The decode occurs in three stages: +- __Demultiplex__ - the combined trace streams in CoreSight trace frame format are split into their constituent streams according to the CoreSight trace ID. +- __Packet Processing__ - the individual trace ID streams are resolved into discrete trace packets. +- __Packet Decode__ - the trace packets are interpreted to produce a decoded representation of instructions executed. + +There are input configuration requirements for each stage of the decode process - these allow the decode process to correctly +interpret the incoming byte stream. +- __Demultiplex__ - Input flags are set to indicate if the frames are 16 byte aligned or if the stream contains alignment +bytes between frames. +- __Packet Processing__ - The hardware configuration of the trace source must be provided. This consists of a sub-set of the +hardware register values for the source. Each protocol has differing requirements, represented by an input structure of the +register values. +- __Packet Decode__ - For ETM/PTM packet decode, this stage requires the memory images of the code executed in order +to determine the path through the code. These are provided either as memory dumps, or as links to binary code files. + +_Note_ : STM, being a largely software generated data trace, does not require memory images to recover the data written by the source +processors. + +The diagram below shows the basic stages of decode for the library when used in a client application: + +![Example Library Usage for Trace Decode](lib_usage.jpg) + +The DecodeTree object is a representation of the structure of the CoreSight hardware, but in reverse in that the data is pushed into the +tree, through the demultiplexor and then along the individual trace stream decode paths till the output decode packets are produced. + +These outpup packets are referred to as Generic Trace packets, and are at this stage protocol independent. They consist primarily of +PE context information and address ranges representing the instructions processed. + +### Decode Tree ### + +The DecodeTree is the principal wrapper for all the decoders the library supports. This provides a programming +API which allows the creation of protocol packet processors and decoders. + +The API allows the client application to configure the de-multiplexor, create and connect packet processors and +packet decoders to the trace data streams and collect the output generic decoded trace packets. The DecodeTree +provides a built in instruction decoder to allow correct trace decode, and an additional API through a memory +access handler to allow the client applications to provide the images of the traced code in file or memory dump +format. + +Once a DecodeTree is configured, then it can be re-used for multiple sets of captured trace data where the same +set of applications has been traced, or by changing only the supplied memory images, different traced applications +on the same hardware configuration. + +The process for programming a decode tree for a specific set of trace hardware is as follows;- +1. Create the decode tree and specify the de-multiplexor options. +2. For each trace protocol of interest, use the API to create a decoder, providing the hardware configuration, +including the CoreSight trace ID for that trace stream. Specify packet processing only, or full decode. Client +program must know the correct protocol to use for each trace stream. +3. Attach callback(s) to receive the decoded generic trace output (ITrcGenElemIn). +4. Provide the memory images if using full decode. + +The DecodeTree can now be used to process the trace data by pushing the captured trace data through the trace + data input API call (ITrcDataIn) and analyzing as required the resulting decoded trace (ITrcGenElemIn). + + The objects and connections used for a single trace stream are shown below. + + ![Decode Tree objects - single trace stream](dt_components.jpg) + + All these components can be created and used outside of a DecodeTree, but that is beyond the scope of this + guide and expected to be used for custom implementations only. + +Programming Examples - decoder configuration. +--------------------------------------------- + +The remainder of this programming guide will provide programming exceprts for each of the required stages +to get a working decode tree, capable of processing trace data. + +The guide will be based on an ETMv4 system, similar to the example above, using the C++ interface, but +equivalent calls from the C-API wrapper library will also be provided. + +The source code for the two test applications `trc_pkt_lister` and `c_api_pkt_print_test` may be used as +further programming guidance. + +### Create the decode tree ### + +The first step is to create the decode tree. Key choices here are the flags defining expected trace data +input format and de-mux operations. + +~~~{.cpp} + uint32_t formatterCfgFlags = OCSD_DFRMTR_FRAME_MEM_ALIGN; /* basic operational mode for on-chip captured trace */ + DecodeTree *pTree = DecodeTree::CreateDecodeTree(OCSD_TRC_SRC_FRAME_FORMATTED, formatterCfgFlags); +~~~ + +This creates a decode tree that is usable in the majority of cases - that is for trace captured in on chip +RAM via ETB or ETR. Additional flags are available if a TPIU is used that will indicate to the frame de-mux +that additional frame synchronisation data is present. + +In limited cases where the hardware has a single trace source, or only a single source is being used, then +it is possible to switch off the hardware frame formatter in the ETB/ETR/TPIU. In this case @ref OCSD_TRC_SRC_SINGLE + (from enum @ref ocsd_dcd_tree_src_t) may be defined as the first parameter to the function. + +C-API version of above code: +~~~{.c} + dcd_tree_handle_t dcdtree_handle = ocsd_create_dcd_tree(OCSD_TRC_SRC_FRAME_FORMATTED, OCSD_DFRMTR_FRAME_MEM_ALIGN); +~~~ + +### Error loggers and printers ### + +The library defines a standard error logging interface ITraceErrorLog which many of the key components can register +with to output errors. The process of registering the source means that errors can be tied to a particular component, +or CoreSight Trace ID. The library provides a standard error logger object - ocsdDefaultErrorLogger - which +keeps a copy of the last error logged, plus a copy of the last error logged for each data stream associated +with a CoreSight trace ID. + +The error logger can be attached to an output logger - ocsdMsgLogger - which can print text versions of the +error, or other error messages, out to screen or logging file. Errors can be filtered according to a severity rating, +defined by @ref ocsd_err_severity_t. + +The DecodeTree will use a default error logger from the library - with a message logger +that will output to `stderr`. Client applications can adjust the configuration of this error logger and +message logger, or provide their own configured error logger / message logger pair. + +The test program `trc_pkt_lister` provides a customised version of an `ocsdMsgLogger` / `ocsdDefaultErrorLogger` pair +to ensure that messages and errors are logged to the screen and a file of its choice. This logger is eventually +passed through to the decode tree. + +Code excerpts below (trc_pkt_lister.cpp): + +~~~{.cpp} + static ocsdMsgLogger logger; + static int logOpts = ocsdMsgLogger::OUT_STDOUT | ocsdMsgLogger::OUT_FILE; + static std::string logfileName = "trc_pkt_lister.ppl"; + + // ** other vars + + main() { + + // ** some init code + + logger.setLogOpts(logOpts); + logger.setLogFileName(logfileName.c_str()); + + + ocsdDefaultErrorLogger err_log; + err_log.initErrorLogger(OCSD_ERR_SEV_INFO); + err_log.setOutputLogger(&logger); + + // pass err_log reference into snapshot library code + SnapShotReader ss_reader; + ss_reader.setErrorLogger(&err_log); + + // ** rest of program + } +~~~ + +In the library code for the snapshot reader (ss_to_dcd_tree.cpp): + +~~~{.cpp} + bool CreateDcdTreeFromSnapShot::createDecodeTree() + { + // ** create a decode tree + + // use our error logger - don't use the tree default. + m_pDecodeTree->setAlternateErrorLogger(m_pErrLogInterface); + } + +~~~ + +__Note__: The Snapshot reader library is test code designed to allow the test application read trace snapshots +which are in the form defined by the open specification in `./decoder/docs/specs/ARM Trace and Debug Snapshot file format 0v2.pdf` + +This format is used in ARM's DS-5 debugger, and the open source CoreSight Access Library (CSAL). + +### Configuring decoders ### + +The next task is to configure the requried decoders. The client program must know the type of ETM/PTM in use +to correctly set the decoder configuration. + +Each class of trace source has a specific set of register values that the decoder requires to correctly interpret the +raw trace data and convert it to packets then fully decode. + +Configuration of an ETMv4 decoder requires initialisation of the EtmV4Config class, which is achieved by filling in a +@ref ocsd_etmv4_cfg structure:- + +~~~{.c} + typedef struct _ocsd_etmv4_cfg + { + uint32_t reg_idr0; /**< ID0 register */ + uint32_t reg_idr1; /**< ID1 register */ + uint32_t reg_idr2; /**< ID2 register */ + uint32_t reg_idr8; + uint32_t reg_idr9; + uint32_t reg_idr10; + uint32_t reg_idr11; + uint32_t reg_idr12; + uint32_t reg_idr13; + uint32_t reg_configr; /**< Config Register */ + uint32_t reg_traceidr; /**< Trace Stream ID register */ + ocsd_arch_version_t arch_ver; /**< Architecture version */ + ocsd_core_profile_t core_prof; /**< Core Profile */ + } ocsd_etmv4_cfg; +~~~ + +The structure contains a number of read-only ID registers, and key programmable control registers that define +the trace output features - such as if the ETM will output timestamps or cycle counts - and the CoreSight Trace ID. + +Once this structure is filled in then the decoder can be configured in the decode tree:- + +~~~{.cpp} + ocsd_etmv4_cfg config; + + // ... + // code to fill in config from programmed registers and id registers + // ... + + EtmV4Config configObj(&config); // initialise decoder config class + std::string decoderName(OCSD_BUILTIN_DCD_ETMV4I); // use built in ETMv4 instruction decoder. + int decoderCreateFlags = OCSD_CREATE_FLG_FULL_DECODER; // decoder type to create - OCSD_CREATE_FLG_PACKET_PROC for packet processor only + ocsd_err_t err = pDecodeTree->createDecoder(decoderName, decoderCreateFlags,&configObj); +~~~ + +This code creates a full trace decoder for an ETMv4 source, which consists of a packet processor and packet decoder pair. The decoder is automatically associated with the +CoreSight Trace ID programmed into the register provided in the `config` structure. + +It is also possible to create a packet processor only decoder if the `OCSD_CREATE_FLG_PACKET_PROC` flag is +used instead. These packet only decoders can be used to create a dump of the raw trace as discrete trace packets. + +All decoders a registered with the library using a name - the standard ARM protocols are considered built in +decoders and are registered automatically. The library contains defined names for these decoders - `OCSD_BUILTIN_DCD_ETMV4I` + being the name used for ETMv4 protocol. + +The C-API uses the call create_generic_decoder() with the same configuration structure:- + +~~~{.c} + ocsd_etmv4_cfg config; + + // ... + // code to fill in config from programmed registers and id registers + // ... + + const char * decoderName = OCSD_BUILTIN_DCD_ETMV4I); // use built in ETMv4 instruction decoder. + int decoderCreateFlags = OCSD_CREATE_FLG_FULL_DECODER; // decoder type to create - OCSD_CREATE_FLG_PACKET_PROC for packet processor only + void *p_context = // + ocsd_err_t err = create_generic_decoder(dcdtree_handle,decoderName,(void *)&config,p_context); +~~~ + +The configuration must be completed for each trace source in the decode tree which requires decoding. + +The different trace source types have different configuration structures, classes and names + +| protocol | config struct | class | name define | +|:----------|:--------------------|:------------|:-----------------------------| +| __ETMv4__ | @ref ocsd_etmv4_cfg | EtmV4Config | @ref OCSD_BUILTIN_DCD_ETMV4I | +| __ETMv3__ | @ref ocsd_etmv3_cfg | EtmV3Config | @ref OCSD_BUILTIN_DCD_ETMV3 | +| __PTM__ | @ref ocsd_ptm_cfg | PtmConfig | @ref OCSD_BUILTIN_DCD_PTM | +| __STM__ | @ref ocsd_stm_cfg | STMConfig | @ref OCSD_BUILTIN_DCD_STM | + +### Adding in Memory Images ### + +Memory images are needed when a full trace decode is required. Memory images consist of a base address and length, and +contain instruction opcodes that may be executed during the operation of the traced program. The images are used by +the decoder to follow the path of the traced program by interpreting the information contained within the trace that +defines which program branches are taken and the target addresses of those branches. + +The library defined memory image accessor objects, which can be simple memory buffers, files containing the binary +code image, or a callback that allows the client to handle memory accesses directly. When files are used, the + object may contain a set of base addresses and lengths, with offsets into the file - allowing the decoder + to directly access multiple code segments in executable image files. + +Memory image objects are collated by a memory mapper. This interfaces to the decoder through the ITargetMemAccess interface, +and selects the correct image object for the address requested by the decoder. The memory mapper will also validate image +objects as they are added to the decoder, and will not permit overlapping images. + +![Memory Mapper and Memory Images](memacc_objs.jpg) + +The client can add memory images to the decoder via API calls to the decode tree. These methods add memory image accessors of various +types to be managed by a memory access mapper:- + +~~~{.cpp} + class DecodeTree { + ///... + ocsd_err_t addBufferMemAcc(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint8_t *p_mem_buffer, const uint32_t mem_length); + ocsd_err_t addBinFileMemAcc(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const std::string &filepath); + ocsd_err_t addBinFileRegionMemAcc(const ocsd_file_mem_region_t *region_array, const int num_regions, const ocsd_mem_space_acc_t mem_space, const std::string &filepath); */ + ocsd_err_t addCallbackMemAcc(const ocsd_vaddr_t st_address, const ocsd_vaddr_t en_address, const ocsd_mem_space_acc_t mem_space, Fn_MemAcc_CB p_cb_func, const void *p_context); + ///... + } +~~~ + +It is further possible to differentiate between memory image access objects by the memory space for which they are valid. If it is known that a certain code image +is present in secure EL3, then an image can be associated with the @ref ocsd_mem_space_acc_t type value @ref OCSD_MEM_SPACE_EL3, which will allow another image to be +present at the same address but a different exception level. However, for the majority of systems, such detailed knowledge of the code is not available, or +overlaps across memory spaces do not occur. In these cases, and for general use (including Linux trace decode), @ref OCSD_MEM_SPACE_ANY should be used. + +The C-API contains a similar set of calls to set up memory access objects:- + +~~~{.c} + OCSD_C_API ocsd_err_t ocsd_dt_add_buffer_mem_acc(const dcd_tree_handle_t handle, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint8_t *p_mem_buffer, const uint32_t mem_length); + OCSD_C_API ocsd_err_t ocsd_dt_add_binfile_mem_acc(const dcd_tree_handle_t handle, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const char *filepath); + OCSD_C_API ocsd_err_t ocsd_dt_add_binfile_region_mem_acc(const dcd_tree_handle_t handle, const ocsd_file_mem_region_t *region_array, const int num_regions, const ocsd_mem_space_acc_t mem_space, const char *filepath); + OCSD_C_API ocsd_err_t ocsd_dt_add_callback_mem_acc(const dcd_tree_handle_t handle, const ocsd_vaddr_t st_address, const ocsd_vaddr_t en_address, const ocsd_mem_space_acc_t mem_space, Fn_MemAcc_CB p_cb_func, const void *p_context); +~~~ + + +### Adding the output callbacks ### + +The decoded trace output ia collect by the client application through callback functions registered with the library. + +Depending on the decode configuration chosen, this can be in the form of the fully decoded trace output as generic trace +packets, or discrete trace packets for each trace stream ID. + +__Full Decode__ + +When full decode is chosen then all output is via the generic packet interface: + +~~~{.cpp} + class ITrcGenElemIn + { + ///... + + virtual ocsd_datapath_resp_t TraceElemIn(const ocsd_trc_index_t index_sop, + const uint8_t trc_chan_id, + const OcsdTraceElement &el); + } +~~~ + +The client application registers a callback class or function with this signature. + +For each output packet the libary calls the registered function, providing the byte index into the raw trace for the first +byte of the trace protocol packet that resulted in its generation, plus the CoreSight trace ID of the source stream, +#and the output packet itself. + +The client callback must process the packet before returning the call - the reference to the packet data is only +valid for the duration of the call. This means that the client will either have to copy and buffer packets for later +processing if required, process immediately, or use an appropriate combination, dependent on the requirements of the +client. + +The client callback provides a ocsd_datapath_resp_t response code to indicate to the input side of the library if decoding is to continue. + +~~~{.cpp} + DecodeTree *pTree; + TrcGenericElementPrinter genElemPrinter; // derived from ITrcGenElemIn, overrides TraceElemIn() to print incoming packet to logger. + + ///... + + pTree->setGenTraceElemOutI(genElemPrinter); + +~~~ + +Alternatively in C-API, the callback function pointer type is defined:- + +~~~{.c} + typedef ocsd_datapath_resp_t (* FnTraceElemIn)( const void *p_context, + const ocsd_trc_index_t index_sop, + const uint8_t trc_chan_id, + const ocsd_generic_trace_elem *elem); +~~~ + +giving API calls to set up:- + +~~~{.c} + FnTraceElemIn gen_pkt_fn = &gen_trace_elem_analyze; // set to function matching signature. + dcd_tree_handle_t dcdtree_handle; + + // ... + + ret = ocsd_dt_set_gen_elem_outfn(dcdtree_handle, gen_pkt_fn, 0); +~~~ + +The output packets and their intepretatation are described here [prog_guide_generic_pkts.md](@ref generic_pkts). + +__Packet Process only, or Monitor packets in Full Decode__ + +The client can set up the library for packet processing only, in which case the library output is +the trace packets only, so these packets need a sink callback for each channel being output. + +When full decode is in operation, then the principle output is the generic packets that are output for +all channels in operation to the single callback mentioned above. Additional callbacks can be added to +each of the trace channels to monitor the packet processing stage as it happens at point that the packets +are passed to the full decoder. + +Both methods of processing the discrete trace packets require callbacks to be registered on a +per Trace ID / channel basis. The specifics of the callback and the resulting packet will vary according to +the protocol of the trace source. + +The .cpp interface registers a packet sink / packet monitor object with the relevant decoder object. + +This sink object is based on the tempated IPktDataIn interface. + +~~~{.cpp} +template class IPktDataIn : public ITrcTypedBase { + // ... + virtual ocsd_datapath_resp_t PacketDataIn( const ocsd_datapath_op_t op, + const ocsd_trc_index_t index_sop, + const P *p_packet_in) = 0; +} +~~~ + +The template type parameter will be the protocol type for the trace source in question - e.g. EtmV4ITrcPacket. +This interface contains a method that will be called with trace packets. + +The monitor object must be based on the IPktRawDataMon class, with a similarly typed template parameter and callback +function. + +~~~{.cpp} +template class IPktRawDataMon : public ITrcTypedBase { + // ... + virtual void RawPacketDataMon( const ocsd_datapath_op_t op, + const ocsd_trc_index_t index_sop, + const P *pkt, + const uint32_t size, + const uint8_t *p_data) = 0; +} +~~~ + +Given a suitable callback object the process for attaching to the decode is as follows:- + +~~~{.cpp} + // client custom packet sink for ETMv4 - derived from IPktDataIn + class MyTracePacketSinkETMv4 : public IPktDataIn { + // ... + }; + + uint8_t CSID; + DecodeTree *pTree; // pointer to decode tree + MyTracePacketSinkETMv4 *pSink; + + // ... obtain CSID and decode tree object + + // decode trees manage decode elements using a tree element object, registered against CSID. + DecodeTreeElement *pElement = pTree->getDecoderElement(CSID); + pSink = new MyTracePacketSinkETMv4(); + if (pElement && pSink) + err = pElement->getDecoderMngr()->attachPktSink(pElement->getDecoderHandle(), pSink); + +~~~ + +The decode tree object is used to obtain the decode tree element associated with the Coresight trace ID. +The IDecoderMngr interface on this object is used to attach the packet sink object to the required decoder. + +For monitor objects use an attachPktMonitor() call with a suitably derived monitor sink object. + +The key difference between the packet sink, and the packet monitor is that the monitor is not in the trace decode +data path, so does not return ocsd_datapath_resp_t values. The monitor callback also provides the raw trace byte +data for the packet. + +Device tree call for registering a callback in C-API and the function signatures for each type of shown below.. +The C-API code contains underlying managment code that connects the callback with the correct packet decoder object. + +~~~{.c} +OCSD_C_API ocsd_err_t ocsd_dt_attach_packet_callback( const dcd_tree_handle_t handle, // decode tree handle + const unsigned char CSID, // trace channel ID + const ocsd_c_api_cb_types callback_type, // defines packet only processing sink or monitor function signature. + void *p_fn_callback_data, // pointer to the callback function for the packet data. + const void *p_context); // opaque context to use inside the callback. +~~~ + +Callback definition for packet only sink callback type: +~~~{.c} +/** function pointer type for packet processor packet output sink, packet analyser/decoder input - generic declaration */ +typedef ocsd_datapath_resp_t (* FnDefPktDataIn)(const void *p_context, + const ocsd_datapath_op_t op, + const ocsd_trc_index_t index_sop, + const void *p_packet_in + ); +~~~ + +Callback definition for packet monitor callback type +~~~{.c} +/** function pointer type for packet processor packet monitor sink, raw packet monitor / display input - generic declaration */ +typedef void (* FnDefPktDataMon)(const void *p_context, + const ocsd_datapath_op_t op, + const ocsd_trc_index_t index_sop, + const void *p_packet_in, + const uint32_t size, + const uint8_t *p_data + ); +~~~ + +As with the `.cpp` code, the monitor callback does not have a return value, but also has the raw trace bytes for the packet as part of +the monitor. + +In both cases in the C-API, the `void *p_packet_in` must be cast to packet structure appropriate to the trace protocol associated with the +CSID value. e.g. for ETMv4 this would be @ref ocsd_etmv4_i_pkt. + + +Programming Examples - using the configured Decode Tree. +-------------------------------------------------------- + +Once the decode tree has been configured then data raw trace data can be processed through the decode tree. + +The client program will require two functions to use the library. The first is on the input side of the library +which must be driven with raw data, until the data is complete, or an error occurs. This processing routine must +check the library returns and respond appropriately. + +The second consists of output callback(s) which process the decoded generic packets, or trace packets. +This routine will return response codes according to the needs of the client. + +![Trace Data call and response path](decode_data_path_resp.jpg) + +The diagram shows the data input and response path. The data is driven into the decoding library by the client raw data input +routine on the left. Processed packets are received by the client packet callback(s) on the right, and push response codes back +through the library. + +The raw data input routine calls the standard ITrcDataIn interface with an operation code, and if appropriate some raw +trace data. The input operation code will define how the library treats the input parameters. + + +| Operation | Description | Trace Data provided | +|:-------------------|:-----------------------------------------------------------------|:--------------------| +| @ref OCSD_OP_DATA | Process data provided by data pointer parameters. | Yes | +| @ref OCSD_OP_FLUSH | Call after prior wait response - finish processing previous data | No | +| @ref OCSD_OP_EOT | End of trace data. Library will complete any pending decode. | No | +| @ref OCSD_OP_RESET | Hard reset of decoder state - use current config for new data | No | + +A set of standard responses is used to indicate to the raw data input whether it should continue to push data through the library, +pause and then flush, or if a fatal processing error has occurred. + +The response codes can come from the internal library decoder, or from the part of the client that is handling the processing of the +output packets on the right of the diagram. + +_Response Codes_: The are contained in the @ref _ocsd_datapath_resp_t enum. + +- __OCSD_RESP_CONT, OCSD_RESP_CONT_xxx__: Indicates that processing is to continue. Generated either internally by the library if more data + is needed to generate an output packet, or by the output packet processors to indicate processing + is to continue. +- __OCSD_RESP_WAIT, OCSD_RESP_WAIT_xxx:__ Sent by the client processors to pause processing. This will freeze the internal state of the library + and cause the WAIT response to be propogated through to the input side, with an indication of the number + of bytes processed. After a WAIT, the input side must respond with flush operations, until a CONT is + seen again and further data can then be input into the library. +- __OCSR_RESP_FATAL_xxx__: Fatal processing error. No further processing can take place. See error response logger for reason. + Normally the result of corrupt or incorrect trace data. + +The user should note that the client program controls routines on both the input and output side of the library. The output routine may be buffering +output packets, and when the buffer is full, returns a WAIT ressponse. This will be propgated through to the input routine. This should now terminate +data processing, saving state and the client will run a routine to empty / process the full packet buffer. Once the necessary processing is done, +then the input routine can be restarted, but __must__ follow the FLUSH operational rule described above. + +Excerpts from the data input routine used by the `trc_pkt_lister` program are shown below: + +~~~{.cpp} + // process the current buffer load until buffer done, or fatal error occurs + while((nBuffProcessed < nBuffRead) && !OCSD_DATA_RESP_IS_FATAL(dataPathResp)) + { + if(OCSD_DATA_RESP_IS_CONT(dataPathResp)) + { + dataPathResp = dcd_tree->TraceDataIn( + OCSD_OP_DATA, + trace_index, + (uint32_t)(nBuffRead - nBuffProcessed), + &(trace_buffer[0])+nBuffProcessed, + &nUsedThisTime); + + nBuffProcessed += nUsedThisTime; + trace_index += nUsedThisTime; + + } + else // last response was _WAIT + { + // may need to acknowledge a wait from the gen elem printer + if(genElemPrinter->needAckWait()) + genElemPrinter->ackWait(); + + // dataPathResp not continue or fatal so must be wait... + dataPathResp = dcd_tree->TraceDataIn(OCSD_OP_FLUSH,0,0,0,0); + } + } + +~~~ + +_Note_: in this test program, the WAIT response is an artificial test condition, so the input routine does not terminate on seeing it - it is cleared down +and FLUSH is immediately sent. Normal client routines would most likely drop out of the processing loop, take actions to clear the WAIT condition, then +resume processing with a FLUSH. + +See the `trc_pkt_lister` and `c_api_pkt_print_test` test program source code for further examples of driving data through the library. diff --git a/decoder/docs/test_progs.md b/decoder/docs/test_progs.md index 51cb526085a..27194553f23 100644 --- a/decoder/docs/test_progs.md +++ b/decoder/docs/test_progs.md @@ -52,8 +52,10 @@ __Command Line Options__ *Decode options* -- `-id ` : Set an ID to list (may be used mutiple times) - default if no id set is for all IDs to be printed. +- `-id ` : Set an ID to list (may be used multiple times) - default if no id set is for all IDs to be printed. - `-src_name ` : List packets from a given snapshot source name (defaults to first source found). +- `-tpiu` : Input data is from a TPIU source that has TPIU FSYNC packets present. +- `-tpiu_hsync` : Input data is from a TPIU source that has both TPIU FSYNC and HSYNC packets present. - `-decode` : Full decode of the packets from the trace snapshot (default is to list undecoded packets only. - `-decode_only` : Does not list the undecoded packets, just the trace decode. - `-o_raw_packed` : Output raw packed trace frames. @@ -140,42 +142,49 @@ Command line:- `trc_pkt_lister -ss_dir ..\..\..\snapshots\juno_r1_1 -decode -id 0x10` ~~~~~~~~~~~~~~~~ -Idx:17230; ID:10; RCTDL_GEN_TRC_ELEM_TRACE_ON() -Idx:17232; ID:10; RCTDL_GEN_TRC_ELEM_PE_CONTEXT(EL1N; AArch64; VMID=0x0; CTXTID=0x0; ) -Idx:17248; ID:10; RCTDL_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc000096a00:[0xffffffc000096a10] ) + +Idx:17204; ID:10; [0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80 ]; I_ASYNC : Alignment Synchronisation. +Idx:17218; ID:10; [0x01 0x01 0x00 ]; I_TRACE_INFO : Trace Info.; INFO=0x0 +Idx:17221; ID:10; [0x9d 0x00 0x35 0x09 0x00 0xc0 0xff 0xff 0xff ]; I_ADDR_L_64IS0 : Address, Long, 64 bit, IS0.; Addr=0xFFFFFFC000096A00; +Idx:17230; ID:10; [0x04 ]; I_TRACE_ON : Trace On. +Idx:17232; ID:10; [0x85 0x00 0x35 0x09 0x00 0xc0 0xff 0xff 0xff 0xf1 0x00 0x00 0x00 0x00 0x00 ]; I_ADDR_CTXT_L_64IS0 : Address & Context, Long, 64 bit, IS0.; Addr=0xFFFFFFC000096A00; Ctxt: AArch64,EL1, NS; CID=0x00000000; VMID=0x0000; +Idx:17248; ID:10; [0xf7 ]; I_ATOM_F1 : Atom format 1.; E +Idx:17230; ID:10; OCSD_GEN_TRC_ELEM_TRACE_ON( [begin or filter]) +Idx:17232; ID:10; OCSD_GEN_TRC_ELEM_PE_CONTEXT((ISA=A64) EL1N; 64-bit; VMID=0x0; CTXTID=0x0; ) +Idx:17248; ID:10; OCSD_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc000096a00:[0xffffffc000096a10] num_i(4) last_sz(4) (ISA=A64) E ISB ) Idx:17249; ID:10; [0x9d 0x30 0x25 0x59 0x00 0xc0 0xff 0xff 0xff ]; I_ADDR_L_64IS0 : Address, Long, 64 bit, IS0.; Addr=0xFFFFFFC000594AC0; Idx:17258; ID:10; [0xf7 ]; I_ATOM_F1 : Atom format 1.; E -Idx:17258; ID:10; RCTDL_GEN_TRC_ELEM_ADDR_NACC( 0xffffffc000594ac0 ) +Idx:17258; ID:10; OCSD_GEN_TRC_ELEM_ADDR_NACC( 0xffffffc000594ac0 ) Idx:17259; ID:10; [0x95 0xd6 0x95 ]; I_ADDR_S_IS0 : Address, Short, IS0.; Addr=0xFFFFFFC000592B58 ~[0x12B58] Idx:17262; ID:10; [0xf9 ]; I_ATOM_F3 : Atom format 3.; ENN -Idx:17262; ID:10; RCTDL_GEN_TRC_ELEM_ADDR_NACC( 0xffffffc000592b58 ) +Idx:17262; ID:10; OCSD_GEN_TRC_ELEM_ADDR_NACC( 0xffffffc000592b58 ) Idx:17264; ID:10; [0xf7 ]; I_ATOM_F1 : Atom format 1.; E -Idx:17265; ID:10; [0x9a 0x32 0x62 0x5a 0x00 ]; I_ADDR_L_32IS0 : Address, Long, 32 bit, IS0.; Addr=0x005AC4C8; +Idx:17265; ID:10; [0x9a 0x32 0x62 0x5a 0x00 ]; I_ADDR_L_32IS0 : Address, Long, 32 bit, IS0.; Addr=0xFFFFFFC0005AC4C8; Idx:17270; ID:10; [0xdb ]; I_ATOM_F2 : Atom format 2.; EE -Idx:17270; ID:10; RCTDL_GEN_TRC_ELEM_ADDR_NACC( 0xffffffc0005ac4c8 ) -Idx:17271; ID:10; [0x9a 0x62 0x52 0x0e 0x00 ]; I_ADDR_L_32IS0 : Address, Long, 32 bit, IS0.; Addr=0x000EA588; +Idx:17270; ID:10; OCSD_GEN_TRC_ELEM_ADDR_NACC( 0xffffffc0005ac4c8 ) +Idx:17271; ID:10; [0x9a 0x62 0x52 0x0e 0x00 ]; I_ADDR_L_32IS0 : Address, Long, 32 bit, IS0.; Addr=0xFFFFFFC0000EA588; Idx:17276; ID:10; [0xfc ]; I_ATOM_F3 : Atom format 3.; NNE -Idx:17276; ID:10; RCTDL_GEN_TRC_ELEM_ADDR_NACC( 0xffffffc0000ea588 ) -Idx:17277; ID:10; [0x9a 0x58 0x15 0x59 0x00 ]; I_ADDR_L_32IS0 : Address, Long, 32 bit, IS0.; Addr=0x00592B60; +Idx:17276; ID:10; OCSD_GEN_TRC_ELEM_ADDR_NACC( 0xffffffc0000ea588 ) +Idx:17277; ID:10; [0x9a 0x58 0x15 0x59 0x00 ]; I_ADDR_L_32IS0 : Address, Long, 32 bit, IS0.; Addr=0xFFFFFFC000592B60; Idx:17283; ID:10; [0x06 0x1d ]; I_EXCEPT : Exception.; IRQ; Ret Addr Follows; -Idx:17285; ID:10; [0x95 0x59 ]; I_ADDR_S_IS0 : Address, Short, IS0.; Addr=0x00592B64 ~[0x164] -Idx:17283; ID:10; RCTDL_GEN_TRC_ELEM_EXCEPTION(pref ret addr:0xffffffc000592b64; excep num (0x0e) -Idx:17287; ID:10; [0x9a 0x20 0x19 0x08 0x00 ]; I_ADDR_L_32IS0 : Address, Long, 32 bit, IS0.; Addr=0x00083280; +Idx:17285; ID:10; [0x95 0x59 ]; I_ADDR_S_IS0 : Address, Short, IS0.; Addr=0xFFFFFFC000592B64 ~[0x164] +Idx:17283; ID:10; OCSD_GEN_TRC_ELEM_ADDR_NACC( 0xffffffc000592b60 ) +Idx:17283; ID:10; OCSD_GEN_TRC_ELEM_EXCEPTION(pref ret addr:0xffffffc000592b64; excep num (0x0e) ) +Idx:17287; ID:10; [0x9a 0x20 0x19 0x08 0x00 ]; I_ADDR_L_32IS0 : Address, Long, 32 bit, IS0.; Addr=0xFFFFFFC000083280; Idx:17292; ID:10; [0xfd ]; I_ATOM_F3 : Atom format 3.; ENE -Idx:17292; ID:10; RCTDL_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc000083280:[0xffffffc000083284] ) -Idx:17292; ID:10; RCTDL_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc000083d40:[0xffffffc000083d9c] ) -Idx:17292; ID:10; RCTDL_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc000083d9c:[0xffffffc000083dac] ) -Idx:17293; ID:10; [0x95 0xf7 0x09 ]; I_ADDR_S_IS0 : Address, Short, IS0.; Addr=0x000813DC ~[0x13DC] +Idx:17292; ID:10; OCSD_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc000083280:[0xffffffc000083284] num_i(1) last_sz(4) (ISA=A64) E BR ) +Idx:17292; ID:10; OCSD_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc000083d40:[0xffffffc000083d9c] num_i(23) last_sz(4) (ISA=A64) N BR ) +Idx:17292; ID:10; OCSD_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc000083d9c:[0xffffffc000083dac] num_i(4) last_sz(4) (ISA=A64) E iBR b+link ) +Idx:17293; ID:10; [0x95 0xf7 0x09 ]; I_ADDR_S_IS0 : Address, Short, IS0.; Addr=0xFFFFFFC0000813DC ~[0x13DC] Idx:17297; ID:10; [0xdb ]; I_ATOM_F2 : Atom format 2.; EE -Idx:17297; ID:10; RCTDL_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc0000813dc:[0xffffffc0000813f0] ) -Idx:17297; ID:10; RCTDL_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc00008f2e0:[0xffffffc00008f2e4] ) -Idx:17298; ID:10; [0x95 0x7e ]; I_ADDR_S_IS0 : Address, Short, IS0.; Addr=0x000813F8 ~[0x1F8] +Idx:17297; ID:10; OCSD_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc0000813dc:[0xffffffc0000813f0] num_i(5) last_sz(4) (ISA=A64) E BR b+link ) +Idx:17297; ID:10; OCSD_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc00008f2e0:[0xffffffc00008f2e4] num_i(1) last_sz(4) (ISA=A64) E iBR A64:ret ) +Idx:17298; ID:10; [0x95 0x7e ]; I_ADDR_S_IS0 : Address, Short, IS0.; Addr=0xFFFFFFC0000813F8 ~[0x1F8] Idx:17300; ID:10; [0xe0 ]; I_ATOM_F6 : Atom format 6.; EEEN -Idx:17300; ID:10; RCTDL_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc0000813f8:[0xffffffc00008140c] ) -Idx:17300; ID:10; RCTDL_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc00008141c:[0xffffffc000081434] ) -Idx:17300; ID:10; RCTDL_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc00008140c:[0xffffffc000081414] ) -Idx:17300; ID:10; RCTDL_GEN_TRC_ELEM_ADDR_NACC( 0xffffffc000117cf0 ) - +Idx:17300; ID:10; OCSD_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc0000813f8:[0xffffffc00008140c] num_i(5) last_sz(4) (ISA=A64) E BR ) +Idx:17300; ID:10; OCSD_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc00008141c:[0xffffffc000081434] num_i(6) last_sz(4) (ISA=A64) E BR ) +Idx:17300; ID:10; OCSD_GEN_TRC_ELEM_INSTR_RANGE(exec range=0xffffffc00008140c:[0xffffffc000081414] num_i(2) last_sz(4) (ISA=A64) E BR b+link ) +Idx:17300; ID:10; OCSD_GEN_TRC_ELEM_ADDR_NACC( 0xffffffc000117cf0 ) ~~~~~~~~~~~~~~~~ diff --git a/decoder/include/common/ocsd_code_follower.h b/decoder/include/common/ocsd_code_follower.h index 0e8691034a6..b024aa02a36 100644 --- a/decoder/include/common/ocsd_code_follower.h +++ b/decoder/include/common/ocsd_code_follower.h @@ -95,6 +95,7 @@ public: const bool isLink() const; //!< is a link (branch with link etc) const bool ISAChanged() const; //!< next ISA different from input ISA. const ocsd_isa nextISA() const; //!< ISA for next instruction + const uint8_t getInstrSize() const; //!< Get the last instruction size. // information on error conditions const bool isNacc() const; //!< true if Memory Not Accessible (nacc) error occurred @@ -192,6 +193,11 @@ inline const ocsd_instr_subtype OcsdCodeFollower::getInstrSubType() const return m_instr_info.sub_type; } +inline const uint8_t OcsdCodeFollower::getInstrSize() const +{ + return m_instr_info.instr_size; +} + inline const bool OcsdCodeFollower::isCondInstr() const { return (bool)(m_instr_info.is_conditional == 1); diff --git a/decoder/include/common/ocsd_dcd_tree.h b/decoder/include/common/ocsd_dcd_tree.h index 496f8e5d72e..e4e74f2bc65 100644 --- a/decoder/include/common/ocsd_dcd_tree.h +++ b/decoder/include/common/ocsd_dcd_tree.h @@ -313,6 +313,22 @@ public: */ ocsd_err_t addBinFileRegionMemAcc(const ocsd_file_mem_region_t *region_array, const int num_regions, const ocsd_mem_space_acc_t mem_space, const std::string &filepath); + + /*! + * Updates/adds to a memory accessor for a memory block supplied as a one or more memory regions in a binary file. + * Region structures are created that describe the memory start address, the offset within the binary file + * for that address, and the length of the region. This accessor can be used to point to the code section + * in a program file for example. + * + * @param *region_array : array of valid memory regions in the file. + * @param num_regions : number of regions + * @param mem_space : Memory space + * @param &filepath : Path to the binary data file + * + * @return ocsd_err_t : Library error code or OCSD_OK if successful. + */ + ocsd_err_t updateBinFileRegionMemAcc(const ocsd_file_mem_region_t *region_array, const int num_regions, const ocsd_mem_space_acc_t mem_space, const std::string &filepath); + /*! * This memory accessor allows the client to supply a callback function for the region * defined by the start and end addresses. This can be used to supply a custom memory accessor, @@ -327,7 +343,8 @@ public: * @return ocsd_err_t : Library error code or OCSD_OK if successful. */ ocsd_err_t addCallbackMemAcc(const ocsd_vaddr_t st_address, const ocsd_vaddr_t en_address, const ocsd_mem_space_acc_t mem_space, Fn_MemAcc_CB p_cb_func, const void *p_context); - + ocsd_err_t addCallbackIDMemAcc(const ocsd_vaddr_t st_address, const ocsd_vaddr_t en_address, const ocsd_mem_space_acc_t mem_space, Fn_MemAccID_CB p_cb_func, const void *p_context); + /*! * Remove the memory accessor from the map, that begins at the given address, for the memory space provided. * @@ -368,6 +385,9 @@ private: ocsd_err_t createDecodeElement(const uint8_t CSID); void destroyDecodeElement(const uint8_t CSID); void destroyMemAccMapper(); + ocsd_err_t initCallbackMemAcc(const ocsd_vaddr_t st_address, const ocsd_vaddr_t en_address, + const ocsd_mem_space_acc_t mem_space, void *p_cb_func, bool IDfn, const void *p_context); + ocsd_dcd_tree_src_t m_dcd_tree_type; diff --git a/decoder/include/common/ocsd_error_logger.h b/decoder/include/common/ocsd_error_logger.h index d60139726ef..3badd337f65 100644 --- a/decoder/include/common/ocsd_error_logger.h +++ b/decoder/include/common/ocsd_error_logger.h @@ -37,7 +37,7 @@ #include #include -#include +//#include #include "interfaces/trc_error_log_i.h" #include "ocsd_error.h" @@ -49,7 +49,7 @@ public: ocsdDefaultErrorLogger(); virtual ~ocsdDefaultErrorLogger(); - bool initErrorLogger(const ocsd_err_severity_t verbosity, bool bCreateOutputLogger = false); + bool initErrorLogger(const ocsd_err_severity_t verbosity, bool bCreateOutputLogger = false); //!< Initialise the error logger with a severity filter, optionally create an output logger on stderr. virtual ocsdMsgLogger *getOutputLogger() { return m_output_logger; }; virtual void setOutputLogger(ocsdMsgLogger *pLogger); diff --git a/decoder/include/common/ocsd_msg_logger.h b/decoder/include/common/ocsd_msg_logger.h index c40af9189aa..d83a0224e17 100644 --- a/decoder/include/common/ocsd_msg_logger.h +++ b/decoder/include/common/ocsd_msg_logger.h @@ -53,23 +53,26 @@ public: ocsdMsgLogger(); ~ocsdMsgLogger(); + /** Typedef enum providing flags to define the output methods for the message logger. + */ typedef enum { - OUT_NONE = 0, - OUT_FILE = 1, - OUT_STDERR = 2, - OUT_STDOUT = 4, - OUT_STR_CB = 8 /* output to external string callback interface */ + OUT_NONE = 0, /*!< No output from logger*/ + OUT_FILE = 1, /*!< Output to file */ + OUT_STDERR = 2, /*!< Output to stderr */ + OUT_STDOUT = 4, /*!< Output to stdout */ + OUT_STR_CB = 8 /*!< output to external string callback interface */ } output_dest; - void setLogOpts(int logOpts); - const int getLogOpts() const { return m_outFlags; }; + void setLogOpts(int logOpts); //!< set the output logging flags. + const int getLogOpts() const //! get the current output logging flags value. + { return m_outFlags; }; - void setLogFileName(const char *fileName); - void setStrOutFn(ocsdMsgLogStrOutI *p_IstrOut); + void setLogFileName(const char *fileName); //!< Set the output log filename, and enable logging to file. + void setStrOutFn(ocsdMsgLogStrOutI *p_IstrOut); //!< Set the output log string callback and enable logging to callback. - void LogMsg(const std::string &msg); + void LogMsg(const std::string &msg); //!< Log a message to the current set output channels. - const bool isLogging() const; + const bool isLogging() const; //!< true if logging active private: int m_outFlags; diff --git a/decoder/include/common/trc_gen_elem.h b/decoder/include/common/trc_gen_elem.h index 00081b55690..1c4a47b3aa0 100644 --- a/decoder/include/common/trc_gen_elem.h +++ b/decoder/include/common/trc_gen_elem.h @@ -73,9 +73,10 @@ public: void setTraceOnReason(const trace_on_reason_t reason); - void setAddrRange(const ocsd_vaddr_t st_addr, const ocsd_vaddr_t en_addr); - void setLastInstrInfo(const bool exec, const ocsd_instr_type last_i_type, const ocsd_instr_subtype last_i_subtype); + void setAddrRange(const ocsd_vaddr_t st_addr, const ocsd_vaddr_t en_addr, const int num_instr = 1); + void setLastInstrInfo(const bool exec, const ocsd_instr_type last_i_type, const ocsd_instr_subtype last_i_subtype, const uint8_t size); void setAddrStart(const ocsd_vaddr_t st_addr) { this->st_addr = st_addr; }; + void setLastInstrCond(const int is_cond) { this->last_instr_cond = is_cond; }; void setSWTInfo(const ocsd_swt_info_t swt_info) { sw_trace_info = swt_info; }; void setExtendedDataPtr(const void *data_ptr); @@ -122,15 +123,17 @@ inline void OcsdTraceElement::setEvent(const event_t ev_type, const uint16_t num trace_event.ev_number = ev_type == EVENT_NUMBERED ? number : 0; } -inline void OcsdTraceElement::setAddrRange(const ocsd_vaddr_t st_addr, const ocsd_vaddr_t en_addr) +inline void OcsdTraceElement::setAddrRange(const ocsd_vaddr_t st_addr, const ocsd_vaddr_t en_addr, const int num_instr /* = 1 */) { this->st_addr = st_addr; this->en_addr = en_addr; + this->num_instr_range = num_instr; } -inline void OcsdTraceElement::setLastInstrInfo(const bool exec, const ocsd_instr_type last_i_type, const ocsd_instr_subtype last_i_subtype) +inline void OcsdTraceElement::setLastInstrInfo(const bool exec, const ocsd_instr_type last_i_type, const ocsd_instr_subtype last_i_subtype, const uint8_t size) { last_instr_exec = exec ? 1 : 0; + last_instr_sz = size & 0x7; this->last_i_type = last_i_type; this->last_i_subtype = last_i_subtype; } diff --git a/decoder/include/i_dec/trc_i_decode.h b/decoder/include/i_dec/trc_i_decode.h index ac31a79ded6..0285f418252 100644 --- a/decoder/include/i_dec/trc_i_decode.h +++ b/decoder/include/i_dec/trc_i_decode.h @@ -49,6 +49,7 @@ private: ocsd_err_t DecodeA32(ocsd_instr_info *instr_info); ocsd_err_t DecodeA64(ocsd_instr_info *instr_info); ocsd_err_t DecodeT32(ocsd_instr_info *instr_info); + void SetArchVersion(ocsd_instr_info *instr_info); }; #endif // ARM_TRC_I_DECODE_H_INCLUDED diff --git a/decoder/include/i_dec/trc_idec_arminst.h b/decoder/include/i_dec/trc_idec_arminst.h index b15984948d2..8697f68d676 100644 --- a/decoder/include/i_dec/trc_idec_arminst.h +++ b/decoder/include/i_dec/trc_idec_arminst.h @@ -73,7 +73,9 @@ Performance event 0x0D counts these. */ int inst_ARM_is_direct_branch(uint32_t inst); int inst_Thumb_is_direct_branch(uint32_t inst); +int inst_Thumb_is_direct_branch_link(uint32_t inst, uint8_t *is_link, uint8_t *is_cond); int inst_A64_is_direct_branch(uint32_t inst); +int inst_A64_is_direct_branch_link(uint32_t inst, uint8_t *is_link); /* Get branch destination for a direct branch. @@ -83,7 +85,9 @@ int inst_Thumb_branch_destination(uint32_t addr, uint32_t inst, uint32_t *pnpc); int inst_A64_branch_destination(uint64_t addr, uint32_t inst, uint64_t *pnpc); int inst_ARM_is_indirect_branch(uint32_t inst); +int inst_Thumb_is_indirect_branch_link(uint32_t inst, uint8_t *is_link); int inst_Thumb_is_indirect_branch(uint32_t inst); +int inst_A64_is_indirect_branch_link(uint32_t inst, uint8_t *is_link); int inst_A64_is_indirect_branch(uint32_t inst); int inst_ARM_is_branch_and_link(uint32_t inst); @@ -109,6 +113,10 @@ arm_barrier_t inst_ARM_barrier(uint32_t inst); arm_barrier_t inst_Thumb_barrier(uint32_t inst); arm_barrier_t inst_A64_barrier(uint32_t inst); +int inst_ARM_wfiwfe(uint32_t inst); +int inst_Thumb_wfiwfe(uint32_t inst); +int inst_A64_wfiwfe(uint32_t inst); + /* Test whether an instruction is definitely undefined, e.g. because allocated to a "permanently UNDEFINED" space (UDF mnemonic). @@ -125,6 +133,9 @@ int inst_A64_is_UDF(uint32_t inst); ocsd_instr_subtype get_instr_subtype(); void clear_instr_subtype(); +/* set arch version info. */ +void set_arch_version(uint16_t version); + #endif // ARM_TRC_IDEC_ARMINST_H_INCLUDED /* End of File trc_idec_arminst.h */ diff --git a/decoder/include/interfaces/trc_error_log_i.h b/decoder/include/interfaces/trc_error_log_i.h index 2fc796f7245..cc65f631e75 100644 --- a/decoder/include/interfaces/trc_error_log_i.h +++ b/decoder/include/interfaces/trc_error_log_i.h @@ -56,8 +56,8 @@ class ocsdMsgLogger; class ITraceErrorLog { public: - ITraceErrorLog() {}; /**< default constructor */ - virtual ~ITraceErrorLog() {}; /**< default destructor */ + ITraceErrorLog() {}; + virtual ~ITraceErrorLog() {}; /*! * Register a named component error source. Allows the logger to associate errors with components. @@ -111,7 +111,7 @@ public: * Get the last error associated with the given Trace source channel ID. * returns a pointer to the error or 0 if no errors associated with the ID. * - * @param chan_id : ID. + * @param chan_id : Trace Source Channel ID (CoreSight Trace ID). * * @return ocsdError *: last error pointer for ID or 0. */ diff --git a/decoder/include/mem_acc/trc_mem_acc_base.h b/decoder/include/mem_acc/trc_mem_acc_base.h index 71b6a816eda..7f17bde125d 100644 --- a/decoder/include/mem_acc/trc_mem_acc_base.h +++ b/decoder/include/mem_acc/trc_mem_acc_base.h @@ -123,12 +123,13 @@ public: * * @param s_address : Start address of the read. * @param memSpace : memory space for this access. + * @param trcID : Trace ID of trace source. * @param reqBytes : Number of bytes required. * @param *byteBuffer : Buffer to copy the bytes into. * * @return uint32_t : Number of bytes read, 0 if s_address out of range, or mem space not accessible. */ - virtual const uint32_t readBytes(const ocsd_vaddr_t s_address, const ocsd_mem_space_acc_t memSpace, const uint32_t reqBytes, uint8_t *byteBuffer) = 0; + virtual const uint32_t readBytes(const ocsd_vaddr_t s_address, const ocsd_mem_space_acc_t memSpace, const uint8_t trcID, const uint32_t reqBytes, uint8_t *byteBuffer) = 0; /*! * Validate the address range - ensure addresses aligned, different, st < en etc. diff --git a/decoder/include/mem_acc/trc_mem_acc_bufptr.h b/decoder/include/mem_acc/trc_mem_acc_bufptr.h index 61ec345abe6..bd9ea8ee1e5 100644 --- a/decoder/include/mem_acc/trc_mem_acc_bufptr.h +++ b/decoder/include/mem_acc/trc_mem_acc_bufptr.h @@ -64,7 +64,7 @@ public: virtual ~TrcMemAccBufPtr() {}; /**< default destructor */ /** Memory access override - allow decoder to read bytes from the buffer. */ - virtual const uint32_t readBytes(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t memSpace, const uint32_t reqBytes, uint8_t *byteBuffer); + virtual const uint32_t readBytes(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t memSpace, const uint8_t trcID, const uint32_t reqBytes, uint8_t *byteBuffer); private: const uint8_t *m_p_buffer; /**< pointer to the memory buffer */ diff --git a/decoder/include/mem_acc/trc_mem_acc_cache.h b/decoder/include/mem_acc/trc_mem_acc_cache.h new file mode 100644 index 00000000000..5e81c2a2ea3 --- /dev/null +++ b/decoder/include/mem_acc/trc_mem_acc_cache.h @@ -0,0 +1,149 @@ +/*! +* \file trc_mem_acc_cache.h +* \brief OpenCSD : Memory accessor cache. +* +* \copyright Copyright (c) 2018, ARM Limited. All Rights Reserved. +*/ + +/* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its contributors +* may be used to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef ARM_TRC_MEM_ACC_CACHE_H_INCLUDED +#define ARM_TRC_MEM_ACC_CACHE_H_INCLUDED + +#include +#include "opencsd/ocsd_if_types.h" + +#define MEM_ACC_CACHE_PAGE_SIZE 256 +#define MEM_ACC_CACHE_MRU_SIZE 12 + +class TrcMemAccessorBase; +class ITraceErrorLog; + +typedef struct cache_block { + ocsd_vaddr_t st_addr; + uint32_t valid_len; + uint8_t data[MEM_ACC_CACHE_PAGE_SIZE]; +} cache_block_t; + +// enable define to collect stats for debugging / cache performance tests +//#define LOG_CACHE_STATS + + +/** class TrcMemAccCache - cache small amounts of data from accessors to speed up decode. */ +class TrcMemAccCache +{ +public: + TrcMemAccCache(); + ~TrcMemAccCache() {}; + + void enableCaching(bool bEnable) { m_bCacheEnabled = bEnable; }; + void invalidateAll(); + const bool enabled() const { return m_bCacheEnabled; }; + const bool enabled_for_size(const uint32_t reqSize) const + { + return (m_bCacheEnabled && (reqSize <= MEM_ACC_CACHE_PAGE_SIZE)); + } + + + /** read bytes from cache if possible - load new page if needed, bail out if data not available */ + ocsd_err_t readBytesFromCache(TrcMemAccessorBase *p_accessor, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint8_t trcID, uint32_t *numBytes, uint8_t *byteBuffer); + + void setErrorLog(ITraceErrorLog *log); + void logAndClearCounts(); + +private: + bool blockInCache(const ocsd_vaddr_t address, const uint32_t reqBytes); // run through each page to look for data. + bool blockInPage(const ocsd_vaddr_t address, const uint32_t reqBytes); + void logMsg(const std::string &szMsg); + + cache_block_t m_mru[MEM_ACC_CACHE_MRU_SIZE]; + int m_mru_idx = 0; // in use index + int m_mru_next_new = 0; // next new page at this index. + bool m_bCacheEnabled = false; + +#ifdef LOG_CACHE_STATS + uint32_t m_hits = 0; + uint32_t m_misses = 0; + uint32_t m_pages = 0; + uint32_t m_hit_rl[MEM_ACC_CACHE_MRU_SIZE]; + uint32_t m_hit_rl_max[MEM_ACC_CACHE_MRU_SIZE]; +#endif + + ITraceErrorLog *m_err_log = 0; +}; + +inline TrcMemAccCache::TrcMemAccCache() +{ + for (int i = 0; i < MEM_ACC_CACHE_MRU_SIZE; i++) + { + m_mru[i].st_addr = 0; + m_mru[i].valid_len = 0; +#ifdef LOG_CACHE_STATS + m_hit_rl[i] = 0; + m_hit_rl_max[i] = 0; +#endif + } +} + +inline bool TrcMemAccCache::blockInPage(const ocsd_vaddr_t address, const uint32_t reqBytes) +{ + if ((m_mru[m_mru_idx].st_addr <= address) && + m_mru[m_mru_idx].st_addr + m_mru[m_mru_idx].valid_len >= (address + reqBytes)) + return true; + return false; +} + +inline bool TrcMemAccCache::blockInCache(const ocsd_vaddr_t address, const uint32_t reqBytes) +{ + int tests = MEM_ACC_CACHE_MRU_SIZE; + while (tests) + { + if (blockInPage(address, reqBytes)) + return true; // found address in page + tests--; + m_mru_idx++; + if (m_mru_idx == MEM_ACC_CACHE_MRU_SIZE) + m_mru_idx = 0; + } + return false; +} + +inline void TrcMemAccCache::invalidateAll() +{ + for (int i = 0; i < MEM_ACC_CACHE_MRU_SIZE; i++) + { + m_mru[i].valid_len = 0; + m_mru[i].st_addr = 0; + } + m_mru_idx = 0; + m_mru_next_new = 0; +} + +#endif // ARM_TRC_MEM_ACC_CACHE_H_INCLUDED + +/* End of File trc_mem_acc_cache.h */ diff --git a/decoder/include/mem_acc/trc_mem_acc_cb.h b/decoder/include/mem_acc/trc_mem_acc_cb.h index df6f9930e4f..e58c6169f17 100644 --- a/decoder/include/mem_acc/trc_mem_acc_cb.h +++ b/decoder/include/mem_acc/trc_mem_acc_cb.h @@ -49,32 +49,47 @@ public: virtual ~TrcMemAccCB() {}; /** Memory access override - allow decoder to read bytes from the buffer. */ - virtual const uint32_t readBytes(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t memSpace, const uint32_t reqBytes, uint8_t *byteBuffer); + virtual const uint32_t readBytes(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t memSpace, const uint8_t trcID, const uint32_t reqBytes, uint8_t *byteBuffer); void setCBIfClass(TrcMemAccCBIF *p_if); void setCBIfFn(Fn_MemAcc_CB p_fn, const void *p_context); + void setCBIDIfFn(Fn_MemAccID_CB p_fn, const void *p_context); private: + void clearCBptrs(); TrcMemAccCBIF *m_p_CBclass; //m_startAddress < rhs.m_startAddress; }; // not going to use these objects to read bytes - defer to the file class for that. - virtual const uint32_t readBytes(const ocsd_vaddr_t s_address, const ocsd_mem_space_acc_t memSpace, const uint32_t reqBytes, uint8_t *byteBuffer) { return 0; }; + virtual const uint32_t readBytes(const ocsd_vaddr_t s_address, const ocsd_mem_space_acc_t memSpace, const uint8_t trcID, const uint32_t reqBytes, uint8_t *byteBuffer) { return 0; }; const ocsd_vaddr_t regionStartAddress() const { return m_startAddress; }; @@ -77,7 +77,7 @@ class TrcMemAccessorFile : public TrcMemAccessorBase { public: /** read bytes override - reads from file */ - virtual const uint32_t readBytes(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t memSpace, const uint32_t reqBytes, uint8_t *byteBuffer); + virtual const uint32_t readBytes(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t memSpace, const uint8_t trcID, const uint32_t reqBytes, uint8_t *byteBuffer); protected: TrcMemAccessorFile(); /**< protected default constructor */ diff --git a/decoder/include/mem_acc/trc_mem_acc_mapper.h b/decoder/include/mem_acc/trc_mem_acc_mapper.h index 07d044ead81..a700e9dbd07 100644 --- a/decoder/include/mem_acc/trc_mem_acc_mapper.h +++ b/decoder/include/mem_acc/trc_mem_acc_mapper.h @@ -41,6 +41,7 @@ #include "interfaces/trc_tgt_mem_access_i.h" #include "interfaces/trc_error_log_i.h" #include "mem_acc/trc_mem_acc_base.h" +#include "mem_acc/trc_mem_acc_cache.h" typedef enum _memacc_mapper_t { MEMACC_MAP_GLOBAL, @@ -76,7 +77,7 @@ public: ocsd_err_t RemoveAccessorByAddress(const ocsd_vaddr_t st_address, const ocsd_mem_space_acc_t mem_space, const uint8_t cs_trace_id = 0); // set the error log. - void setErrorLog(ITraceErrorLog *err_log_i) { m_err_log = err_log_i; }; + void setErrorLog(ITraceErrorLog *err_log_i); // print out the ranges in this mapper. virtual void logMappedRanges() = 0; @@ -89,11 +90,13 @@ protected: virtual void clearAccessorList() = 0; void LogMessage(const std::string &msg); + void LogWarn(const ocsd_err_t err, const std::string &msg); TrcMemAccessorBase *m_acc_curr; // most recently used - try this first. uint8_t m_trace_id_curr; // trace ID for the current accessor const bool m_using_trace_id; // true if we are using separate memory spaces by TraceID. ITraceErrorLog *m_err_log; // error log to print out mappings on request. + TrcMemAccCache m_cache; // memory accessor caching. }; diff --git a/decoder/include/opencsd/c_api/ocsd_c_api_types.h b/decoder/include/opencsd/c_api/ocsd_c_api_types.h index ca61e0aaed3..cde351fc525 100644 --- a/decoder/include/opencsd/c_api/ocsd_c_api_types.h +++ b/decoder/include/opencsd/c_api/ocsd_c_api_types.h @@ -37,6 +37,7 @@ /* select the library types that are C compatible - the interface data types */ #include "opencsd/ocsd_if_types.h" +#include "opencsd/ocsd_if_version.h" #include "opencsd/trc_gen_elem_types.h" #include "opencsd/trc_pkt_types.h" diff --git a/decoder/include/opencsd/c_api/opencsd_c_api.h b/decoder/include/opencsd/c_api/opencsd_c_api.h index f9f4ed4b861..90201d436e0 100644 --- a/decoder/include/opencsd/c_api/opencsd_c_api.h +++ b/decoder/include/opencsd/c_api/opencsd_c_api.h @@ -84,7 +84,7 @@ /** @name Library Version API @{*/ -/** Get Library version. Return a 32 bit version in form MMMMnnpp - MMMM = major verison, nn = minor version, pp = patch version */ +/** Get Library version. Return a 32 bit version in form MMMMnnpp - MMMM = major version, nn = minor version, pp = patch version */ OCSD_C_API uint32_t ocsd_get_version(void); /** Get library version string */ @@ -286,6 +286,23 @@ OCSD_C_API ocsd_err_t ocsd_dt_add_buffer_mem_acc(const dcd_tree_handle_t handle, */ OCSD_C_API ocsd_err_t ocsd_dt_add_callback_mem_acc(const dcd_tree_handle_t handle, const ocsd_vaddr_t st_address, const ocsd_vaddr_t en_address, const ocsd_mem_space_acc_t mem_space, Fn_MemAcc_CB p_cb_func, const void *p_context); + +/*! + * Add a memory access callback function. The decoder will call the function for opcode addresses in the + * address range supplied for the memory spaces covered. + * + * @param handle : Handle to decode tree. + * @param st_address : Start address of memory area covered by the callback. + * @param en_address : End address of the memory area covered by the callback. (inclusive) + * @param mem_space : Memory space(s) covered by the callback. + * @param p_cb_func : Callback function - Signature for CB with Trace ID passed to client. + * @param p_context : opaque context pointer value used in callback function. + * + * @return OCSD_C_API ocsd_err_t : Library error code - RCDTL_OK if successful. + */ +OCSD_C_API ocsd_err_t ocsd_dt_add_callback_trcid_mem_acc(const dcd_tree_handle_t handle, const ocsd_vaddr_t st_address, const ocsd_vaddr_t en_address, const ocsd_mem_space_acc_t mem_space, Fn_MemAccID_CB p_cb_func, const void *p_context); + + /*! * Remove a memory accessor by address and memory space. * diff --git a/decoder/include/opencsd/etmv4/trc_cmp_cfg_etmv4.h b/decoder/include/opencsd/etmv4/trc_cmp_cfg_etmv4.h index a3f883540a3..1d72d97afe5 100644 --- a/decoder/include/opencsd/etmv4/trc_cmp_cfg_etmv4.h +++ b/decoder/include/opencsd/etmv4/trc_cmp_cfg_etmv4.h @@ -108,6 +108,7 @@ public: /* idr 1 */ const uint8_t MajVersion() const; const uint8_t MinVersion() const; + const uint8_t FullVersion() const; /* idr 2 */ const uint32_t iaSizeMax() const; @@ -117,6 +118,7 @@ public: const uint32_t dvSize() const; const uint32_t ccSize() const; const bool vmidOpt() const; + const bool wfiwfeBranch() const; /* id regs 8-13*/ const uint32_t MaxSpecDepth() const; @@ -180,7 +182,11 @@ private: bool m_condTraceCalc; CondITrace_t m_CondTrace; +protected: ocsd_etmv4_cfg m_cfg; + uint8_t m_MajVer; + uint8_t m_MinVer; + }; /* idr 0 */ @@ -265,14 +271,18 @@ inline const bool EtmV4Config::commitOpt1() const /* idr 1 */ inline const uint8_t EtmV4Config::MajVersion() const { - return (uint8_t)((m_cfg.reg_idr1 >> 8) & 0xF); + return m_MajVer; } inline const uint8_t EtmV4Config::MinVersion() const { - return (uint8_t)((m_cfg.reg_idr1 >> 4) & 0xF); + return m_MinVer; } +inline const uint8_t EtmV4Config::FullVersion() const +{ + return (m_MajVer << 4) | m_MinVer; +} /* idr 2 */ inline const uint32_t EtmV4Config::iaSizeMax() const @@ -320,6 +330,12 @@ inline const bool EtmV4Config::vmidOpt() const return (bool)((m_cfg.reg_idr2 & 0x20000000) == 0x20000000) && (MinVersion() > 0); } +inline const bool EtmV4Config::wfiwfeBranch() const +{ + return (bool)((m_cfg.reg_idr2 & 0x80000000) && (FullVersion() >= 0x43)); +} + + /* id regs 8-13*/ inline const uint32_t EtmV4Config::MaxSpecDepth() const diff --git a/decoder/include/opencsd/etmv4/trc_etmv4_stack_elem.h b/decoder/include/opencsd/etmv4/trc_etmv4_stack_elem.h index 8bf0fb0c047..15996547163 100644 --- a/decoder/include/opencsd/etmv4/trc_etmv4_stack_elem.h +++ b/decoder/include/opencsd/etmv4/trc_etmv4_stack_elem.h @@ -56,7 +56,8 @@ typedef enum _p0_elem_t P0_TS, P0_CC, P0_TS_CC, - P0_OVERFLOW + P0_OVERFLOW, + P0_FUNC_RET, } p0_elem_t; @@ -250,6 +251,7 @@ public: ~EtmV4P0Stack(); void push_front(TrcStackElem *pElem); + void push_back(TrcStackElem *pElem); // insert element when processing void pop_back(); TrcStackElem *back(); size_t size(); @@ -260,7 +262,7 @@ public: // creation functions - create and push if successful. TrcStackElemParam *createParamElem(const p0_elem_t p0_type, const bool isP0, const ocsd_etmv4_i_pkt_type root_pkt, const ocsd_trc_index_t root_index, const std::vector ¶ms); - TrcStackElemParam *createParamElemNoParam(const p0_elem_t p0_type, const bool isP0, const ocsd_etmv4_i_pkt_type root_pkt, const ocsd_trc_index_t root_index); + TrcStackElem *createParamElemNoParam(const p0_elem_t p0_type, const bool isP0, const ocsd_etmv4_i_pkt_type root_pkt, const ocsd_trc_index_t root_index, bool back = false); TrcStackElemAtom *createAtomElem (const ocsd_etmv4_i_pkt_type root_pkt, const ocsd_trc_index_t root_index, const ocsd_pkt_atom &atom); TrcStackElemExcept *createExceptElem(const ocsd_etmv4_i_pkt_type root_pkt, const ocsd_trc_index_t root_index, const bool bSame, const uint16_t excepNum); TrcStackElemCtxt *createContextElem(const ocsd_etmv4_i_pkt_type root_pkt, const ocsd_trc_index_t root_index, const etmv4_context_t &context); @@ -284,6 +286,12 @@ inline void EtmV4P0Stack::push_front(TrcStackElem *pElem) m_P0_stack.push_front(pElem); } +// put an element on the back of the stack +inline void EtmV4P0Stack::push_back(TrcStackElem *pElem) +{ + m_P0_stack.push_back(pElem); +} + // pop last element pointer off the stack and stash it for later deletion inline void EtmV4P0Stack::pop_back() { diff --git a/decoder/include/opencsd/etmv4/trc_pkt_decode_etmv4i.h b/decoder/include/opencsd/etmv4/trc_pkt_decode_etmv4i.h index f27bb45d9fa..1c06e5ddf03 100644 --- a/decoder/include/opencsd/etmv4/trc_pkt_decode_etmv4i.h +++ b/decoder/include/opencsd/etmv4/trc_pkt_decode_etmv4i.h @@ -93,6 +93,8 @@ private: ocsd_datapath_resp_t returnStackPop(); // pop return stack and update instruction address. + ocsd_datapath_resp_t outputTraceRange(const bool executed, ocsd_trc_index_t index); + //** intra packet state (see ETMv4 spec 6.2.1); // timestamping @@ -152,12 +154,17 @@ private: EXCEP_POP, // start of processing read exception packets off the stack and analyze EXCEP_RANGE, // output a range element EXCEP_NACC, // output a nacc element + EXCEP_CTXT, // output a ctxt element EXCEP_EXCEP, // output an ecxeption element. } excep_proc_state_t; - excep_proc_state_t m_excep_proc; //!< state of exception processing - etmv4_addr_val_t m_excep_addr; //!< excepiton return address. - ocsd_trc_index_t m_excep_index; //!< trace index for exception element + struct { + excep_proc_state_t proc; //!< state of exception processing + etmv4_addr_val_t addr; //!< excetion return address. + uint32_t number; //!< exception number. + ocsd_trc_index_t index; //!< trace index for exception element + bool addr_b_tgt; //!< return address is also branch tgt address. + } m_excep_info; //!< exception info when processing exception packets ocsd_instr_info m_instr_info; //!< instruction info for code follower - in address is the next to be decoded. diff --git a/decoder/include/opencsd/etmv4/trc_pkt_elem_etmv4i.h b/decoder/include/opencsd/etmv4/trc_pkt_elem_etmv4i.h index e0343c76260..02adfc51aa7 100644 --- a/decoder/include/opencsd/etmv4/trc_pkt_elem_etmv4i.h +++ b/decoder/include/opencsd/etmv4/trc_pkt_elem_etmv4i.h @@ -115,7 +115,7 @@ public: void initNextPacket(); //!< clear any single packet only flags / state. void setType(const ocsd_etmv4_i_pkt_type pkt_type) { type = pkt_type; }; - void updateErrType(const ocsd_etmv4_i_pkt_type err_pkt_type); + void updateErrType(const ocsd_etmv4_i_pkt_type err_pkt_type, const uint8_t val = 0); void clearTraceInfo(); //!< clear all the trace info data prior to setting for new trace info packet. void setTraceInfo(const uint32_t infoVal); @@ -208,11 +208,12 @@ private: Etmv4PktAddrStack m_addr_stack; }; -inline void EtmV4ITrcPacket::updateErrType(const ocsd_etmv4_i_pkt_type err_pkt_type) +inline void EtmV4ITrcPacket::updateErrType(const ocsd_etmv4_i_pkt_type err_pkt_type, const uint8_t err_val /* = 0 */) { // set primary type to incoming error type, set packet err type to previous primary type. err_type = type; type = err_pkt_type; + err_hdr_val = err_val; } inline void EtmV4ITrcPacket::clearTraceInfo() @@ -223,7 +224,9 @@ inline void EtmV4ITrcPacket::clearTraceInfo() pkt_valid.bits.spec_depth_valid = 0; pkt_valid.bits.cc_thresh_valid = 0; - pkt_valid.bits.ts_valid = 0; // mark TS as invalid - must be re-updated after trace info. + // set these as defaults - if they don't appear in TINFO this is the state. + setTraceInfo(0); + setTraceInfoSpec(0); } inline void EtmV4ITrcPacket::setTraceInfo(const uint32_t infoVal) @@ -444,18 +447,20 @@ inline void EtmV4ITrcPacket::set32BitAddress(const uint32_t addr, const uint8_t uint64_t mask = OCSD_BIT_MASK(32); v_addr.pkt_bits = 32; - if (pkt_valid.bits.context_valid && context.SF) - v_addr.size = VA_64BIT; + if (pkt_valid.bits.context_valid && context.SF) + { + v_addr.size = VA_64BIT; + if (v_addr.valid_bits < 32) // may be updating a 64 bit address so only set 32 if currently less. + v_addr.valid_bits = 32; + v_addr.val = (v_addr.val & ~mask) | (addr & mask); + } else { - v_addr.val &= 0xFFFFFFFF; // ensure vaddr is only 32 bits if not 64 bit + v_addr.val = addr; v_addr.size = VA_32BIT; - } - - if (v_addr.valid_bits < 32) // may be 64 bit address so only set 32 if less - v_addr.valid_bits = 32; - - v_addr.val = (v_addr.val & ~mask) | (addr & mask); + v_addr.valid_bits = 32; + } + v_addr_ISA = IS; push_vaddr(); } diff --git a/decoder/include/opencsd/etmv4/trc_pkt_types_etmv4.h b/decoder/include/opencsd/etmv4/trc_pkt_types_etmv4.h index b22a2b93971..dd69a4bf677 100644 --- a/decoder/include/opencsd/etmv4/trc_pkt_types_etmv4.h +++ b/decoder/include/opencsd/etmv4/trc_pkt_types_etmv4.h @@ -1,8 +1,8 @@ /* * \file trc_pkt_types_etmv4.h - * \brief OpenCSD : + * \brief OpenCSD : ETMv4 packet info * - * \copyright Copyright (c) 2015, ARM Limited. All Rights Reserved. + * \copyright Copyright (c) 2015,2019 ARM Limited. All Rights Reserved. */ @@ -56,80 +56,95 @@ typedef enum _ocsd_etmv4_i_pkt_type ETM4_PKT_I_BAD_SEQUENCE = 0x300, /*!< invalid sequence for packet type. */ ETM4_PKT_I_BAD_TRACEMODE, /*!< invalid packet type for this trace mode. */ ETM4_PKT_I_RESERVED, /*!< packet type reserved. */ + ETM4_PKT_I_RESERVED_CFG, /*!< packet type reserved for current configuration */ /* I stream packet types. */ /* extension header. */ - ETM4_PKT_I_EXTENSION = 0x00, /*!< b00000000 */ + ETM4_PKT_I_EXTENSION = 0x00, /*!< b00000000 */ - /* address amd context */ - ETM4_PKT_I_ADDR_CTXT_L_32IS0 = 0x82, /*!< b10000010 */ + /* sync */ + ETM4_PKT_I_TRACE_INFO = 0x01, /*!< b00000001 */ + // timestamp + ETM4_PKT_I_TIMESTAMP = 0x02, /*!< b0000001x */ + ETM4_PKT_I_TRACE_ON = 0x04, /*!< b00000100 */ + ETM4_PKT_I_FUNC_RET = 0x05, /*!< b00000101 (V8M only) */ + // Exceptions + ETM4_PKT_I_EXCEPT = 0x06, /*!< b00000110 */ + ETM4_PKT_I_EXCEPT_RTN = 0x07, /*!< b00000111 */ + + /* unused encodings 0x08-0xB b00001000 to b00001011 */ + + /* cycle count packets */ + ETM4_PKT_I_CCNT_F2 = 0x0C, /*!< b0000110x */ + ETM4_PKT_I_CCNT_F1 = 0x0E, /*!< b0000111x */ + ETM4_PKT_I_CCNT_F3 = 0x10, /*!< b0001xxxx */ + + // data synchronisation markers + ETM4_PKT_I_NUM_DS_MKR = 0x20, /*!< b00100xxx */ + ETM4_PKT_I_UNNUM_DS_MKR = 0x28, /*!< b00101000 to b00101100 0x2C */ + + // speculation + ETM4_PKT_I_COMMIT = 0x2D, /*!< b00101101 */ + ETM4_PKT_I_CANCEL_F1 = 0x2E, /*!< b0010111x */ + ETM4_PKT_I_MISPREDICT = 0x30, /*!< b001100xx */ + ETM4_PKT_I_CANCEL_F2 = 0x34, /*!< b001101xx */ + ETM4_PKT_I_CANCEL_F3 = 0x38, /*!< b00111xxx */ + + /* conditional instruction tracing */ + ETM4_PKT_I_COND_I_F2 = 0x40, /*!< b01000000 - b01000010 */ + ETM4_PKT_I_COND_FLUSH = 0x43, /*!< b01000011 */ + ETM4_PKT_I_COND_RES_F4 = 0x44, /*!< b0100010x, b01000110 */ + /* unused encoding 0x47 b01000111 */ + ETM4_PKT_I_COND_RES_F2 = 0x48, /*!< b0100100x, b01001010, b0100110x, b01001110 */ + /* unused encodings 0x4B,0x4F b01001011, b01001111 */ + ETM4_PKT_I_COND_RES_F3 = 0x50, /*!< b0101xxxx */ + /* unused encodings 0x60-0x67 b01100xxx */ + ETM4_PKT_I_COND_RES_F1 = 0x68, /*!< b011010xx, b0110111x 0x68-0x6B, 0x6e-0x6F */ + ETM4_PKT_I_COND_I_F1 = 0x6C, /*!< b01101100 */ + ETM4_PKT_I_COND_I_F3 = 0x6D, /*!< b01101101 */ + + // event trace + ETM4_PKT_I_IGNORE = 0x70, /*!< b01110000 */ + ETM4_PKT_I_EVENT = 0x71, /*!< b01110001 to 0x01111111 0x7F */ + + /* address and context */ + ETM4_PKT_I_CTXT = 0x80, /*!< b1000000x */ + ETM4_PKT_I_ADDR_CTXT_L_32IS0 = 0x82, /*!< b10000010 */ ETM4_PKT_I_ADDR_CTXT_L_32IS1, /*!< b10000011 */ - /* unused encoding b10000100 */ - ETM4_PKT_I_ADDR_CTXT_L_64IS0 = 0x85, /*!< b10000101 */ + /* unused encoding 0x84 b10000100 */ + ETM4_PKT_I_ADDR_CTXT_L_64IS0 = 0x85, /*!< b10000101 */ ETM4_PKT_I_ADDR_CTXT_L_64IS1, /*!< b10000110 */ - /* unused encoding b10000111 */ - ETM4_PKT_I_CTXT = 0x80, /*!< b1000000x */ - ETM4_PKT_I_ADDR_MATCH = 0x90, /*!< b10010000 to b10010010 */ - ETM4_PKT_I_ADDR_L_32IS0 = 0x9A, /*!< b10011010 */ - ETM4_PKT_I_ADDR_L_32IS1, /*!< b10011011 */ - /* unused encoding b10011100 */ - ETM4_PKT_I_ADDR_L_64IS0 = 0x9D, /*!< b10011101 */ - ETM4_PKT_I_ADDR_L_64IS1, /*!< b10011110 */ - /* unused encoding b10011111 */ - ETM4_PKT_I_ADDR_S_IS0 = 0x95, /*!< b10010101 */ + /* unused encoding 0x87 b10000111 */ + /* unused encodings 0x88-0x8F b10001xxx */ + ETM4_PKT_I_ADDR_MATCH = 0x90, /*!< b10010000 to b10010010 0x92 */ + /* unused encodings 0x93-0x94 b10010011 to b10010010 */ + ETM4_PKT_I_ADDR_S_IS0 = 0x95, /*!< b10010101 */ ETM4_PKT_I_ADDR_S_IS1, /*!< b10010110 */ - /* unused encoding b10010111 - unused encoding b10011000 - unused encoding b10011001 */ + /* unused encodings 0x97 b10010111 to b10011001 0x99 */ + ETM4_PKT_I_ADDR_L_32IS0 = 0x9A, /*!< b10011010 */ + ETM4_PKT_I_ADDR_L_32IS1, /*!< b10011011 */ + /* unused encoding 0x9C b10011100 */ + ETM4_PKT_I_ADDR_L_64IS0 = 0x9D, /*!< b10011101 */ + ETM4_PKT_I_ADDR_L_64IS1, /*!< b10011110 */ + /* unused encoding 0x9F b10011111 */ /* Q packets */ ETM4_PKT_I_Q = 0xA0, /*!< b1010xxxx */ + /* unused encodings 0xB0-0xBF b1011xxxx */ + /* Atom packets */ - ETM4_PKT_I_ATOM_F1 = 0xF6, /*!< b1111011x */ - ETM4_PKT_I_ATOM_F2 = 0xD8, /*!< b110110xx */ - ETM4_PKT_I_ATOM_F3 = 0xF8, //!< b11111xxx - ETM4_PKT_I_ATOM_F4 = 0xDC, //!< b110111xx - ETM4_PKT_I_ATOM_F5 = 0xD5, //!< b11010101 - b11010111, b11110101 - ETM4_PKT_I_ATOM_F6 = 0xC0, //!< b11000000 - b11010100, b11100000 - b11110100 + ETM4_PKT_I_ATOM_F6 = 0xC0, /*!< b11000000 - b11010100 0xC0 - 0xD4, b11100000 - b11110100 0xE0 - 0xF4 */ + ETM4_PKT_I_ATOM_F5 = 0xD5, /*!< b11010101 - b11010111 0xD5 - 0xD7, b11110101 0xF5 */ + ETM4_PKT_I_ATOM_F2 = 0xD8, /*!< b110110xx to 0xDB */ + ETM4_PKT_I_ATOM_F4 = 0xDC, /*!< b110111xx to 0xDF */ + ETM4_PKT_I_ATOM_F1 = 0xF6, /*!< b1111011x to 0xF7 */ + ETM4_PKT_I_ATOM_F3 = 0xF8, /*!< b11111xxx to 0xFF */ - /* conditional instruction tracing */ - ETM4_PKT_I_COND_FLUSH = 0x43, //!< b01000011 - ETM4_PKT_I_COND_I_F1 = 0x6C, //!< b01101100 - ETM4_PKT_I_COND_I_F2 = 0x40, //!< b01000000 - b01000010 - ETM4_PKT_I_COND_I_F3 = 0x6D, //!< b01101101 - ETM4_PKT_I_COND_RES_F1 = 0x68, //!< b0110111x, b011010xx - ETM4_PKT_I_COND_RES_F2 = 0x48, //!< b0100100x, b01001010, b0100110x, b01001110 - ETM4_PKT_I_COND_RES_F3 = 0x50, //!< b0101xxxx - ETM4_PKT_I_COND_RES_F4 = 0x44, //!< b0100010x, b01000110 - - /* cycle count packets */ - ETM4_PKT_I_CCNT_F1 = 0x0E, //!< b0000111x - ETM4_PKT_I_CCNT_F2 = 0x0C, //!< b0000110x - ETM4_PKT_I_CCNT_F3 = 0x10, //!< b0001xxxx - // data synchronisation markers - ETM4_PKT_I_NUM_DS_MKR = 0x20, //!< b00100xxx - ETM4_PKT_I_UNNUM_DS_MKR = 0x28, //!< b00101000 - b00101100 - // event trace - ETM4_PKT_I_EVENT = 0x70, //!< b0111xxxx - // Exceptions - ETM4_PKT_I_EXCEPT = 0x06, //!< b00000110 - ETM4_PKT_I_EXCEPT_RTN = 0x07, //!< b00000111 - // timestamp - ETM4_PKT_I_TIMESTAMP = 0x02, //!< b0000001x - // speculation - ETM4_PKT_I_CANCEL_F1 = 0x2E, //!< b0010111x - ETM4_PKT_I_CANCEL_F2 = 0x34, //!< b001101xx - ETM4_PKT_I_CANCEL_F3 = 0x38, //!< b00111xxx - ETM4_PKT_I_COMMIT = 0x2D, //!< b00101101 - ETM4_PKT_I_MISPREDICT = 0x30, //!< b001100xx - // Sync - ETM4_PKT_I_TRACE_INFO = 0x01, //!< b00000001 - ETM4_PKT_I_TRACE_ON = 0x04, //!< b00000100 // extension packets - follow 0x00 header ETM4_PKT_I_ASYNC = 0x100, //!< b00000000 ETM4_PKT_I_DISCARD = 0x103, //!< b00000011 - ETM4_PKT_I_OVERFLOW = 0x105 //!< b00000101 + ETM4_PKT_I_OVERFLOW = 0x105, //!< b00000101 } ocsd_etmv4_i_pkt_type; @@ -139,7 +154,7 @@ typedef union _etmv4_trace_info_t { uint32_t cc_enabled:1; //!< 1 if cycle count enabled uint32_t cond_enabled:3; //!< conditional trace enabeld type uint32_t p0_load:1; //!< 1 if tracing with P0 load elements (for data trace) - uint32_t p0_store:1; //1< 1 if tracing with P0 store elements (for data trace) + uint32_t p0_store:1; //!< 1 if tracing with P0 store elements (for data trace) } bits; //!< bitfields for trace info value. } etmv4_trace_info_t; @@ -259,6 +274,7 @@ typedef struct _ocsd_etmv4_i_pkt // original header type when packet type changed to error on decode error. ocsd_etmv4_i_pkt_type err_type; + uint8_t err_hdr_val; } ocsd_etmv4_i_pkt; @@ -342,6 +358,7 @@ typedef struct _ocsd_etmv4_cfg ocsd_core_profile_t core_prof; /**< Core Profile */ } ocsd_etmv4_cfg; + /** @}*/ /** @}*/ #endif // ARM_TRC_PKT_TYPES_ETMV4_H_INCLUDED diff --git a/decoder/include/opencsd/ocsd_if_types.h b/decoder/include/opencsd/ocsd_if_types.h index def16575f2b..7d74d77c240 100644 --- a/decoder/include/opencsd/ocsd_if_types.h +++ b/decoder/include/opencsd/ocsd_if_types.h @@ -107,6 +107,7 @@ typedef enum _ocsd_err_t { OCSD_ERR_DATA_DECODE_FATAL, /**< A decoder in the data path has returned a fatal error. */ /* frame deformatter errors */ OCSD_ERR_DFMTR_NOTCONTTRACE, /**< Trace input to deformatter none-continuous */ + OCSD_ERR_DFMTR_BAD_FHSYNC, /**< Bad frame or half frame sync in trace deformatter */ /* packet processor errors - protocol issues etc */ OCSD_ERR_BAD_PACKET_SEQ, /**< Bad packet sequence */ OCSD_ERR_INVALID_PCKT_HDR, /**< Invalid packet header */ @@ -126,6 +127,7 @@ typedef enum _ocsd_err_t { OCSD_ERR_MEM_ACC_FILE_NOT_FOUND, /**< Memory access file could not be opened */ OCSD_ERR_MEM_ACC_FILE_DIFF_RANGE, /**< Attempt to re-use the same memory access file for a different address range */ OCSD_ERR_MEM_ACC_RANGE_INVALID, /**< Address range in accessor set to invalid values */ + OCSD_ERR_MEM_ACC_BAD_LEN, /**< Memory accessor returned a bad read length value (larger than requested */ /* test errors - errors generated only by the test code, not the library */ OCSD_ERR_TEST_SNAPSHOT_PARSE, /**< test snapshot file parse error */ OCSD_ERR_TEST_SNAPSHOT_PARSE_INFO, /**< test snapshot file parse information */ @@ -137,7 +139,7 @@ typedef enum _ocsd_err_t { OCSD_ERR_DCDREG_TYPE_UNKNOWN, /**< attempted to find a decoder with a type that is not known in the library */ OCSD_ERR_DCDREG_TOOMANY, /**< attempted to register too many custom decoders */ /* decoder config */ - OCSD_ERR_DCD_INTERFACE_UNUSED, /**< Attempt to connect or use and inteface not supported by this decoder. */ + OCSD_ERR_DCD_INTERFACE_UNUSED, /**< Attempt to connect or use and interface not supported by this decoder. */ /* end marker*/ OCSD_ERR_LAST } ocsd_err_t; @@ -272,11 +274,16 @@ typedef enum _ocsd_dcd_tree_src_t { /** Core Architecture Version */ typedef enum _ocsd_arch_version { ARCH_UNKNOWN, /**< unknown architecture */ + ARCH_CUSTOM, /**< None ARM, custom architecture */ ARCH_V7, /**< V7 architecture */ ARCH_V8, /**< V8 architecture */ - ARCH_CUSTOM, /**< None ARM, custom architecture */ + ARCH_V8r3, /**< V8.3 architecture */ } ocsd_arch_version_t; +// macros for arch version comparisons. +#define OCSD_IS_V8_ARCH(arch) ((arch >= ARCH_V8) && (arch <= ARCH_V8r3)) +#define OCSD_MIN_V8_ARCH(arch) (arch >= ARCH_V8) + /** Core Profile */ typedef enum _ocsd_core_profile { profile_Unknown, /**< Unknown profile */ @@ -351,7 +358,8 @@ typedef enum _ocsd_instr_type { OCSD_INSTR_BR, /**< Immediate Branch instruction */ OCSD_INSTR_BR_INDIRECT, /**< Indirect Branch instruction */ OCSD_INSTR_ISB, /**< Barrier : ISB instruction */ - OCSD_INSTR_DSB_DMB /**< Barrier : DSB or DMB instruction */ + OCSD_INSTR_DSB_DMB, /**< Barrier : DSB or DMB instruction */ + OCSD_INSTR_WFI_WFE, /**< WFI or WFE traced as direct branch */ } ocsd_instr_type; /** instruction sub types - addiitonal information passed to the output packets @@ -362,6 +370,7 @@ typedef enum _ocsd_instr_subtype { OCSD_S_INSTR_BR_LINK, /**< branch with link */ OCSD_S_INSTR_V8_RET, /**< v8 ret instruction - subtype of BR_INDIRECT */ OCSD_S_INSTR_V8_ERET, /**< v8 eret instruction - subtype of BR_INDIRECT */ + OCSD_S_INSTR_V7_IMPLIED_RET, /**< v7 instruction which could imply return e.g. MOV PC, LR; POP { ,pc} */ } ocsd_instr_subtype; /** Instruction decode request structure. @@ -377,6 +386,7 @@ typedef struct _ocsd_instr_info { ocsd_vaddr_t instr_addr; /**< Input: Instruction address. */ uint32_t opcode; /**< Input: Opcode at address. 16 bit opcodes will use MS 16bits of parameter. */ uint8_t dsb_dmb_waypoints; /**< Input: DMB and DSB are waypoints */ + uint8_t wfi_wfe_branch; /**< Input: WFI, WFE classed as direct branches */ /* instruction decode info */ ocsd_instr_type type; /**< Decoder: Current instruction type. */ @@ -446,7 +456,31 @@ typedef enum _ocsd_mem_space_acc_t { * * @return uint32_t : Number of bytes actually read, or 0 for access error. */ -typedef uint32_t (* Fn_MemAcc_CB)(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer); +typedef uint32_t (* Fn_MemAcc_CB)(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer); + +/** +* Callback function definition for callback function memory accessor type. +* +* When using callback memory accessor, the decoder will call this function to obtain the +* memory at the address for the current opcodes. The memory space will represent the current +* exception level and security context of the traced code. +* +* Return the number of bytes read, which can be less than the amount requested if this would take the +* access address outside the range of addresses defined when this callback was registered with the decoder. +* +* Return 0 bytes if start address out of covered range, or memory space is not one of those defined as supported +* when the callback was registered. +* +* @param p_context : opaque context pointer set by callback client. +* @param address : start address of memory to be accessed +* @param mem_space : memory space of accessed memory (current EL & security state) +* @param trcID : Trace ID for source of trace - allow CB to client to associate mem req with source cpu. +* @param reqBytes : number of bytes required +* @param *byteBuffer : buffer for data. +* +* @return uint32_t : Number of bytes actually read, or 0 for access error. +*/ +typedef uint32_t (* Fn_MemAccID_CB)(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint8_t trcID, const uint32_t reqBytes, uint8_t *byteBuffer); /** memory region type for adding multi-region binary files to memory access interface */ diff --git a/decoder/include/ocsd_if_version.h b/decoder/include/opencsd/ocsd_if_version.h similarity index 88% rename from decoder/include/ocsd_if_version.h rename to decoder/include/opencsd/ocsd_if_version.h index 7d51ba27bdf..70c8df41f52 100644 --- a/decoder/include/ocsd_if_version.h +++ b/decoder/include/opencsd/ocsd_if_version.h @@ -43,17 +43,17 @@ /** @name Library Versioning @{*/ #define OCSD_VER_MAJOR 0x0 /**< Library Major Version */ -#define OCSD_VER_MINOR 0x8 /**< Library Minor Version */ -#define OCSD_VER_PATCH 0x2 /**< Library Patch Version */ +#define OCSD_VER_MINOR 0xC /**< Library Minor Version */ +#define OCSD_VER_PATCH 0x0 /**< Library Patch Version */ /** Library version number - MMMMnnpp format. MMMM = major version, nn = minor version, pp = patch version */ -#define OCSD_VER_NUM (((uint32_t)OCSD_VER_MAJOR << 16) | ((uint32_t)OCSD_VER_MINOR << 8) | ((uint32_t)OCSD_VER_PATCH)) +#define OCSD_VER_NUM ((OCSD_VER_MAJOR << 16) | (OCSD_VER_MINOR << 8) | OCSD_VER_PATCH) -#define OCSD_VER_STRING "0.8.2" /**< Library Version string */ +#define OCSD_VER_STRING "0.12.0" /**< Library Version string */ #define OCSD_LIB_NAME "OpenCSD Library" /**< Library name string */ #define OCSD_LIB_SHORT_NAME "OCSD" /**< Library Short name string */ /** @}*/ diff --git a/decoder/include/opencsd/trc_gen_elem_types.h b/decoder/include/opencsd/trc_gen_elem_types.h index 3766785dbd6..1d77b53cf8f 100644 --- a/decoder/include/opencsd/trc_gen_elem_types.h +++ b/decoder/include/opencsd/trc_gen_elem_types.h @@ -58,7 +58,7 @@ typedef enum _ocsd_gen_trc_elem_t OCSD_GEN_TRC_ELEM_EXCEPTION_RET, /*!< expection return */ OCSD_GEN_TRC_ELEM_TIMESTAMP, /*!< Timestamp - preceding elements happeded before this time. */ OCSD_GEN_TRC_ELEM_CYCLE_COUNT, /*!< Cycle count - cycles since last cycle count value - associated with a preceding instruction range. */ - OCSD_GEN_TRC_ELEM_EVENT, /*!< Event - trigger, (TBC - perhaps have a set of event types - cut down additional processing?) */ + OCSD_GEN_TRC_ELEM_EVENT, /*!< Event - trigger or numbered event */ OCSD_GEN_TRC_ELEM_SWTRACE, /*!< Software trace packet - may contain data payload. */ OCSD_GEN_TRC_ELEM_CUSTOM, /*!< Fully custom packet type - used by none-ARM architecture decoders */ } ocsd_gen_trc_elem_t; @@ -90,12 +90,15 @@ typedef struct _ocsd_generic_trace_elem { union { struct { uint32_t last_instr_exec:1; /**< 1 if last instruction in range was executed; */ + uint32_t last_instr_sz:3; /**< size of last instruction in bytes (2/4) */ uint32_t has_cc:1; /**< 1 if this packet has a valid cycle count included (e.g. cycle count included as part of instruction range packet, always 1 for pure cycle count packet.*/ uint32_t cpu_freq_change:1; /**< 1 if this packet indicates a change in CPU frequency */ uint32_t excep_ret_addr:1; /**< 1 if en_addr is the preferred exception return address on exception packet type */ uint32_t excep_data_marker:1; /**< 1 if the exception entry packet is a data push marker only, with no address information (used typically in v7M trace for marking data pushed onto stack) */ uint32_t extended_data:1; /**< 1 if the packet extended data pointer is valid. Allows packet extensions for custom decoders, or additional data payloads for data trace. */ uint32_t has_ts:1; /**< 1 if the packet has an associated timestamp - e.g. SW/STM trace TS+Payload as a single packet */ + uint32_t last_instr_cond:1; /**< 1 if the last instruction was conditional */ + uint32_t excep_ret_addr_br_tgt:1; /**< 1 if exception return address (en_addr) is also the target of a taken branch addr from the previous range. */ }; uint32_t flag_bits; }; @@ -105,7 +108,8 @@ typedef struct _ocsd_generic_trace_elem { uint32_t exception_number; /**< exception number for exception type packets */ trace_event_t trace_event; /**< Trace event - trigger etc */ trace_on_reason_t trace_on_reason; /**< reason for the trace on packet */ - ocsd_swt_info_t sw_trace_info; /**< software trace packet info */ + ocsd_swt_info_t sw_trace_info; /**< software trace packet info */ + uint32_t num_instr_range; /**< number of instructions covered by range packet (for T32 this cannot be calculated from en-st/i_size) */ }; const void *ptr_extended_data; /**< pointer to extended data buffer (data trace, sw trace payload) / custom structure */ diff --git a/decoder/include/pkt_printers/pkt_printer_t.h b/decoder/include/pkt_printers/pkt_printer_t.h index fc3ad2a78e5..9eb37f4e283 100644 --- a/decoder/include/pkt_printers/pkt_printer_t.h +++ b/decoder/include/pkt_printers/pkt_printer_t.h @@ -39,7 +39,7 @@ #include #include -#include +//#include #include template diff --git a/decoder/source/c_api/ocsd_c_api.cpp b/decoder/source/c_api/ocsd_c_api.cpp index 1a2a74f899b..4824c427e3d 100644 --- a/decoder/source/c_api/ocsd_c_api.cpp +++ b/decoder/source/c_api/ocsd_c_api.cpp @@ -74,7 +74,7 @@ static std::map s_data_map; /* C API functions */ /*******************************************************************************/ -/** Get Library version. Return a 32 bit version in form MMMMnnpp - MMMM = major verison, nn = minor version, pp = patch version */ +/** Get Library version. Return a 32 bit version in form MMMMnnpp - MMMM = major version, nn = minor version, pp = patch version */ OCSD_C_API uint32_t ocsd_get_version(void) { return ocsdVersion::vers_num(); @@ -404,6 +404,17 @@ OCSD_C_API ocsd_err_t ocsd_dt_add_callback_mem_acc(const dcd_tree_handle_t handl return err; } +OCSD_C_API ocsd_err_t ocsd_dt_add_callback_trcid_mem_acc(const dcd_tree_handle_t handle, const ocsd_vaddr_t st_address, const ocsd_vaddr_t en_address, const ocsd_mem_space_acc_t mem_space, Fn_MemAccID_CB p_cb_func, const void *p_context) +{ + ocsd_err_t err = OCSD_OK; + DecodeTree *pDT; + err = ocsd_check_and_add_mem_acc_mapper(handle, &pDT); + if (err == OCSD_OK) + err = pDT->addCallbackIDMemAcc(st_address, en_address, mem_space, p_cb_func, p_context); + return err; +} + + OCSD_C_API ocsd_err_t ocsd_dt_remove_mem_acc(const dcd_tree_handle_t handle, const ocsd_vaddr_t st_address, const ocsd_mem_space_acc_t mem_space) { ocsd_err_t err = OCSD_OK; diff --git a/decoder/source/etmv3/trc_pkt_decode_etmv3.cpp b/decoder/source/etmv3/trc_pkt_decode_etmv3.cpp index bd7cb60b5b6..0a15a33c42f 100644 --- a/decoder/source/etmv3/trc_pkt_decode_etmv3.cpp +++ b/decoder/source/etmv3/trc_pkt_decode_etmv3.cpp @@ -598,7 +598,8 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV3::processPHdr() pElem->setAddrRange(m_IAddr,m_code_follower.getRangeEn()); pElem->setLastInstrInfo(atoms.getCurrAtomVal() == ATOM_E, m_code_follower.getInstrType(), - m_code_follower.getInstrSubType()); + m_code_follower.getInstrSubType(),m_code_follower.getInstrSize()); + pElem->setLastInstrCond(m_code_follower.isCondInstr()); pElem->setISA(isa); if(m_code_follower.hasNextAddr()) m_IAddr = m_code_follower.getNextAddr(); diff --git a/decoder/source/etmv4/trc_cmp_cfg_etmv4.cpp b/decoder/source/etmv4/trc_cmp_cfg_etmv4.cpp index 7db0fa61f96..9f5b37396b4 100644 --- a/decoder/source/etmv4/trc_cmp_cfg_etmv4.cpp +++ b/decoder/source/etmv4/trc_cmp_cfg_etmv4.cpp @@ -75,6 +75,8 @@ void EtmV4Config::PrivateInit() m_VMIDSize = 0; m_condTraceCalc = false; m_CondTrace = COND_TR_DIS; + m_MajVer = (uint8_t)((m_cfg.reg_idr1 >> 8) & 0xF); + m_MinVer = (uint8_t)((m_cfg.reg_idr1 >> 4) & 0xF); } void EtmV4Config::CalcQSupp() diff --git a/decoder/source/etmv4/trc_etmv4_stack_elem.cpp b/decoder/source/etmv4/trc_etmv4_stack_elem.cpp index ace0ac932b7..8916c7dc350 100644 --- a/decoder/source/etmv4/trc_etmv4_stack_elem.cpp +++ b/decoder/source/etmv4/trc_etmv4_stack_elem.cpp @@ -36,11 +36,17 @@ #include "opencsd/etmv4/trc_etmv4_stack_elem.h" /* implementation of P0 element stack in ETM v4 trace*/ -TrcStackElemParam *EtmV4P0Stack::createParamElemNoParam(const p0_elem_t p0_type, const bool isP0, const ocsd_etmv4_i_pkt_type root_pkt, const ocsd_trc_index_t root_index) +TrcStackElem *EtmV4P0Stack::createParamElemNoParam(const p0_elem_t p0_type, const bool isP0, const ocsd_etmv4_i_pkt_type root_pkt, const ocsd_trc_index_t root_index, bool back /*= false*/) { - std::vector params; - params.clear(); - return createParamElem(p0_type, isP0, root_pkt, root_index, params); + TrcStackElem *pElem = new (std::nothrow) TrcStackElem(p0_type, isP0, root_pkt, root_index); + if (pElem) + { + if (back) + push_back(pElem); + else + push_front(pElem); + } + return pElem; } TrcStackElemParam *EtmV4P0Stack::createParamElem(const p0_elem_t p0_type, const bool isP0, const ocsd_etmv4_i_pkt_type root_pkt, const ocsd_trc_index_t root_index, const std::vector ¶ms) diff --git a/decoder/source/etmv4/trc_pkt_decode_etmv4i.cpp b/decoder/source/etmv4/trc_pkt_decode_etmv4i.cpp index 865aacbdb2a..2eb6cbc3f5b 100644 --- a/decoder/source/etmv4/trc_pkt_decode_etmv4i.cpp +++ b/decoder/source/etmv4/trc_pkt_decode_etmv4i.cpp @@ -128,7 +128,7 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::onFlush() ocsd_datapath_resp_t resp = OCSD_RESP_CONT; // continue exception processing (can't go through processPacket as elements no longer on stack) - if(m_excep_proc != EXCEP_POP) + if(m_excep_info.proc != EXCEP_POP) resp = processException(); // continue ongoing output operations on comitted elements. else if(m_curr_state == COMMIT_ELEM) @@ -151,6 +151,7 @@ ocsd_err_t TrcPktDecodeEtmV4I::onProtocolConfig() // set up static trace instruction decode elements m_instr_info.dsb_dmb_waypoints = 0; + m_instr_info.wfi_wfe_branch = m_config->wfiwfeBranch() ? 1 : 0; m_instr_info.pe_type.arch = m_config->archVersion(); m_instr_info.pe_type.profile = m_config->coreProfile(); @@ -233,7 +234,7 @@ void TrcPktDecodeEtmV4I::resetDecoder() m_prev_overflow = false; m_P0_stack.delete_all(); m_output_elem.init(); - m_excep_proc = EXCEP_POP; + m_excep_info.proc = EXCEP_POP; m_flush_EOT = false; } @@ -250,6 +251,7 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::decodePacket(bool &Complete) switch(m_curr_packet_in->getType()) { case ETM4_PKT_I_ASYNC: // nothing to do with this packet. + case ETM4_PKT_I_IGNORE: // or this one. break; case ETM4_PKT_I_TRACE_INFO: @@ -350,6 +352,21 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::decodePacket(bool &Complete) bool bV7MProfile = (m_config->archVersion() == ARCH_V7) && (m_config->coreProfile() == profile_CortexM); if (m_P0_stack.createParamElemNoParam(P0_EXCEP_RET, bV7MProfile, m_curr_packet_in->getType(), m_index_curr_pkt) == 0) bAllocErr = true; + else if (bV7MProfile) + m_curr_spec_depth++; + } + break; + + case ETM4_PKT_I_FUNC_RET: + { + // P0 element iff V8M profile, otherwise ignore + if (OCSD_IS_V8_ARCH(m_config->archVersion()) && (m_config->coreProfile() == profile_CortexM)) + { + if (m_P0_stack.createParamElemNoParam(P0_FUNC_RET, true, m_curr_packet_in->getType(), m_index_curr_pkt) == 0) + bAllocErr = true; + else + m_curr_spec_depth++; + } } break; @@ -371,7 +388,7 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::decodePacket(bool &Complete) { std::vector params = { 0 }; params[0] = m_curr_packet_in->getCC(); - if (m_P0_stack.createParamElem(P0_EVENT, false, m_curr_packet_in->getType(), m_index_curr_pkt, params) == 0) + if (m_P0_stack.createParamElem(P0_CC, false, m_curr_packet_in->getType(), m_index_curr_pkt, params) == 0) bAllocErr = true; } @@ -387,7 +404,7 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::decodePacket(bool &Complete) params[1] = (uint32_t)((ts >> 32) & 0xFFFFFFFF); if (bTSwithCC) params[2] = m_curr_packet_in->getCC(); - if (m_P0_stack.createParamElem(P0_EVENT, false, m_curr_packet_in->getType(), m_index_curr_pkt, params) == 0) + if (m_P0_stack.createParamElem(bTSwithCC ? P0_TS_CC : P0_TS, false, m_curr_packet_in->getType(), m_index_curr_pkt, params) == 0) bAllocErr = true; } @@ -490,6 +507,7 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::commitElements(bool &Complete) bool bPause = false; // pause commit operation bool bPopElem = true; // do we remove the element from the stack (multi atom elements may need to stay!) int num_commit_req = m_P0_commit; + ocsd_trc_index_t err_idx = 0; Complete = true; // assume we exit due to completion of commit operation @@ -500,8 +518,9 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::commitElements(bool &Complete) if(m_P0_stack.size() > 0) { pElem = m_P0_stack.back(); // get oldest element - - switch(pElem->getP0Type()) + err_idx = pElem->getRootIndex(); // save index in case of error. + + switch (pElem->getP0Type()) { // indicates a trace restart - beginning of trace or discontinuiuty case P0_TRC_ON: @@ -612,7 +631,7 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::commitElements(bool &Complete) if ((resp = returnStackPop()) != OCSD_RESP_CONT) break; - m_excep_proc = EXCEP_POP; // set state in case we need to stop part way through + m_excep_info.proc = EXCEP_POP; // set state in case we need to stop part way through resp = processException(); // output trace + exception elements. m_P0_commit--; break; @@ -623,6 +642,13 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::commitElements(bool &Complete) if(pElem->isP0()) // are we on a core that counts ERET as P0? m_P0_commit--; break; + + case P0_FUNC_RET: + // func ret is V8M - data trace only - hint that data has been popped off the stack. + // at this point nothing to do till the decoder starts handling data trace. + if (pElem->isP0()) + m_P0_commit--; + break; } if(bPopElem) @@ -637,10 +663,6 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::commitElements(bool &Complete) else { // too few elements for commit operation - decode error. - ocsd_trc_index_t err_idx = 0; - if(pElem) - err_idx = pElem->getRootIndex(); - resp = OCSD_RESP_FATAL_INVALID_DATA; LogError(ocsdError(OCSD_ERR_SEV_ERROR,OCSD_ERR_COMMIT_PKT_OVERRUN,err_idx,m_CSID,"Not enough elements to commit")); bPause = true; @@ -757,7 +779,7 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::flushEOT() ocsd_datapath_resp_t TrcPktDecodeEtmV4I::outputCC(TrcStackElemParam *pParamElem) { m_output_elem.setType(OCSD_GEN_TRC_ELEM_CYCLE_COUNT); - m_output_elem.cycle_count = pParamElem->getParam(0); + m_output_elem.setCycleCount(pParamElem->getParam(0)); return outputTraceElementIdx(pParamElem->getRootIndex(),m_output_elem); } @@ -778,6 +800,17 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::outputEvent(TrcStackElemParam *pParamEl return outputTraceElementIdx(pParamElem->getRootIndex(),m_output_elem); } +ocsd_datapath_resp_t TrcPktDecodeEtmV4I::outputTraceRange(const bool executed, ocsd_trc_index_t index) +{ + m_output_elem.setType(OCSD_GEN_TRC_ELEM_INSTR_RANGE); + m_output_elem.setLastInstrInfo(executed, m_instr_info.type, m_instr_info.sub_type, m_instr_info.instr_size); + m_output_elem.setISA(m_instr_info.isa); + m_output_elem.setLastInstrCond(m_instr_info.is_conditional); + if (executed) + m_instr_info.isa = m_instr_info.next_isa; + return outputTraceElementIdx(index, m_output_elem); +} + ocsd_datapath_resp_t TrcPktDecodeEtmV4I::processAtom(const ocsd_atm_val atom, bool &bCont) { ocsd_datapath_resp_t resp = OCSD_RESP_CONT; @@ -834,10 +867,7 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::processAtom(const ocsd_atm_val atom, bo } break; } - m_output_elem.setType(OCSD_GEN_TRC_ELEM_INSTR_RANGE); - m_output_elem.setLastInstrInfo((atom == ATOM_E),m_instr_info.type, m_instr_info.sub_type); - m_output_elem.setISA(m_instr_info.isa); - resp = outputTraceElementIdx(pElem->getRootIndex(),m_output_elem); + resp = outputTraceRange((atom == ATOM_E), pElem->getRootIndex()); } else @@ -848,10 +878,7 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::processAtom(const ocsd_atm_val atom, bo if(m_output_elem.st_addr != m_output_elem.en_addr) { // some trace before we were out of memory access range - m_output_elem.setType(OCSD_GEN_TRC_ELEM_INSTR_RANGE); - m_output_elem.setLastInstrInfo(true,m_instr_info.type, m_instr_info.sub_type); - m_output_elem.setISA(m_instr_info.isa); - resp = outputTraceElementIdx(pElem->getRootIndex(),m_output_elem); + resp = outputTraceRange(true, pElem->getRootIndex()); } if(m_mem_nacc_pending && OCSD_DATA_RESP_IS_CONT(resp)) @@ -873,11 +900,13 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::processAtom(const ocsd_atm_val atom, bo ocsd_datapath_resp_t TrcPktDecodeEtmV4I::processException() { ocsd_datapath_resp_t resp = OCSD_RESP_CONT; - bool excep_implied_P0 = false; //!< exception implies P0 + TrcStackElemExcept *pExceptElem; - if(m_excep_proc == EXCEP_POP) + m_excep_info.addr_b_tgt = false; + + if(m_excep_info.proc == EXCEP_POP) { - TrcStackElemExcept *pExceptElem = dynamic_cast(m_P0_stack.back()); // get the exception element + pExceptElem = dynamic_cast(m_P0_stack.back()); // get the exception element TrcStackElemAddr *pAddressElem = 0; TrcStackElemCtxt *pCtxtElem = 0; TrcStackElem *pElem = 0; @@ -902,32 +931,52 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::processException() // extract address pAddressElem = static_cast(pElem); - m_excep_addr = pAddressElem->getAddr(); + // fill in exception info for use later + m_excep_info.addr = pAddressElem->getAddr(); + m_excep_info.number = pExceptElem->getExcepNum(); + m_excep_info.index = pExceptElem->getRootIndex(); + m_excep_info.addr_b_tgt = pExceptElem->getPrevSame(); - // if we have context, get that. - if(pCtxtElem) - updateContext(pCtxtElem); - - // record the exception number - m_output_elem.exception_number = pExceptElem->getExcepNum(); - - // see if there is an implied P0 element on the exception. - excep_implied_P0 = pExceptElem->getPrevSame(); - - // save the trace index. - m_excep_index = pExceptElem->getRootIndex(); + // see if there is an address + optional context element implied + // prior to the exception. + if (m_excep_info.addr_b_tgt) + { + // this was a branch target address - update current setting + bool b64bit = m_instr_info.isa == ocsd_isa_aarch64; + if (pCtxtElem) { + b64bit = pCtxtElem->getContext().SF; + } + m_instr_info.instr_addr = m_excep_info.addr.val; + m_instr_info.isa = (m_excep_info.addr.isa == 0) ? + (b64bit ? ocsd_isa_aarch64 : ocsd_isa_arm) : ocsd_isa_thumb2; + m_need_addr = false; + } // figure out next move - if(m_excep_addr.val == m_instr_info.instr_addr) - m_excep_proc = EXCEP_EXCEP; + if (pCtxtElem) { + m_excep_info.proc = EXCEP_CTXT; + updateContext(pCtxtElem); + } + else if(m_excep_info.addr.val == m_instr_info.instr_addr) + m_excep_info.proc = EXCEP_EXCEP; else - m_excep_proc = EXCEP_RANGE; + m_excep_info.proc = EXCEP_RANGE; } m_P0_stack.delete_popped(); } + // output a context element + if (m_excep_info.proc == EXCEP_CTXT) + { + m_output_elem.setType(OCSD_GEN_TRC_ELEM_PE_CONTEXT); + resp = outputTraceElementIdx(m_excep_info.index, m_output_elem); + m_excep_info.proc = EXCEP_EXCEP; + if (!OCSD_DATA_RESP_IS_CONT(resp)) + return resp; + } + // output a range element - if(m_excep_proc == EXCEP_RANGE) + if(m_excep_info.proc == EXCEP_RANGE) { bool bWPFound = false; ocsd_err_t err; @@ -935,8 +984,8 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::processException() // last instr_info address is the start address m_output_elem.st_addr = m_instr_info.instr_addr; - // look for either a WP or match to return address. - err = traceInstrToWP(bWPFound,!excep_implied_P0,m_excep_addr.val); + // look for match to return address. + err = traceInstrToWP(bWPFound,true,m_excep_info.addr.val); if(err != OCSD_OK) { @@ -944,37 +993,21 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::processException() { m_need_addr = true; m_need_ctxt = true; - LogError(ocsdError(OCSD_ERR_SEV_WARN,err,m_excep_index,m_CSID,"Warning: unsupported instruction set processing exception packet.")); + LogError(ocsdError(OCSD_ERR_SEV_WARN,err,m_excep_info.index,m_CSID,"Warning: unsupported instruction set processing exception packet.")); } else { resp = OCSD_RESP_FATAL_INVALID_DATA; - LogError(ocsdError(OCSD_ERR_SEV_ERROR,err,m_excep_index,m_CSID,"Error processing exception packet.")); - m_excep_proc = EXCEP_POP; // nothing more to do, reset to start of exception handling + LogError(ocsdError(OCSD_ERR_SEV_ERROR,err,m_excep_info.index,m_CSID,"Error processing exception packet.")); + m_excep_info.proc = EXCEP_POP; // nothing more to do, reset to start of exception handling } } if(bWPFound) { - // action according to waypoint type and atom value - if(excep_implied_P0) - { - switch(m_instr_info.type) - { - case OCSD_INSTR_BR: - m_instr_info.instr_addr = m_instr_info.branch_addr; - break; - - case OCSD_INSTR_BR_INDIRECT: - m_instr_info.instr_addr = m_excep_addr.val; - break; - } - } - m_output_elem.setType(OCSD_GEN_TRC_ELEM_INSTR_RANGE); - m_output_elem.setLastInstrInfo(true,m_instr_info.type, m_instr_info.sub_type); - m_output_elem.setISA(m_instr_info.isa); - resp = outputTraceElementIdx(m_excep_index, m_output_elem); - m_excep_proc = EXCEP_EXCEP; + // waypoint address found - output range + resp = outputTraceRange(true, m_excep_info.index); + m_excep_info.proc = EXCEP_EXCEP; } else { @@ -984,34 +1017,33 @@ ocsd_datapath_resp_t TrcPktDecodeEtmV4I::processException() if(m_output_elem.st_addr != m_output_elem.en_addr) { // some trace before we were out of memory access range - m_output_elem.setType(OCSD_GEN_TRC_ELEM_INSTR_RANGE); - m_output_elem.setLastInstrInfo(true,m_instr_info.type, m_instr_info.sub_type); - m_output_elem.setISA(m_instr_info.isa); - resp = outputTraceElementIdx(m_excep_index,m_output_elem); + resp = outputTraceRange(true, m_excep_info.index); } - m_excep_proc = m_mem_nacc_pending ? EXCEP_NACC : EXCEP_EXCEP; + m_excep_info.proc = m_mem_nacc_pending ? EXCEP_NACC : EXCEP_EXCEP; } } - if((m_excep_proc == EXCEP_NACC) && OCSD_DATA_RESP_IS_CONT(resp)) + if((m_excep_info.proc == EXCEP_NACC) && OCSD_DATA_RESP_IS_CONT(resp)) { m_output_elem.setType(OCSD_GEN_TRC_ELEM_ADDR_NACC); m_output_elem.st_addr = m_nacc_addr; - resp = outputTraceElementIdx(m_excep_index,m_output_elem); - m_excep_proc = EXCEP_EXCEP; + resp = outputTraceElementIdx(m_excep_info.index,m_output_elem); + m_excep_info.proc = EXCEP_EXCEP; m_mem_nacc_pending = false; } - if((m_excep_proc == EXCEP_EXCEP) && OCSD_DATA_RESP_IS_CONT(resp)) + if((m_excep_info.proc == EXCEP_EXCEP) && OCSD_DATA_RESP_IS_CONT(resp)) { // output element. m_output_elem.setType(OCSD_GEN_TRC_ELEM_EXCEPTION); // add end address as preferred return address to end addr in element - m_output_elem.en_addr = m_excep_addr.val; + m_output_elem.en_addr = m_excep_info.addr.val; m_output_elem.excep_ret_addr = 1; - resp = outputTraceElementIdx(m_excep_index,m_output_elem); - m_excep_proc = EXCEP_POP; + m_output_elem.excep_ret_addr_br_tgt = m_excep_info.addr_b_tgt; + m_output_elem.exception_number = m_excep_info.number; + resp = outputTraceElementIdx(m_excep_info.index,m_output_elem); + m_excep_info.proc = EXCEP_POP; } return resp; } @@ -1036,6 +1068,7 @@ ocsd_err_t TrcPktDecodeEtmV4I::traceInstrToWP(bool &bWPFound, const bool traceTo ocsd_mem_space_acc_t mem_space = m_is_secure ? OCSD_MEM_SPACE_S : OCSD_MEM_SPACE_N; m_output_elem.st_addr = m_output_elem.en_addr = m_instr_info.instr_addr; + m_output_elem.num_instr_range = 0; bWPFound = false; @@ -1057,6 +1090,7 @@ ocsd_err_t TrcPktDecodeEtmV4I::traceInstrToWP(bool &bWPFound, const bool traceTo // update the range decoded address in the output packet. m_output_elem.en_addr = m_instr_info.instr_addr; + m_output_elem.num_instr_range++; // either walking to match the next instruction address or a real watchpoint if(traceToAddrNext) @@ -1068,7 +1102,7 @@ ocsd_err_t TrcPktDecodeEtmV4I::traceInstrToWP(bool &bWPFound, const bool traceTo { // not enough memory accessible. m_mem_nacc_pending = true; - m_nacc_addr = m_instr_info.instr_addr; + m_nacc_addr = m_instr_info.instr_addr; } } return err; diff --git a/decoder/source/etmv4/trc_pkt_elem_etmv4i.cpp b/decoder/source/etmv4/trc_pkt_elem_etmv4i.cpp index 0761f7aa64f..3f9d534db82 100644 --- a/decoder/source/etmv4/trc_pkt_elem_etmv4i.cpp +++ b/decoder/source/etmv4/trc_pkt_elem_etmv4i.cpp @@ -91,6 +91,7 @@ void EtmV4ITrcPacket::toString(std::string &str) const { case ETM4_PKT_I_BAD_SEQUENCE: case ETM4_PKT_I_INCOMPLETE_EOT: + case ETM4_PKT_I_RESERVED_CFG: name = packetTypeName(err_type, 0); str += "[" + (std::string)name + "]"; break; @@ -185,28 +186,16 @@ void EtmV4ITrcPacket::toStringFmt(const uint32_t fmtFlags, std::string &str) con const char *EtmV4ITrcPacket::packetTypeName(const ocsd_etmv4_i_pkt_type type, const char **ppDesc) const { - const char *pName = "I_RESERVED"; - const char *pDesc = "Reserved Packet Header"; + const char *pName = "I_UNKNOWN"; + const char *pDesc = "Unknown Packet Header"; switch(type) { - case ETM4_PKT_I_RESERVED: break; // default; - case ETM4_PKT_I_NOTSYNC: pName = "I_NOT_SYNC"; pDesc = "I Stream not synchronised"; break; - case ETM4_PKT_I_BAD_SEQUENCE: - pName = "I_BAD_SEQUENCE"; - pDesc = "Invalid Sequence in packet."; - break; - - case ETM4_PKT_I_BAD_TRACEMODE: - pName = "I_BAD_TRACEMODE"; - pDesc = "Invalid Packet for trace mode."; - break; - case ETM4_PKT_I_INCOMPLETE_EOT: pName = "I_INCOMPLETE_EOT"; pDesc = "Incomplete packet at end of trace."; @@ -217,144 +206,59 @@ const char *EtmV4ITrcPacket::packetTypeName(const ocsd_etmv4_i_pkt_type type, co pDesc = "No Error Type."; break; + case ETM4_PKT_I_BAD_SEQUENCE: + pName = "I_BAD_SEQUENCE"; + pDesc = "Invalid Sequence in packet."; + break; + + case ETM4_PKT_I_BAD_TRACEMODE: + pName = "I_BAD_TRACEMODE"; + pDesc = "Invalid Packet for trace mode."; + break; + + case ETM4_PKT_I_RESERVED: + pName = "I_RESERVED"; + pDesc = "Reserved Packet Header"; + break; + + case ETM4_PKT_I_RESERVED_CFG: + pName = "I_RESERVED_CFG"; + pDesc = "Reserved header for current configuration."; + break; + case ETM4_PKT_I_EXTENSION: pName = "I_EXTENSION"; - pDesc = "Extention packet header."; + pDesc = "Extension packet header."; break; - case ETM4_PKT_I_ADDR_CTXT_L_32IS0: - pName = "I_ADDR_CTXT_L_32IS0"; - pDesc = "Address & Context, Long, 32 bit, IS0."; + case ETM4_PKT_I_TRACE_INFO: + pName = "I_TRACE_INFO"; + pDesc = "Trace Info."; break; - case ETM4_PKT_I_ADDR_CTXT_L_32IS1: - pName = "I_ADDR_CTXT_L_32IS1"; - pDesc = "Address & Context, Long, 32 bit, IS0."; + case ETM4_PKT_I_TIMESTAMP: + pName = "I_TIMESTAMP"; + pDesc = "Timestamp."; break; - case ETM4_PKT_I_ADDR_CTXT_L_64IS0: - pName = "I_ADDR_CTXT_L_64IS0"; - pDesc = "Address & Context, Long, 64 bit, IS0."; + case ETM4_PKT_I_TRACE_ON: + pName = "I_TRACE_ON"; + pDesc = "Trace On."; break; - case ETM4_PKT_I_ADDR_CTXT_L_64IS1: - pName = "I_ADDR_CTXT_L_64IS1"; - pDesc = "Address & Context, Long, 64 bit, IS1."; + case ETM4_PKT_I_FUNC_RET: + pName = "I_FUNC_RET"; + pDesc = "V8M - function return."; break; - case ETM4_PKT_I_CTXT: - pName = "I_CTXT"; - pDesc = "Context Packet."; + case ETM4_PKT_I_EXCEPT: + pName = "I_EXCEPT"; + pDesc = "Exception."; break; - case ETM4_PKT_I_ADDR_MATCH: - pName = "I_ADDR_MATCH"; - pDesc = "Exact Address Match."; - break; - - case ETM4_PKT_I_ADDR_L_32IS0: - pName = "I_ADDR_L_32IS0"; - pDesc = "Address, Long, 32 bit, IS0."; - break; - - case ETM4_PKT_I_ADDR_L_32IS1: - pName = "I_ADDR_L_32IS1"; - pDesc = "Address, Long, 32 bit, IS1."; - break; - - case ETM4_PKT_I_ADDR_L_64IS0: - pName = "I_ADDR_L_64IS0"; - pDesc = "Address, Long, 64 bit, IS0."; - break; - - case ETM4_PKT_I_ADDR_L_64IS1: - pName = "I_ADDR_L_64IS1"; - pDesc = "Address, Long, 64 bit, IS1."; - break; - - case ETM4_PKT_I_ADDR_S_IS0: - pName = "I_ADDR_S_IS0"; - pDesc = "Address, Short, IS0."; - break; - - case ETM4_PKT_I_ADDR_S_IS1: - pName = "I_ADDR_S_IS1"; - pDesc = "Address, Short, IS1."; - break; - - case ETM4_PKT_I_Q: - pName = "I_Q"; - pDesc = "Q Packet."; - break; - - case ETM4_PKT_I_ATOM_F1: - pName = "I_ATOM_F1"; - pDesc = "Atom format 1."; - break; - - case ETM4_PKT_I_ATOM_F2: - pName = "I_ATOM_F2"; - pDesc = "Atom format 2."; - break; - - case ETM4_PKT_I_ATOM_F3: - pName = "I_ATOM_F3"; - pDesc = "Atom format 3."; - break; - - case ETM4_PKT_I_ATOM_F4: - pName = "I_ATOM_F4"; - pDesc = "Atom format 4."; - break; - - case ETM4_PKT_I_ATOM_F5: - pName = "I_ATOM_F5"; - pDesc = "Atom format 5."; - break; - - case ETM4_PKT_I_ATOM_F6: - pName = "I_ATOM_F6"; - pDesc = "Atom format 6."; - break; - - case ETM4_PKT_I_COND_FLUSH: - pName = "I_COND_FLUSH"; - pDesc = "Conditional Flush."; - break; - - case ETM4_PKT_I_COND_I_F1: - pName = "I_COND_I_F1"; - pDesc = "Conditional Instruction, format 1."; - break; - - case ETM4_PKT_I_COND_I_F2: - pName = "I_COND_I_F2"; - pDesc = "Conditional Instruction, format 2."; - break; - - case ETM4_PKT_I_COND_I_F3: - pName = "I_COND_I_F3"; - pDesc = "Conditional Instruction, format 3."; - break; - - case ETM4_PKT_I_COND_RES_F1: - pName = "I_COND_RES_F1"; - pDesc = "Conditional Result, format 1."; - break; - - case ETM4_PKT_I_COND_RES_F2: - pName = "I_COND_RES_F2"; - pDesc = "Conditional Result, format 2."; - break; - - case ETM4_PKT_I_COND_RES_F3: - pName = "I_COND_RES_F3"; - pDesc = "Conditional Result, format 3."; - break; - - case ETM4_PKT_I_COND_RES_F4: - pName = "I_COND_RES_F4"; - pDesc = "Conditional Result, format 4."; + case ETM4_PKT_I_EXCEPT_RTN: + pName = "I_EXCEPT_RTN"; + pDesc = "Exception Return."; break; case ETM4_PKT_I_CCNT_F1: @@ -382,30 +286,21 @@ const char *EtmV4ITrcPacket::packetTypeName(const ocsd_etmv4_i_pkt_type type, co pDesc = "Data Synchronisation Marker - Unnumbered."; break; - case ETM4_PKT_I_EVENT: - pName = "I_EVENT"; - pDesc = "Trace Event."; - break; - - case ETM4_PKT_I_EXCEPT: - pName = "I_EXCEPT"; - pDesc = "Exception."; - break; - - case ETM4_PKT_I_EXCEPT_RTN: - pName = "I_EXCEPT_RTN"; - pDesc = "Exception Return."; - break; - - case ETM4_PKT_I_TIMESTAMP: - pName = "I_TIMESTAMP"; - pDesc = "Timestamp."; + case ETM4_PKT_I_COMMIT: + pName = "I_COMMIT"; + pDesc = "Commit"; break; case ETM4_PKT_I_CANCEL_F1: pName = "I_CANCEL_F1"; pDesc = "Cancel Format 1."; break; + + case ETM4_PKT_I_MISPREDICT: + pName = "I_MISPREDICT"; + pDesc = "Mispredict."; + break; + case ETM4_PKT_I_CANCEL_F2: pName = "I_CANCEL_F2"; pDesc = "Cancel Format 2."; @@ -416,24 +311,149 @@ const char *EtmV4ITrcPacket::packetTypeName(const ocsd_etmv4_i_pkt_type type, co pDesc = "Cancel Format 3."; break; - case ETM4_PKT_I_COMMIT: - pName = "I_COMMIT"; - pDesc = "Commit"; + case ETM4_PKT_I_COND_I_F2: + pName = "I_COND_I_F2"; + pDesc = "Conditional Instruction, format 2."; break; - case ETM4_PKT_I_MISPREDICT: - pName = "I_MISPREDICT"; - pDesc = "Mispredict."; + case ETM4_PKT_I_COND_FLUSH: + pName = "I_COND_FLUSH"; + pDesc = "Conditional Flush."; break; - case ETM4_PKT_I_TRACE_INFO: - pName = "I_TRACE_INFO"; - pDesc = "Trace Info."; + case ETM4_PKT_I_COND_RES_F4: + pName = "I_COND_RES_F4"; + pDesc = "Conditional Result, format 4."; break; - case ETM4_PKT_I_TRACE_ON: - pName = "I_TRACE_ON"; - pDesc = "Trace On."; + case ETM4_PKT_I_COND_RES_F2: + pName = "I_COND_RES_F2"; + pDesc = "Conditional Result, format 2."; + break; + + case ETM4_PKT_I_COND_RES_F3: + pName = "I_COND_RES_F3"; + pDesc = "Conditional Result, format 3."; + break; + + case ETM4_PKT_I_COND_RES_F1: + pName = "I_COND_RES_F1"; + pDesc = "Conditional Result, format 1."; + break; + + case ETM4_PKT_I_COND_I_F1: + pName = "I_COND_I_F1"; + pDesc = "Conditional Instruction, format 1."; + break; + + case ETM4_PKT_I_COND_I_F3: + pName = "I_COND_I_F3"; + pDesc = "Conditional Instruction, format 3."; + break; + + case ETM4_PKT_I_IGNORE: + pName = "I_IGNORE"; + pDesc = "Ignore."; + break; + + case ETM4_PKT_I_EVENT: + pName = "I_EVENT"; + pDesc = "Trace Event."; + break; + + case ETM4_PKT_I_CTXT: + pName = "I_CTXT"; + pDesc = "Context Packet."; + break; + + case ETM4_PKT_I_ADDR_CTXT_L_32IS0: + pName = "I_ADDR_CTXT_L_32IS0"; + pDesc = "Address & Context, Long, 32 bit, IS0."; + break; + + case ETM4_PKT_I_ADDR_CTXT_L_32IS1: + pName = "I_ADDR_CTXT_L_32IS1"; + pDesc = "Address & Context, Long, 32 bit, IS0."; + break; + + case ETM4_PKT_I_ADDR_CTXT_L_64IS0: + pName = "I_ADDR_CTXT_L_64IS0"; + pDesc = "Address & Context, Long, 64 bit, IS0."; + break; + + case ETM4_PKT_I_ADDR_CTXT_L_64IS1: + pName = "I_ADDR_CTXT_L_64IS1"; + pDesc = "Address & Context, Long, 64 bit, IS1."; + break; + + case ETM4_PKT_I_ADDR_MATCH: + pName = "I_ADDR_MATCH"; + pDesc = "Exact Address Match."; + break; + + case ETM4_PKT_I_ADDR_S_IS0: + pName = "I_ADDR_S_IS0"; + pDesc = "Address, Short, IS0."; + break; + + case ETM4_PKT_I_ADDR_S_IS1: + pName = "I_ADDR_S_IS1"; + pDesc = "Address, Short, IS1."; + break; + + case ETM4_PKT_I_ADDR_L_32IS0: + pName = "I_ADDR_L_32IS0"; + pDesc = "Address, Long, 32 bit, IS0."; + break; + + case ETM4_PKT_I_ADDR_L_32IS1: + pName = "I_ADDR_L_32IS1"; + pDesc = "Address, Long, 32 bit, IS1."; + break; + + case ETM4_PKT_I_ADDR_L_64IS0: + pName = "I_ADDR_L_64IS0"; + pDesc = "Address, Long, 64 bit, IS0."; + break; + + case ETM4_PKT_I_ADDR_L_64IS1: + pName = "I_ADDR_L_64IS1"; + pDesc = "Address, Long, 64 bit, IS1."; + break; + + case ETM4_PKT_I_Q: + pName = "I_Q"; + pDesc = "Q Packet."; + break; + + case ETM4_PKT_I_ATOM_F6: + pName = "I_ATOM_F6"; + pDesc = "Atom format 6."; + break; + + case ETM4_PKT_I_ATOM_F5: + pName = "I_ATOM_F5"; + pDesc = "Atom format 5."; + break; + + case ETM4_PKT_I_ATOM_F2: + pName = "I_ATOM_F2"; + pDesc = "Atom format 2."; + break; + + case ETM4_PKT_I_ATOM_F4: + pName = "I_ATOM_F4"; + pDesc = "Atom format 4."; + break; + + case ETM4_PKT_I_ATOM_F1: + pName = "I_ATOM_F1"; + pDesc = "Atom format 1."; + break; + + case ETM4_PKT_I_ATOM_F3: + pName = "I_ATOM_F3"; + pDesc = "Atom format 3."; break; case ETM4_PKT_I_ASYNC: @@ -450,6 +470,9 @@ const char *EtmV4ITrcPacket::packetTypeName(const ocsd_etmv4_i_pkt_type type, co pName = "I_OVERFLOW"; pDesc = "Overflow."; break; + + default: + break; } if(ppDesc) *ppDesc = pDesc; diff --git a/decoder/source/etmv4/trc_pkt_proc_etmv4i_impl.cpp b/decoder/source/etmv4/trc_pkt_proc_etmv4i_impl.cpp index 8d17d8386eb..0607c192f87 100644 --- a/decoder/source/etmv4/trc_pkt_proc_etmv4i_impl.cpp +++ b/decoder/source/etmv4/trc_pkt_proc_etmv4i_impl.cpp @@ -34,12 +34,46 @@ #include "trc_pkt_proc_etmv4i_impl.h" +/* Trace raw input buffer class */ +TraceRawBuffer::TraceRawBuffer() +{ + m_bufSize = 0; + m_bufProcessed = 0; + m_pBuffer = 0; + pkt = 0; +} + +// init the buffer +void TraceRawBuffer::init(const uint32_t size, const uint8_t *rawtrace, std::vector *out_packet) +{ + m_bufSize = size; + m_bufProcessed = 0; + m_pBuffer = rawtrace; + pkt = out_packet; +} + +void TraceRawBuffer::copyByteToPkt() +{ + if (!empty()) { + pkt->push_back(m_pBuffer[m_bufProcessed]); + m_bufProcessed++; + } +} +uint8_t TraceRawBuffer::peekNextByte() +{ + uint8_t val = 0; + if (!empty()) + val = m_pBuffer[m_bufProcessed]; + return val; +} + +/* trace etmv4 packet processing class */ EtmV4IPktProcImpl::EtmV4IPktProcImpl() : m_isInit(false), m_interface(0), m_first_trace_info(false) { - BuildIPacketTable(); + } EtmV4IPktProcImpl::~EtmV4IPktProcImpl() @@ -62,6 +96,7 @@ ocsd_err_t EtmV4IPktProcImpl::Configure(const EtmV4Config *p_config) if(p_config != 0) { m_config = *p_config; + BuildIPacketTable(); // packet table based on config } else { @@ -78,64 +113,81 @@ ocsd_datapath_resp_t EtmV4IPktProcImpl::processData( const ocsd_trc_index_t ind uint32_t *numBytesProcessed) { ocsd_datapath_resp_t resp = OCSD_RESP_CONT; - m_blockBytesProcessed = 0; + m_trcIn.init(dataBlockSize, pDataBlock, &m_currPacketData); m_blockIndex = index; - uint8_t currByte; - while( ( (m_blockBytesProcessed < dataBlockSize) || - ((m_blockBytesProcessed == dataBlockSize) && (m_process_state == SEND_PKT)) ) && - OCSD_DATA_RESP_IS_CONT(resp)) + bool done = false; + uint8_t nextByte; + + do { - currByte = pDataBlock[m_blockBytesProcessed]; try { - switch(m_process_state) + /* while (((m_blockBytesProcessed < dataBlockSize) || + ((m_blockBytesProcessed == dataBlockSize) && (m_process_state == SEND_PKT))) && + OCSD_DATA_RESP_IS_CONT(resp))*/ + while ( (!m_trcIn.empty() || (m_process_state == SEND_PKT)) && + OCSD_DATA_RESP_IS_CONT(resp) + ) { - case PROC_HDR: - m_packet_index = m_blockIndex + m_blockBytesProcessed; - if(m_is_sync) + switch (m_process_state) { - m_pIPktFn = m_i_table[currByte].pptkFn; - m_curr_packet.type = m_i_table[currByte].pkt_type; - } - else - { - // unsynced - process data until we see a sync point - m_pIPktFn = &EtmV4IPktProcImpl::iNotSync; - m_curr_packet.type = ETM4_PKT_I_NOTSYNC; - } - m_process_state = PROC_DATA; + case PROC_HDR: + m_packet_index = m_blockIndex + m_trcIn.processed(); + if (m_is_sync) + { + nextByte = m_trcIn.peekNextByte(); + m_pIPktFn = m_i_table[nextByte].pptkFn; + m_curr_packet.type = m_i_table[nextByte].pkt_type; + } + else + { + // unsynced - process data until we see a sync point + m_pIPktFn = &EtmV4IPktProcImpl::iNotSync; + m_curr_packet.type = ETM4_PKT_I_NOTSYNC; + } + m_process_state = PROC_DATA; - case PROC_DATA: - m_currPacketData.push_back(pDataBlock[m_blockBytesProcessed]); - m_blockBytesProcessed++; - (this->*m_pIPktFn)(); - break; + case PROC_DATA: + // loop till full packet or no more data... + while (!m_trcIn.empty() && (m_process_state == PROC_DATA)) + { + nextByte = m_trcIn.peekNextByte(); + m_trcIn.copyByteToPkt(); // move next byte into the packet + // m_currPacketData.push_back(pDataBlock[m_blockBytesProcessed]); + // m_blockBytesProcessed++; + (this->*m_pIPktFn)(nextByte); + } + break; - case SEND_PKT: - resp = outputPacket(); - InitPacketState(); - m_process_state = PROC_HDR; - break; + case SEND_PKT: + resp = outputPacket(); + InitPacketState(); + m_process_state = PROC_HDR; + break; - case SEND_UNSYNCED: - resp = outputUnsyncedRawPacket(); - if(m_update_on_unsync_packet_index != 0) - { - m_packet_index = m_update_on_unsync_packet_index; - m_update_on_unsync_packet_index = 0; + case SEND_UNSYNCED: + resp = outputUnsyncedRawPacket(); + if (m_update_on_unsync_packet_index != 0) + { + m_packet_index = m_update_on_unsync_packet_index; + m_update_on_unsync_packet_index = 0; + } + m_process_state = PROC_DATA; // after dumping unsynced data, still in data mode. + break; } - m_process_state = PROC_DATA; // after dumping unsynced data, still in data mode. - break; } + done = true; } catch(ocsdError &err) { + done = true; m_interface->LogError(err); if( (err.getErrorCode() == OCSD_ERR_BAD_PACKET_SEQ) || (err.getErrorCode() == OCSD_ERR_INVALID_PCKT_HDR)) { // send invalid packets up the pipe to let the next stage decide what to do. m_process_state = SEND_PKT; + done = false; } else { @@ -145,14 +197,15 @@ ocsd_datapath_resp_t EtmV4IPktProcImpl::processData( const ocsd_trc_index_t ind } catch(...) { + done = true; /// vv bad at this point. resp = OCSD_RESP_FATAL_SYS_ERR; const ocsdError &fatal = ocsdError(OCSD_ERR_SEV_ERROR,OCSD_ERR_FAIL,m_packet_index,m_config.getTraceID(),"Unknown System Error decoding trace."); m_interface->LogError(fatal); } - } + } while (!done); - *numBytesProcessed = m_blockBytesProcessed; + *numBytesProcessed = m_trcIn.processed(); return resp; } @@ -230,38 +283,35 @@ ocsd_datapath_resp_t EtmV4IPktProcImpl::outputUnsyncedRawPacket() return resp; } -void EtmV4IPktProcImpl::iNotSync() +void EtmV4IPktProcImpl::iNotSync(const uint8_t lastByte) { - uint8_t lastByte = m_currPacketData.back(); // peek at the byte being processed... - // is it an extension byte? - if(lastByte == 0x00) // TBD : add check for forced sync in here? + if (lastByte == 0x00) // TBD : add check for forced sync in here? { - if(m_currPacketData.size() > 1) + if (m_currPacketData.size() > 1) { m_dump_unsynced_bytes = m_currPacketData.size() - 1; m_process_state = SEND_UNSYNCED; // outputting some data then update packet index after so output indexes accurate - m_update_on_unsync_packet_index = m_blockIndex + m_blockBytesProcessed - 1; + m_update_on_unsync_packet_index = m_blockIndex + m_trcIn.processed() - 1; } else - m_packet_index = m_blockIndex + m_blockBytesProcessed - 1; // set it up now otherwise. + m_packet_index = m_blockIndex + m_trcIn.processed() - 1; // set it up now otherwise. - m_pIPktFn = m_i_table[lastByte].pptkFn; + m_pIPktFn = m_i_table[lastByte].pptkFn; } - else if(m_currPacketData.size() >= 8) + else if (m_currPacketData.size() >= 8) { m_dump_unsynced_bytes = m_currPacketData.size(); m_process_state = SEND_UNSYNCED; // outputting some data then update packet index after so output indexes accurate - m_update_on_unsync_packet_index = m_blockIndex + m_blockBytesProcessed; + m_update_on_unsync_packet_index = m_blockIndex + m_trcIn.processed(); } } -void EtmV4IPktProcImpl::iPktNoPayload() +void EtmV4IPktProcImpl::iPktNoPayload(const uint8_t lastByte) { // some expansion may be required... - uint8_t lastByte = m_currPacketData.back(); switch(m_curr_packet.type) { case ETM4_PKT_I_ADDR_MATCH: @@ -281,20 +331,27 @@ void EtmV4IPktProcImpl::iPktNoPayload() case ETM4_PKT_I_COND_FLUSH: case ETM4_PKT_I_EXCEPT_RTN: case ETM4_PKT_I_TRACE_ON: + case ETM4_PKT_I_FUNC_RET: + case ETM4_PKT_I_IGNORE: default: break; } m_process_state = SEND_PKT; // now just send it.... } -void EtmV4IPktProcImpl::iPktReserved() +void EtmV4IPktProcImpl::iPktReserved(const uint8_t lastByte) { - m_curr_packet.updateErrType(ETM4_PKT_I_RESERVED); // swap type for err type + m_curr_packet.updateErrType(ETM4_PKT_I_RESERVED, lastByte); // swap type for err type throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_INVALID_PCKT_HDR,m_packet_index,m_config.getTraceID()); } -void EtmV4IPktProcImpl::iPktExtension() +void EtmV4IPktProcImpl::iPktInvalidCfg(const uint8_t lastByte) +{ + m_curr_packet.updateErrType(ETM4_PKT_I_RESERVED_CFG, lastByte); // swap type for err type + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_INVALID_PCKT_HDR, m_packet_index, m_config.getTraceID()); +} + +void EtmV4IPktProcImpl::iPktExtension(const uint8_t lastByte) { - uint8_t lastByte = m_currPacketData.back(); if(m_currPacketData.size() == 2) { // not sync and not next by 0x00 - not sync sequence @@ -331,9 +388,8 @@ void EtmV4IPktProcImpl::iPktExtension() } } -void EtmV4IPktProcImpl::iPktASync() +void EtmV4IPktProcImpl::iPktASync(const uint8_t lastByte) { - uint8_t lastByte = m_currPacketData.back(); if(lastByte != 0x00) { // not sync and not next by 0x00 - not sync sequence if < 12 @@ -372,9 +428,8 @@ void EtmV4IPktProcImpl::iPktASync() } } -void EtmV4IPktProcImpl::iPktTraceInfo() +void EtmV4IPktProcImpl::iPktTraceInfo(const uint8_t lastByte) { - uint8_t lastByte = m_currPacketData.back(); if(m_currPacketData.size() == 1) // header { //clear flags @@ -445,11 +500,8 @@ void EtmV4IPktProcImpl::iPktTraceInfo() } -void EtmV4IPktProcImpl::iPktTimestamp() +void EtmV4IPktProcImpl::iPktTimestamp(const uint8_t lastByte) { - // save the latest byte - uint8_t lastByte = m_currPacketData.back(); - // process the header byte if(m_currPacketData.size() == 1) { @@ -498,9 +550,9 @@ void EtmV4IPktProcImpl::iPktTimestamp() } } -void EtmV4IPktProcImpl::iPktException() +void EtmV4IPktProcImpl::iPktException(const uint8_t lastByte) { - uint8_t lastByte = m_currPacketData.back(); + uint16_t excep_type = 0; switch(m_currPacketData.size()) { @@ -512,7 +564,7 @@ void EtmV4IPktProcImpl::iPktException() if(m_currPacketData.size() == (unsigned)m_excep_size) { - uint16_t excep_type = (m_currPacketData[1] >> 1) & 0x1F; + excep_type = (m_currPacketData[1] >> 1) & 0x1F; uint8_t addr_interp = (m_currPacketData[1] & 0x40) >> 5 | (m_currPacketData[1] & 0x1); uint8_t m_fault_pending = 0; uint8_t m_type = (m_config.coreProfile() == profile_CortexM) ? 1 : 0; @@ -530,11 +582,10 @@ void EtmV4IPktProcImpl::iPktException() } } -void EtmV4IPktProcImpl::iPktCycleCntF123() +void EtmV4IPktProcImpl::iPktCycleCntF123(const uint8_t lastByte) { ocsd_etmv4_i_pkt_type format = m_curr_packet.type; - uint8_t lastByte = m_currPacketData.back(); if( m_currPacketData.size() == 1) { m_count_done = m_commit_done = false; @@ -606,9 +657,8 @@ void EtmV4IPktProcImpl::iPktCycleCntF123() } } -void EtmV4IPktProcImpl::iPktSpeclRes() -{ - uint8_t lastByte = m_currPacketData.back(); +void EtmV4IPktProcImpl::iPktSpeclRes(const uint8_t lastByte) +{ if(m_currPacketData.size() == 1) { switch(m_curr_packet.getType()) @@ -650,9 +700,8 @@ void EtmV4IPktProcImpl::iPktSpeclRes() } } -void EtmV4IPktProcImpl::iPktCondInstr() +void EtmV4IPktProcImpl::iPktCondInstr(const uint8_t lastByte) { - uint8_t lastByte = m_currPacketData.back(); bool bF1Done = false; if(m_currPacketData.size() == 1) @@ -691,10 +740,8 @@ void EtmV4IPktProcImpl::iPktCondInstr() } } -void EtmV4IPktProcImpl::iPktCondResult() +void EtmV4IPktProcImpl::iPktCondResult(const uint8_t lastByte) { - //static ocsd_etmv4_i_pkt_type format = ETM4_PKT_I_COND_RES_F1; // conditional result formats F1-F4 - uint8_t lastByte = m_currPacketData.back(); if(m_currPacketData.size() == 1) { m_F1P1_done = false; // F1 payload 1 done @@ -763,10 +810,10 @@ void EtmV4IPktProcImpl::iPktCondResult() } } -void EtmV4IPktProcImpl::iPktContext() +void EtmV4IPktProcImpl::iPktContext(const uint8_t lastByte) { bool bSendPacket = false; - uint8_t lastByte = m_currPacketData.back(); + if(m_currPacketData.size() == 1) { if((lastByte & 0x1) == 0) @@ -840,10 +887,8 @@ void EtmV4IPktProcImpl::extractAndSetContextInfo(const std::vector &buf } } -void EtmV4IPktProcImpl::iPktAddrCtxt() +void EtmV4IPktProcImpl::iPktAddrCtxt(const uint8_t lastByte) { - uint8_t lastByte = m_currPacketData.back(); - if( m_currPacketData.size() == 1) { m_addrIS = 0; @@ -910,13 +955,14 @@ void EtmV4IPktProcImpl::iPktAddrCtxt() } } -void EtmV4IPktProcImpl::iPktShortAddr() +void EtmV4IPktProcImpl::iPktShortAddr(const uint8_t lastByte) { - uint8_t lastByte = m_currPacketData.back(); - if(m_currPacketData.size() == 1) + if (m_currPacketData.size() == 1) { m_addr_done = false; - m_addrIS = (lastByte == ETM4_PKT_I_ADDR_S_IS0) ? 0 : 1; + m_addrIS = 0; + if (lastByte == ETM4_PKT_I_ADDR_S_IS1) + m_addrIS = 1; } else if(!m_addr_done) { @@ -954,7 +1000,7 @@ int EtmV4IPktProcImpl::extractShortAddr(const std::vector &buffer, cons return idx; } -void EtmV4IPktProcImpl::iPktLongAddr() +void EtmV4IPktProcImpl::iPktLongAddr(const uint8_t lastByte) { if(m_currPacketData.size() == 1) { @@ -998,10 +1044,8 @@ void EtmV4IPktProcImpl::iPktLongAddr() } } -void EtmV4IPktProcImpl::iPktQ() +void EtmV4IPktProcImpl::iPktQ(const uint8_t lastByte) { - uint8_t lastByte = m_currPacketData.back(); - if(m_currPacketData.size() == 1) { m_Q_type = lastByte & 0xF; @@ -1112,7 +1156,7 @@ void EtmV4IPktProcImpl::iPktQ() } -void EtmV4IPktProcImpl::iAtom() +void EtmV4IPktProcImpl::iAtom(const uint8_t lastByte) { // patterns lsbit = oldest atom, ms bit = newest. static const uint32_t f4_patterns[] = { @@ -1122,7 +1166,6 @@ void EtmV4IPktProcImpl::iAtom() 0x5 // NENE }; - uint8_t lastByte = m_currPacketData.back(); uint8_t pattIdx = 0, pattCount = 0; uint32_t pattern; @@ -1212,13 +1255,23 @@ void EtmV4IPktProcImpl::BuildIPacketTable() m_i_table[0x04].pkt_type = ETM4_PKT_I_TRACE_ON; m_i_table[0x04].pptkFn = &EtmV4IPktProcImpl::iPktNoPayload; + + // b0000 0101 - Funct ret V8M + m_i_table[0x05].pkt_type = ETM4_PKT_I_FUNC_RET; + if ((m_config.coreProfile() == profile_CortexM) && + (OCSD_IS_V8_ARCH(m_config.archVersion())) && + (m_config.FullVersion() >= 0x42)) + { + m_i_table[0x05].pptkFn = &EtmV4IPktProcImpl::iPktNoPayload; + } + // b0000 0110 - exception m_i_table[0x06].pkt_type = ETM4_PKT_I_EXCEPT; m_i_table[0x06].pptkFn = &EtmV4IPktProcImpl::iPktException; // b0000 0111 - exception return m_i_table[0x07].pkt_type = ETM4_PKT_I_EXCEPT_RTN; - m_i_table[0x07].pptkFn = &EtmV4IPktProcImpl::iPktNoPayload; + m_i_table[0x07].pptkFn = &EtmV4IPktProcImpl::iPktNoPayload; // b0000 110x - cycle count f2 // b0000 111x - cycle count f1 @@ -1238,22 +1291,27 @@ void EtmV4IPktProcImpl::BuildIPacketTable() // b0010 0xxx - NDSM for(int i = 0; i < 8; i++) { - m_i_table[0x20+i].pkt_type = ETM4_PKT_I_NUM_DS_MKR; - m_i_table[0x20+i].pptkFn = &EtmV4IPktProcImpl::iPktNoPayload; + m_i_table[0x20 + i].pkt_type = ETM4_PKT_I_NUM_DS_MKR; + if (m_config.enabledDataTrace()) + m_i_table[0x20+i].pptkFn = &EtmV4IPktProcImpl::iPktNoPayload; + else + m_i_table[0x20+i].pptkFn = &EtmV4IPktProcImpl::iPktInvalidCfg; } // b0010 10xx, b0010 1100 - UDSM for(int i = 0; i < 5; i++) { m_i_table[0x28+i].pkt_type = ETM4_PKT_I_UNNUM_DS_MKR; - m_i_table[0x28+i].pptkFn = &EtmV4IPktProcImpl::iPktNoPayload; + if (m_config.enabledDataTrace()) + m_i_table[0x28+i].pptkFn = &EtmV4IPktProcImpl::iPktNoPayload; + else + m_i_table[0x28+i].pptkFn = &EtmV4IPktProcImpl::iPktInvalidCfg; } // b0010 1101 - commit m_i_table[0x2D].pkt_type = ETM4_PKT_I_COMMIT; m_i_table[0x2D].pptkFn = &EtmV4IPktProcImpl::iPktSpeclRes; - // b0010 111x - cancel f1 for(int i = 0; i < 2; i++) { @@ -1284,68 +1342,107 @@ void EtmV4IPktProcImpl::BuildIPacketTable() m_i_table[0x38+i].pptkFn = &EtmV4IPktProcImpl::iPktSpeclRes; } + bool bCondValid = m_config.hasCondTrace() && m_config.enabledCondITrace(); + // b0100 000x, b0100 0010 - cond I f2 - for(int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { - m_i_table[0x40+i].pkt_type = ETM4_PKT_I_COND_I_F2; - m_i_table[0x40+i].pptkFn = &EtmV4IPktProcImpl::iPktCondInstr; + m_i_table[0x40 + i].pkt_type = ETM4_PKT_I_COND_I_F2; + if (bCondValid) + m_i_table[0x40 + i].pptkFn = &EtmV4IPktProcImpl::iPktCondInstr; + else + m_i_table[0x40 + i].pptkFn = &EtmV4IPktProcImpl::iPktInvalidCfg; } // b0100 0011 - cond flush m_i_table[0x43].pkt_type = ETM4_PKT_I_COND_FLUSH; - m_i_table[0x43].pptkFn = &EtmV4IPktProcImpl::iPktNoPayload; + if (bCondValid) + m_i_table[0x43].pptkFn = &EtmV4IPktProcImpl::iPktNoPayload; + else + m_i_table[0x43].pptkFn = &EtmV4IPktProcImpl::iPktInvalidCfg; // b0100 010x, b0100 0110 - cond res f4 - for(int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { - m_i_table[0x44+i].pkt_type = ETM4_PKT_I_COND_RES_F4; - m_i_table[0x44+i].pptkFn = &EtmV4IPktProcImpl::iPktCondResult; + m_i_table[0x44 + i].pkt_type = ETM4_PKT_I_COND_RES_F4; + if (bCondValid) + m_i_table[0x44 + i].pptkFn = &EtmV4IPktProcImpl::iPktCondResult; + else + m_i_table[0x44 + i].pptkFn = &EtmV4IPktProcImpl::iPktInvalidCfg; } // b0100 100x, b0100 0110 - cond res f2 // b0100 110x, b0100 1110 - cond res f2 - for(int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { - m_i_table[0x48+i].pkt_type = ETM4_PKT_I_COND_RES_F2; - m_i_table[0x48+i].pptkFn = &EtmV4IPktProcImpl::iPktCondResult; + m_i_table[0x48 + i].pkt_type = ETM4_PKT_I_COND_RES_F2; + if (bCondValid) + m_i_table[0x48 + i].pptkFn = &EtmV4IPktProcImpl::iPktCondResult; + else + m_i_table[0x48 + i].pptkFn = &EtmV4IPktProcImpl::iPktInvalidCfg; } - for(int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { - m_i_table[0x4C+i].pkt_type = ETM4_PKT_I_COND_RES_F2; - m_i_table[0x4C+i].pptkFn = &EtmV4IPktProcImpl::iPktCondResult; + m_i_table[0x4C + i].pkt_type = ETM4_PKT_I_COND_RES_F2; + if (bCondValid) + m_i_table[0x4C + i].pptkFn = &EtmV4IPktProcImpl::iPktCondResult; + else + m_i_table[0x4C + i].pptkFn = &EtmV4IPktProcImpl::iPktInvalidCfg; } // b0101xxxx - cond res f3 - for(int i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) { - m_i_table[0x50+i].pkt_type = ETM4_PKT_I_COND_RES_F3; - m_i_table[0x50+i].pptkFn = &EtmV4IPktProcImpl::iPktCondResult; + m_i_table[0x50 + i].pkt_type = ETM4_PKT_I_COND_RES_F3; + if (bCondValid) + m_i_table[0x50 + i].pptkFn = &EtmV4IPktProcImpl::iPktCondResult; + else + m_i_table[0x50 + i].pptkFn = &EtmV4IPktProcImpl::iPktInvalidCfg; } // b011010xx - cond res f1 - for(int i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { - m_i_table[0x68+i].pkt_type = ETM4_PKT_I_COND_RES_F1; - m_i_table[0x68+i].pptkFn = &EtmV4IPktProcImpl::iPktCondResult; + m_i_table[0x68 + i].pkt_type = ETM4_PKT_I_COND_RES_F1; + if (bCondValid) + m_i_table[0x68 + i].pptkFn = &EtmV4IPktProcImpl::iPktCondResult; + else + m_i_table[0x68 + i].pptkFn = &EtmV4IPktProcImpl::iPktInvalidCfg; } // b0110 1100 - cond instr f1 m_i_table[0x6C].pkt_type = ETM4_PKT_I_COND_I_F1; - m_i_table[0x6C].pptkFn = &EtmV4IPktProcImpl::iPktCondInstr; + if (bCondValid) + m_i_table[0x6C].pptkFn = &EtmV4IPktProcImpl::iPktCondInstr; + else + m_i_table[0x6C].pptkFn = &EtmV4IPktProcImpl::iPktInvalidCfg; // b0110 1101 - cond instr f3 m_i_table[0x6D].pkt_type = ETM4_PKT_I_COND_I_F3; - m_i_table[0x6D].pptkFn = &EtmV4IPktProcImpl::iPktCondInstr; + if (bCondValid) + m_i_table[0x6D].pptkFn = &EtmV4IPktProcImpl::iPktCondInstr; + else + m_i_table[0x6D].pptkFn = &EtmV4IPktProcImpl::iPktInvalidCfg; // b0110111x - cond res f1 - for(int i = 0; i < 2; i++) + for (int i = 0; i < 2; i++) { // G++ cannot understand [0x6E+i] so change these round - m_i_table[i+0x6E].pkt_type = ETM4_PKT_I_COND_RES_F1; - m_i_table[i+0x6E].pptkFn = &EtmV4IPktProcImpl::iPktCondResult; + m_i_table[i + 0x6E].pkt_type = ETM4_PKT_I_COND_RES_F1; + if (bCondValid) + m_i_table[i + 0x6E].pptkFn = &EtmV4IPktProcImpl::iPktCondResult; + else + m_i_table[i + 0x6E].pptkFn = &EtmV4IPktProcImpl::iPktInvalidCfg; } - // b01110001 - b01111111 - cond res f1 + // ETM 4.3 introduces ignore packets + if (m_config.FullVersion() >= 0x43) + { + m_i_table[0x70].pkt_type = ETM4_PKT_I_IGNORE; + m_i_table[0x70].pptkFn = &EtmV4IPktProcImpl::iPktNoPayload; + } + + // b01110001 - b01111111 - event trace for(int i = 0; i < 15; i++) { m_i_table[0x71+i].pkt_type = ETM4_PKT_I_EVENT; @@ -1399,12 +1496,27 @@ void EtmV4IPktProcImpl::BuildIPacketTable() m_i_table[0x9D+i].pkt_type = (i == 0) ? ETM4_PKT_I_ADDR_L_64IS0 : ETM4_PKT_I_ADDR_L_64IS1; m_i_table[0x9D+i].pptkFn = &EtmV4IPktProcImpl::iPktLongAddr; } - + // b1010xxxx - Q packet - for(int i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) { - m_i_table[0xA0+i].pkt_type = ETM4_PKT_I_Q; - m_i_table[0xA0+i].pptkFn = &EtmV4IPktProcImpl::iPktQ; + m_i_table[0xA0 + i].pkt_type = ETM4_PKT_I_Q; + // certain Q type codes are reserved. + switch (i) { + case 0x3: + case 0x4: + case 0x7: + case 0x8: + case 0x9: + case 0xD: + case 0xE: + // don't update pkt fn - leave at default reserved. + break; + default: + // if this config supports Q elem - otherwise reserved again. + if (m_config.hasQElem()) + m_i_table[0xA0 + i].pptkFn = &EtmV4IPktProcImpl::iPktQ; + } } // Atom Packets - all no payload but have specific pattern generation fn diff --git a/decoder/source/etmv4/trc_pkt_proc_etmv4i_impl.h b/decoder/source/etmv4/trc_pkt_proc_etmv4i_impl.h index 5c79c256967..429f32711f3 100644 --- a/decoder/source/etmv4/trc_pkt_proc_etmv4i_impl.h +++ b/decoder/source/etmv4/trc_pkt_proc_etmv4i_impl.h @@ -39,6 +39,31 @@ #include "opencsd/etmv4/trc_cmp_cfg_etmv4.h" #include "opencsd/etmv4/trc_pkt_elem_etmv4i.h" +class TraceRawBuffer +{ +public: + TraceRawBuffer(); + ~TraceRawBuffer() {}; + + // init the buffer + void init(const uint32_t size, const uint8_t *rawtrace, std::vector *out_packet); + void copyByteToPkt(); // move a byte to the packet buffer + uint8_t peekNextByte(); // value of next byte in buffer. + + bool empty() { return m_bufProcessed == m_bufSize; }; + // bytes processed. + uint32_t processed() { return m_bufProcessed; }; + // buffer size; + uint32_t size() { return m_bufSize; } + +private: + uint32_t m_bufSize; + uint32_t m_bufProcessed; + const uint8_t *m_pBuffer; + std::vector *pkt; + +}; + class EtmV4IPktProcImpl { public: @@ -81,11 +106,12 @@ protected: EtmV4Config m_config; /** packet data **/ - std::vector m_currPacketData; // raw data + TraceRawBuffer m_trcIn; // trace data in buffer + std::vector m_currPacketData; // raw data packet int m_currPktIdx; // index into raw packet when expanding EtmV4ITrcPacket m_curr_packet; // expanded packet ocsd_trc_index_t m_packet_index; // index of the start of the current packet - uint32_t m_blockBytesProcessed; // number of bytes processed in the current data block +// uint32_t m_blockBytesProcessed; // number of bytes processed in the current data block ocsd_trc_index_t m_blockIndex; // index at the start of the current data block being processed // searching for sync @@ -110,9 +136,9 @@ private: #define TINFO_KEY_SECT 0x02 #define TINFO_SPEC_SECT 0x04 #define TINFO_CYCT_SECT 0x08 - #define TINFO_CTRL 0x10 - #define TINFO_ALL_SECT 0x0F - #define TINFO_ALL 0x1F + #define TINFO_CTRL 0x20 + #define TINFO_ALL_SECT 0x1F + #define TINFO_ALL 0x3F // address and context packets @@ -152,24 +178,25 @@ private: ocsd_datapath_resp_t outputPacket(); ocsd_datapath_resp_t outputUnsyncedRawPacket(); - void iNotSync(); // not synced yet - void iPktNoPayload(); // process a single byte packet - void iPktReserved(); // deal with reserved header value - void iPktExtension(); - void iPktASync(); - void iPktTraceInfo(); - void iPktTimestamp(); - void iPktException(); - void iPktCycleCntF123(); - void iPktSpeclRes(); - void iPktCondInstr(); - void iPktCondResult(); - void iPktContext(); - void iPktAddrCtxt(); - void iPktShortAddr(); - void iPktLongAddr(); - void iPktQ(); - void iAtom(); + void iNotSync(const uint8_t lastByte); // not synced yet + void iPktNoPayload(const uint8_t lastByte); // process a single byte packet + void iPktReserved(const uint8_t lastByte); // deal with reserved header value + void iPktExtension(const uint8_t lastByte); + void iPktASync(const uint8_t lastByte); + void iPktTraceInfo(const uint8_t lastByte); + void iPktTimestamp(const uint8_t lastByte); + void iPktException(const uint8_t lastByte); + void iPktCycleCntF123(const uint8_t lastByte); + void iPktSpeclRes(const uint8_t lastByte); + void iPktCondInstr(const uint8_t lastByte); + void iPktCondResult(const uint8_t lastByte); + void iPktContext(const uint8_t lastByte); + void iPktAddrCtxt(const uint8_t lastByte); + void iPktShortAddr(const uint8_t lastByte); + void iPktLongAddr(const uint8_t lastByte); + void iPktQ(const uint8_t lastByte); + void iAtom(const uint8_t lastByte); + void iPktInvalidCfg(const uint8_t lastByte); // packet invalid in current config. unsigned extractContField(const std::vector &buffer, const unsigned st_idx, uint32_t &value, const unsigned byte_limit = 5); unsigned extractContField64(const std::vector &buffer, const unsigned st_idx, uint64_t &value, const unsigned byte_limit = 9); @@ -180,7 +207,7 @@ private: int extractShortAddr(const std::vector &buffer, const int st_idx, const uint8_t IS, uint32_t &value, int &bits); // packet processing is table driven. - typedef void (EtmV4IPktProcImpl::*PPKTFN)(void); + typedef void (EtmV4IPktProcImpl::*PPKTFN)(uint8_t); PPKTFN m_pIPktFn; struct _pkt_i_table_t { diff --git a/decoder/source/i_dec/trc_i_decode.cpp b/decoder/source/i_dec/trc_i_decode.cpp index 47b4867e6c5..ab93284848b 100644 --- a/decoder/source/i_dec/trc_i_decode.cpp +++ b/decoder/source/i_dec/trc_i_decode.cpp @@ -40,6 +40,8 @@ ocsd_err_t TrcIDecode::DecodeInstruction(ocsd_instr_info *instr_info) { ocsd_err_t err = OCSD_OK; clear_instr_subtype(); + SetArchVersion(instr_info); + switch(instr_info->isa) { case ocsd_isa_arm: @@ -65,6 +67,22 @@ ocsd_err_t TrcIDecode::DecodeInstruction(ocsd_instr_info *instr_info) return err; } +void TrcIDecode::SetArchVersion(ocsd_instr_info *instr_info) +{ + uint16_t arch = 0x0700; + + switch (instr_info->pe_type.arch) + { + case ARCH_V8: arch = 0x0800; break; + case ARCH_V8r3: arch = 0x0803; break; + case ARCH_V7: + default: + break; + } + set_arch_version(arch); +} + + ocsd_err_t TrcIDecode::DecodeA32(ocsd_instr_info *instr_info) { uint32_t branchAddr = 0; @@ -107,7 +125,13 @@ ocsd_err_t TrcIDecode::DecodeA32(ocsd_instr_info *instr_info) break; } } - + else if (instr_info->wfi_wfe_branch) + { + if (inst_ARM_wfiwfe(instr_info->opcode)) + { + instr_info->type = OCSD_INSTR_WFI_WFE; + } + } instr_info->is_conditional = inst_ARM_is_conditional(instr_info->opcode); return OCSD_OK; @@ -123,17 +147,17 @@ ocsd_err_t TrcIDecode::DecodeA64(ocsd_instr_info *instr_info) instr_info->next_isa = instr_info->isa; // assume same ISA instr_info->is_link = 0; - if(inst_A64_is_indirect_branch(instr_info->opcode)) + if(inst_A64_is_indirect_branch_link(instr_info->opcode, &instr_info->is_link)) { instr_info->type = OCSD_INSTR_BR_INDIRECT; - instr_info->is_link = inst_A64_is_branch_and_link(instr_info->opcode); +// instr_info->is_link = inst_A64_is_branch_and_link(instr_info->opcode); } - else if(inst_A64_is_direct_branch(instr_info->opcode)) + else if(inst_A64_is_direct_branch_link(instr_info->opcode, &instr_info->is_link)) { inst_A64_branch_destination(instr_info->instr_addr,instr_info->opcode,&branchAddr); instr_info->type = OCSD_INSTR_BR; instr_info->branch_addr = (ocsd_vaddr_t)branchAddr; - instr_info->is_link = inst_A64_is_branch_and_link(instr_info->opcode); +// instr_info->is_link = inst_A64_is_branch_and_link(instr_info->opcode); } else if((barrier = inst_A64_barrier(instr_info->opcode)) != ARM_BARRIER_NONE) { @@ -150,6 +174,13 @@ ocsd_err_t TrcIDecode::DecodeA64(ocsd_instr_info *instr_info) break; } } + else if (instr_info->wfi_wfe_branch) + { + if (inst_A64_wfiwfe(instr_info->opcode)) + { + instr_info->type = OCSD_INSTR_WFI_WFE; + } + } instr_info->is_conditional = inst_A64_is_conditional(instr_info->opcode); @@ -172,20 +203,20 @@ ocsd_err_t TrcIDecode::DecodeT32(ocsd_instr_info *instr_info) instr_info->type = OCSD_INSTR_OTHER; // default type instr_info->next_isa = instr_info->isa; // assume same ISA instr_info->is_link = 0; + instr_info->is_conditional = 0; - if(inst_Thumb_is_indirect_branch(instr_info->opcode)) - { - instr_info->type = OCSD_INSTR_BR_INDIRECT; - instr_info->is_link = inst_Thumb_is_branch_and_link(instr_info->opcode); - } - else if(inst_Thumb_is_direct_branch(instr_info->opcode)) + + if(inst_Thumb_is_direct_branch_link(instr_info->opcode,&instr_info->is_link, &instr_info->is_conditional)) { inst_Thumb_branch_destination((uint32_t)instr_info->instr_addr,instr_info->opcode,&branchAddr); instr_info->type = OCSD_INSTR_BR; instr_info->branch_addr = (ocsd_vaddr_t)(branchAddr & ~0x1); if((branchAddr & 0x1) == 0) instr_info->next_isa = ocsd_isa_arm; - instr_info->is_link = inst_Thumb_is_branch_and_link(instr_info->opcode); + } + else if (inst_Thumb_is_indirect_branch_link(instr_info->opcode, &instr_info->is_link)) + { + instr_info->type = OCSD_INSTR_BR_INDIRECT; } else if((barrier = inst_Thumb_barrier(instr_info->opcode)) != ARM_BARRIER_NONE) { @@ -202,7 +233,13 @@ ocsd_err_t TrcIDecode::DecodeT32(ocsd_instr_info *instr_info) break; } } - + else if (instr_info->wfi_wfe_branch) + { + if (inst_Thumb_wfiwfe(instr_info->opcode)) + { + instr_info->type = OCSD_INSTR_WFI_WFE; + } + } instr_info->is_conditional = inst_Thumb_is_conditional(instr_info->opcode); instr_info->thumb_it_conditions = inst_Thumb_is_IT(instr_info->opcode); diff --git a/decoder/source/i_dec/trc_idec_arminst.cpp b/decoder/source/i_dec/trc_idec_arminst.cpp index ed7eb247d3b..09964a15e7b 100644 --- a/decoder/source/i_dec/trc_idec_arminst.cpp +++ b/decoder/source/i_dec/trc_idec_arminst.cpp @@ -5,7 +5,6 @@ * \copyright Copyright (c) 2015, ARM Limited. All Rights Reserved. */ - /* * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -40,13 +39,15 @@ block identification and trace decode. #include "i_dec/trc_idec_arminst.h" - #include /* for NULL */ #include static ocsd_instr_subtype instr_sub_type = OCSD_S_INSTR_NONE; +/* need to spot the architecture version for certain instructions */ +static uint16_t arch_version = 0x70; + ocsd_instr_subtype get_instr_subtype() { return instr_sub_type; @@ -57,6 +58,11 @@ void clear_instr_subtype() instr_sub_type = OCSD_S_INSTR_NONE; } +void set_arch_version(uint16_t version) +{ + arch_version = version; +} + int inst_ARM_is_direct_branch(uint32_t inst) { int is_direct_branch = 1; @@ -75,6 +81,15 @@ int inst_ARM_is_direct_branch(uint32_t inst) return is_direct_branch; } +int inst_ARM_wfiwfe(uint32_t inst) +{ + if ( ((inst & 0xf0000000) != 0xf0000000) && + ((inst & 0x0ffffffe) == 0x0320f002) + ) + /* WFI & WFE may be traced as branches in etm4.3 ++ */ + return 1; + return 0; +} int inst_ARM_is_indirect_branch(uint32_t inst) { @@ -88,14 +103,24 @@ int inst_ARM_is_indirect_branch(uint32_t inst) } } else if ((inst & 0x0ff000d0) == 0x01200010) { /* BLX (register), BX */ + if ((inst & 0xFF) == 0x1E) + instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* BX LR */ + } else if ((inst & 0x0ff000f0) == 0x01200020) { + /* BXJ: in v8 this behaves like BX */ } else if ((inst & 0x0e108000) == 0x08108000) { /* POP {...,pc} or LDMxx {...,pc} */ + if ((inst & 0x0FFFA000) == 0x08BD8000) /* LDMIA SP!,{...,pc} */ + instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; } else if ((inst & 0x0e50f000) == 0x0410f000) { /* LDR PC,imm... inc. POP {PC} */ + if ( (inst & 0x01ff0000) == 0x009D0000) + instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* LDR PC, [SP], #imm */ } else if ((inst & 0x0e50f010) == 0x0610f000) { /* LDR PC,reg */ } else if ((inst & 0x0fe0f000) == 0x01a0f000) { /* MOV PC,rx */ + if ((inst & 0x00100FFF) == 0x00E) /* ensure the S=0, LSL #0 variant - i.e plain MOV */ + instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* MOV PC, R14 */ } else if ((inst & 0x0f900080) == 0x01000000) { /* "Miscellaneous instructions" - in DP space */ is_indirect_branch = 0; @@ -119,39 +144,88 @@ int inst_ARM_is_indirect_branch(uint32_t inst) return is_indirect_branch; } - int inst_Thumb_is_direct_branch(uint32_t inst) +{ + uint8_t link, cond; + return inst_Thumb_is_direct_branch_link(inst, &link, &cond); +} + +int inst_Thumb_is_direct_branch_link(uint32_t inst, uint8_t *is_link, uint8_t *is_cond) { int is_direct_branch = 1; + if ((inst & 0xf0000000) == 0xd0000000 && (inst & 0x0e000000) != 0x0e000000) { /* B (encoding T1) */ + *is_cond = 1; } else if ((inst & 0xf8000000) == 0xe0000000) { /* B (encoding T2) */ } else if ((inst & 0xf800d000) == 0xf0008000 && (inst & 0x03800000) != 0x03800000) { /* B (encoding T3) */ + *is_cond = 1; } else if ((inst & 0xf8009000) == 0xf0009000) { /* B (encoding T4); BL (encoding T1) */ + if (inst & 0x00004000) { + *is_link = 1; + instr_sub_type = OCSD_S_INSTR_BR_LINK; + } } else if ((inst & 0xf800d001) == 0xf000c000) { /* BLX (imm) (encoding T2) */ + *is_link = 1; + instr_sub_type = OCSD_S_INSTR_BR_LINK; } else if ((inst & 0xf5000000) == 0xb1000000) { /* CB(NZ) */ + *is_cond = 1; } else { is_direct_branch = 0; } return is_direct_branch; } +int inst_Thumb_wfiwfe(uint32_t inst) +{ + int is_wfiwfe = 1; + /* WFI, WFE may be branches in etm4.3++ */ + if ((inst & 0xfffffffe) == 0xf3af8002) { + /* WFI & WFE (encoding T2) */ + } + else if ((inst & 0xffef0000) == 0xbf200000) { + /* WFI & WFE (encoding T1) */ + } + else { + is_wfiwfe = 0; + } + return is_wfiwfe; +} int inst_Thumb_is_indirect_branch(uint32_t inst) +{ + uint8_t link; + return inst_Thumb_is_indirect_branch_link(inst, &link); +} + +int inst_Thumb_is_indirect_branch_link(uint32_t inst, uint8_t *is_link) { /* See e.g. PFT Table 2-3 and Table 2-5 */ int is_branch = 1; + if ((inst & 0xff000000) == 0x47000000) { - /* BX, BLX (reg) */ + /* BX, BLX (reg) [v8M includes BXNS, BLXNS] */ + if (inst & 0x00800000) { + *is_link = 1; + instr_sub_type = OCSD_S_INSTR_BR_LINK; + } + else if ((inst & 0x00780000) == 0x00700000) { + instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* BX LR */ + } + } else if ((inst & 0xfff0d000) == 0xf3c08000) { + /* BXJ: in v8 this behaves like BX */ } else if ((inst & 0xff000000) == 0xbd000000) { /* POP {pc} */ + instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; } else if ((inst & 0xfd870000) == 0x44870000) { /* MOV PC,reg or ADD PC,reg */ + if ((inst & 0xffff0000) == 0x46f700000) + instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* MOV PC,LR */ } else if ((inst & 0xfff0ffe0) == 0xe8d0f000) { /* TBB/TBH */ } else if ((inst & 0xffd00000) == 0xe8100000) { @@ -166,18 +240,27 @@ int inst_Thumb_is_indirect_branch(uint32_t inst) /* LDR PC,literal (T2) */ } else if ((inst & 0xfff0f800) == 0xf850f800) { /* LDR PC,imm (T4) */ + if((inst & 0x000f0f00) == 0x000d0b00) + instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* LDR PC, [SP], #imm*/ } else if ((inst & 0xfff0ffc0) == 0xf850f000) { /* LDR PC,reg (T2) */ } else if ((inst & 0xfe508000) == 0xe8108000) { /* LDM PC */ + if ((inst & 0x0FFF0000) == 0x08BD0000) /* LDMIA [SP]!, */ + instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* POP {...,pc} */ } else { is_branch = 0; } return is_branch; } - int inst_A64_is_direct_branch(uint32_t inst) +{ + uint8_t link = 0; + return inst_A64_is_direct_branch_link(inst, &link); +} + +int inst_A64_is_direct_branch_link(uint32_t inst, uint8_t *is_link) { int is_direct_branch = 1; if ((inst & 0x7c000000) == 0x34000000) { @@ -186,31 +269,75 @@ int inst_A64_is_direct_branch(uint32_t inst) /* B */ } else if ((inst & 0x7c000000) == 0x14000000) { /* B, BL imm */ + if (inst & 0x80000000) { + *is_link = 1; + instr_sub_type = OCSD_S_INSTR_BR_LINK; + } } else { is_direct_branch = 0; } return is_direct_branch; } +int inst_A64_wfiwfe(uint32_t inst) +{ + /* WFI, WFE may be traced as branches in etm 4.3++ */ + if ((inst & 0xffffffdf) == 0xd503205f) + return 1; + return 0; +} int inst_A64_is_indirect_branch(uint32_t inst) +{ + uint8_t link = 0; + return inst_A64_is_indirect_branch_link(inst, &link); +} + +int inst_A64_is_indirect_branch_link(uint32_t inst, uint8_t *is_link) { int is_indirect_branch = 1; + if ((inst & 0xffdffc1f) == 0xd61f0000) { /* BR, BLR */ + if (inst & 0x00200000) { + *is_link = 1; + instr_sub_type = OCSD_S_INSTR_BR_LINK; + } } else if ((inst & 0xfffffc1f) == 0xd65f0000) { instr_sub_type = OCSD_S_INSTR_V8_RET; /* RET */ } else if ((inst & 0xffffffff) == 0xd69f03e0) { /* ERET */ instr_sub_type = OCSD_S_INSTR_V8_ERET; + } else if (arch_version >= 0x0803) { + /* new pointer auth instr for v8.3 arch */ + if ((inst & 0xffdff800) == 0xd61f0800) { + /* BRAA, BRAB, BLRAA, BLRBB */ + if (inst & 0x00200000) { + *is_link = 1; + instr_sub_type = OCSD_S_INSTR_BR_LINK; + } + } else if ((inst & 0xffdff81F) == 0xd71f081F) { + /* BRAAZ, BRABZ, BLRAAZ, BLRBBZ */ + if (inst & 0x00200000) { + *is_link = 1; + instr_sub_type = OCSD_S_INSTR_BR_LINK; + } + } else if ((inst & 0xfffffbff) == 0xd69f0bff) { + /* ERETAA, ERETAB */ + instr_sub_type = OCSD_S_INSTR_V8_ERET; + } else if ((inst & 0xfffffbff) == 0xd65f0bff) { + /* RETAA, RETAB */ + instr_sub_type = OCSD_S_INSTR_V8_RET; + } else { + is_indirect_branch = 0; + } } else { is_indirect_branch = 0; } return is_indirect_branch; } - int inst_ARM_branch_destination(uint32_t addr, uint32_t inst, uint32_t *pnpc) { uint32_t npc; @@ -235,7 +362,6 @@ int inst_ARM_branch_destination(uint32_t addr, uint32_t inst, uint32_t *pnpc) return is_direct_branch; } - int inst_Thumb_branch_destination(uint32_t addr, uint32_t inst, uint32_t *pnpc) { uint32_t npc; @@ -290,7 +416,6 @@ int inst_Thumb_branch_destination(uint32_t addr, uint32_t inst, uint32_t *pnpc) return is_direct_branch; } - int inst_A64_branch_destination(uint64_t addr, uint32_t inst, uint64_t *pnpc) { uint64_t npc; @@ -322,21 +447,18 @@ int inst_ARM_is_branch(uint32_t inst) inst_ARM_is_direct_branch(inst); } - int inst_Thumb_is_branch(uint32_t inst) { return inst_Thumb_is_indirect_branch(inst) || inst_Thumb_is_direct_branch(inst); } - int inst_A64_is_branch(uint32_t inst) { return inst_A64_is_indirect_branch(inst) || inst_A64_is_direct_branch(inst); } - int inst_ARM_is_branch_and_link(uint32_t inst) { int is_branch = 1; @@ -359,7 +481,6 @@ int inst_ARM_is_branch_and_link(uint32_t inst) return is_branch; } - int inst_Thumb_is_branch_and_link(uint32_t inst) { int is_branch = 1; @@ -375,7 +496,6 @@ int inst_Thumb_is_branch_and_link(uint32_t inst) return is_branch; } - int inst_A64_is_branch_and_link(uint32_t inst) { int is_branch = 1; @@ -385,19 +505,28 @@ int inst_A64_is_branch_and_link(uint32_t inst) } else if ((inst & 0xfc000000) == 0x94000000) { /* BL */ instr_sub_type = OCSD_S_INSTR_BR_LINK; + } else if (arch_version >= 0x0803) { + /* new pointer auth instr for v8.3 arch */ + if ((inst & 0xfffff800) == 0xd73f0800) { + /* BLRAA, BLRBB */ + instr_sub_type = OCSD_S_INSTR_BR_LINK; + } else if ((inst & 0xfffff81F) == 0xd63f081F) { + /* BLRAAZ, BLRBBZ */ + instr_sub_type = OCSD_S_INSTR_BR_LINK; + } else { + is_branch = 0; + } } else { is_branch = 0; } return is_branch; } - int inst_ARM_is_conditional(uint32_t inst) { return (inst & 0xe0000000) != 0xe0000000; } - int inst_Thumb_is_conditional(uint32_t inst) { if ((inst & 0xf0000000) == 0xd0000000 && (inst & 0x0e000000) != 0x0e000000) { @@ -413,7 +542,6 @@ int inst_Thumb_is_conditional(uint32_t inst) return 0; } - unsigned int inst_Thumb_is_IT(uint32_t inst) { if ((inst & 0xff000000) == 0xbf000000 && @@ -433,7 +561,6 @@ unsigned int inst_Thumb_is_IT(uint32_t inst) } } - /* Test whether an A64 instruction is conditional. @@ -454,7 +581,6 @@ int inst_A64_is_conditional(uint32_t inst) return 0; } - arm_barrier_t inst_ARM_barrier(uint32_t inst) { if ((inst & 0xfff00000) == 0xf5700000) { @@ -484,7 +610,6 @@ arm_barrier_t inst_ARM_barrier(uint32_t inst) } } - arm_barrier_t inst_Thumb_barrier(uint32_t inst) { if ((inst & 0xffffff00) == 0xf3bf8f00) { @@ -516,7 +641,6 @@ arm_barrier_t inst_Thumb_barrier(uint32_t inst) } } - arm_barrier_t inst_A64_barrier(uint32_t inst) { if ((inst & 0xfffff09f) == 0xd503309f) { @@ -535,20 +659,17 @@ arm_barrier_t inst_A64_barrier(uint32_t inst) } } - int inst_ARM_is_UDF(uint32_t inst) { return (inst & 0xfff000f0) == 0xe7f000f0; } - int inst_Thumb_is_UDF(uint32_t inst) { return (inst & 0xff000000) == 0xde000000 || /* T1 */ (inst & 0xfff0f000) == 0xf7f0a000; /* T2 */ } - int inst_A64_is_UDF(uint32_t inst) { /* No A64 encodings are formally allocated as permanently undefined, diff --git a/decoder/source/mem_acc/trc_mem_acc_bufptr.cpp b/decoder/source/mem_acc/trc_mem_acc_bufptr.cpp index fee663b30d0..25c736387c0 100644 --- a/decoder/source/mem_acc/trc_mem_acc_bufptr.cpp +++ b/decoder/source/mem_acc/trc_mem_acc_bufptr.cpp @@ -42,7 +42,7 @@ TrcMemAccBufPtr::TrcMemAccBufPtr(const ocsd_vaddr_t s_address, const uint8_t *p_ { } -const uint32_t TrcMemAccBufPtr::readBytes(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer) +const uint32_t TrcMemAccBufPtr::readBytes(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint8_t trcID, const uint32_t reqBytes, uint8_t *byteBuffer) { // mapper wlll filter memory spaces. uint32_t bytesRead = bytesInRange(address,reqBytes); // check bytes available diff --git a/decoder/source/mem_acc/trc_mem_acc_cache.cpp b/decoder/source/mem_acc/trc_mem_acc_cache.cpp new file mode 100644 index 00000000000..444314ee9da --- /dev/null +++ b/decoder/source/mem_acc/trc_mem_acc_cache.cpp @@ -0,0 +1,176 @@ +/*! +* \file trc_mem_acc_cache.cpp +* \brief OpenCSD : Memory accessor cache. +* +* \copyright Copyright (c) 2018, ARM Limited. All Rights Reserved. +*/ + +/* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its contributors +* may be used to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include "mem_acc/trc_mem_acc_cache.h" +#include "mem_acc/trc_mem_acc_base.h" +#include "interfaces/trc_error_log_i.h" + +#ifdef LOG_CACHE_STATS +#define INC_HITS_RL(idx) m_hits++; m_hit_rl[m_mru_idx]++; +#define INC_MISS() m_misses++; +#define INC_PAGES() m_pages++; +#define SET_MAX_RL(idx) \ + { \ + if (m_hit_rl_max[idx] < m_hit_rl[idx]) \ + m_hit_rl_max[idx] = m_hit_rl[idx]; \ + m_hit_rl[idx] = 0; \ + } +#define INC_RL(idx) m_hit_rl[m_mru_idx]++; +#else +#define INC_HITS_RL(idx) +#define INC_MISS() +#define INC_PAGES() +#define SET_MAX_RL(idx) +#define INC_RL(idx) +#endif + +// uncomment to log cache ops +//#define LOG_CACHE_OPS + +ocsd_err_t TrcMemAccCache::readBytesFromCache(TrcMemAccessorBase *p_accessor, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint8_t trcID, uint32_t *numBytes, uint8_t *byteBuffer) +{ + uint32_t bytesRead = 0, reqBytes = *numBytes; + ocsd_err_t err = OCSD_OK; + +#ifdef LOG_CACHE_OPS + std::ostringstream oss; +#endif + + if (m_bCacheEnabled) + { + if (blockInCache(address, reqBytes)) + { + bytesRead = reqBytes; + memcpy(byteBuffer, &m_mru[m_mru_idx].data[address - m_mru[m_mru_idx].st_addr], reqBytes); +#ifdef LOG_CACHE_OPS + oss << "TrcMemAccCache:: hit [page: " << std::dec << m_mru_idx << "[addr:0x" << std::hex << address << ", bytes: " << std::dec << reqBytes << "]\n"; + logMsg(oss.str()); +#endif + INC_HITS_RL(m_mru_idx); + } + else + { + INC_MISS(); +#ifdef LOG_CACHE_OPS + oss << "TrcMemAccCache:: miss [addr:0x" << std::hex << address << ", bytes: " << std::dec << reqBytes << "]\n"; + logMsg(oss.str()); +#endif + /* need a new cache page - check the underlying accessor for the data */ + m_mru_idx = m_mru_next_new; + m_mru[m_mru_idx].valid_len = p_accessor->readBytes(address, mem_space, trcID, MEM_ACC_CACHE_PAGE_SIZE, &m_mru[m_mru_idx].data[0]); + + /* check return length valid - v bad if return length more than request */ + if (m_mru[m_mru_idx].valid_len > MEM_ACC_CACHE_PAGE_SIZE) + { + m_mru[m_mru_idx].valid_len = 0; // set to nothing returned. + err = OCSD_ERR_MEM_ACC_BAD_LEN; + } + + if (m_mru[m_mru_idx].valid_len > 0) + { + // got some data - so save the + m_mru[m_mru_idx].st_addr = address; + + // log the run length hit counts + SET_MAX_RL(m_mru_idx); + +#ifdef LOG_CACHE_OPS + oss.str(""); + oss << "TrcMemAccCache:: load [page: " << std::dec << m_mru_idx << "[addr:0x" << std::hex << address << ", bytes: " << std::dec << m_mru[m_mru_idx].valid_len << "]\n"; + logMsg(oss.str()); +#endif + INC_PAGES(); + + // increment the next new page counter. + m_mru_next_new++; + if (m_mru_next_new == MEM_ACC_CACHE_MRU_SIZE) + m_mru_next_new = 0; + + if (blockInPage(address, reqBytes)) /* check we got the data we needed */ + { + bytesRead = reqBytes; + memcpy(byteBuffer, &m_mru[m_mru_idx].data[address - m_mru[m_mru_idx].st_addr], reqBytes); + INC_RL(m_mru_idx); + } + else + { +#ifdef LOG_CACHE_OPS + oss.str(""); + oss << "TrcMemAccCache:: miss-after-load [page: " << std::dec << m_mru_idx << "[addr:0x" << std::hex << address << ", bytes: " << std::dec << m_mru[m_mru_idx].valid_len << "]\n"; + logMsg(oss.str()); +#endif + INC_MISS(); + } + } + } + } + *numBytes = bytesRead; + return err; +} + +void TrcMemAccCache::logMsg(const std::string &szMsg) +{ + if (m_err_log) + m_err_log->LogMessage(ITraceErrorLog::HANDLE_GEN_INFO, OCSD_ERR_SEV_INFO, szMsg); +} + +void TrcMemAccCache::setErrorLog(ITraceErrorLog *log) +{ + m_err_log = log; +} + +void TrcMemAccCache::logAndClearCounts() +{ +#ifdef LOG_CACHE_STATS + std::ostringstream oss; + + oss << "TrcMemAccCache:: cache performance: hits(" << std::dec << m_hits << "), miss(" << m_misses << "), pages(" << m_pages << ")\n"; + logMsg(oss.str()); + for (int i = 0; i < MEM_ACC_CACHE_MRU_SIZE; i++) + { + if (m_hit_rl_max[i] < m_hit_rl[i]) + m_hit_rl_max[i] = m_hit_rl[i]; + oss.str(""); + oss << "Run length max page " << std::dec << i << ": " << m_hit_rl_max[i] << "\n"; + logMsg(oss.str()); + } + m_hits = m_misses = m_pages = 0; +#endif +} + + +/* End of File trc_mem_acc_cache.cpp */ diff --git a/decoder/source/mem_acc/trc_mem_acc_cb.cpp b/decoder/source/mem_acc/trc_mem_acc_cb.cpp index 28dedcb0fba..1a1565bff03 100644 --- a/decoder/source/mem_acc/trc_mem_acc_cb.cpp +++ b/decoder/source/mem_acc/trc_mem_acc_cb.cpp @@ -19,13 +19,15 @@ TrcMemAccCB::TrcMemAccCB(const ocsd_vaddr_t s_address, } /** Memory access override - allow decoder to read bytes from the buffer. */ -const uint32_t TrcMemAccCB::readBytes(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t memSpace, const uint32_t reqBytes, uint8_t *byteBuffer) +const uint32_t TrcMemAccCB::readBytes(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t memSpace, const uint8_t trcID, const uint32_t reqBytes, uint8_t *byteBuffer) { // if we have a callback object, use it to call back. if(m_p_CBclass) return m_p_CBclass->readBytes(address,memSpace,reqBytes,byteBuffer); if(m_p_CBfn) return m_p_CBfn(m_p_cbfn_context, address,memSpace,reqBytes,byteBuffer); + if (m_p_CBIDfn) + return m_p_CBIDfn(m_p_cbfn_context, address, memSpace, trcID, reqBytes, byteBuffer); return 0; } diff --git a/decoder/source/mem_acc/trc_mem_acc_file.cpp b/decoder/source/mem_acc/trc_mem_acc_file.cpp index 87901c83dce..25b718ea589 100644 --- a/decoder/source/mem_acc/trc_mem_acc_file.cpp +++ b/decoder/source/mem_acc/trc_mem_acc_file.cpp @@ -199,7 +199,7 @@ TrcMemAccessorFile * TrcMemAccessorFile::getExistingFileAccessor(const std::stri /***************************************************/ /* accessor instance functions */ /***************************************************/ -const uint32_t TrcMemAccessorFile::readBytes(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer) +const uint32_t TrcMemAccessorFile::readBytes(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint8_t trcID, const uint32_t reqBytes, uint8_t *byteBuffer) { if(!m_mem_file.is_open()) return 0; diff --git a/decoder/source/mem_acc/trc_mem_acc_mapper.cpp b/decoder/source/mem_acc/trc_mem_acc_mapper.cpp index 6d4f085c6fa..53edfe1a161 100644 --- a/decoder/source/mem_acc/trc_mem_acc_mapper.cpp +++ b/decoder/source/mem_acc/trc_mem_acc_mapper.cpp @@ -34,17 +34,23 @@ #include "mem_acc/trc_mem_acc_mapper.h" #include "mem_acc/trc_mem_acc_file.h" +#include "common/ocsd_error.h" /************************************************************************************/ /* mappers base class */ /************************************************************************************/ +#define USING_MEM_ACC_CACHE + TrcMemAccMapper::TrcMemAccMapper() : m_acc_curr(0), m_trace_id_curr(0), m_using_trace_id(false), m_err_log(0) { +#ifdef USING_MEM_ACC_CACHE + m_cache.enableCaching(true); +#endif } TrcMemAccMapper::TrcMemAccMapper(bool using_trace_id) : @@ -53,27 +59,64 @@ TrcMemAccMapper::TrcMemAccMapper(bool using_trace_id) : m_using_trace_id(using_trace_id), m_err_log(0) { +#ifdef USING_MEM_ACC_CACHE + m_cache.enableCaching(true); +#endif } TrcMemAccMapper::~TrcMemAccMapper() { } +void TrcMemAccMapper::setErrorLog(ITraceErrorLog *err_log_i) +{ + m_err_log = err_log_i; + m_cache.setErrorLog(err_log_i); +} + // memory access interface ocsd_err_t TrcMemAccMapper::ReadTargetMemory(const ocsd_vaddr_t address, const uint8_t cs_trace_id, const ocsd_mem_space_acc_t mem_space, uint32_t *num_bytes, uint8_t *p_buffer) { bool bReadFromCurr = true; + uint32_t readBytes = 0; + ocsd_err_t err = OCSD_OK; /* see if the address is in any range we know */ - if(!readFromCurrent(address, mem_space, cs_trace_id)) - bReadFromCurr = findAccessor(address, mem_space, cs_trace_id); + if (!readFromCurrent(address, mem_space, cs_trace_id)) + { + bReadFromCurr = findAccessor(address, mem_space, cs_trace_id); + + // found a new accessor - invalidate any cache entries used by the previous one. + if (m_cache.enabled() && bReadFromCurr) + m_cache.invalidateAll(); + } /* if bReadFromCurr then we know m_acc_curr is set */ - if(bReadFromCurr) - *num_bytes = m_acc_curr->readBytes(address, mem_space, *num_bytes,p_buffer); - else - *num_bytes = 0; - return OCSD_OK; + if (bReadFromCurr) + { + // use cache if enabled and the amount fits into a cache page + if (m_cache.enabled_for_size(*num_bytes)) + { + // read from cache - or load a new cache page and read.... + readBytes = *num_bytes; + err = m_cache.readBytesFromCache(m_acc_curr, address, mem_space, cs_trace_id, &readBytes, p_buffer); + if (err != OCSD_OK) + LogWarn(err, "Mem Acc: Cache access error"); + } + else + { + readBytes = m_acc_curr->readBytes(address, mem_space, cs_trace_id, *num_bytes, p_buffer); + // guard against bad accessor returns (e.g. callback not obeying the rules for return values) + if (readBytes > *num_bytes) + { + err = OCSD_ERR_MEM_ACC_BAD_LEN; + LogWarn(err,"Mem acc: bad return length"); + } + } + } + + *num_bytes = readBytes; + return err; } void TrcMemAccMapper::RemoveAllAccessors() @@ -84,8 +127,12 @@ void TrcMemAccMapper::RemoveAllAccessors() { TrcMemAccFactory::DestroyAccessor(pAcc); pAcc = getNextAccessor(); + if (m_cache.enabled()) + m_cache.invalidateAll(); } clearAccessorList(); + if (m_cache.enabled()) + m_cache.logAndClearCounts(); } ocsd_err_t TrcMemAccMapper::RemoveAccessorByAddress(const ocsd_vaddr_t st_address, const ocsd_mem_space_acc_t mem_space, const uint8_t cs_trace_id /* = 0 */) @@ -95,9 +142,13 @@ ocsd_err_t TrcMemAccMapper::RemoveAccessorByAddress(const ocsd_vaddr_t st_addres { err = RemoveAccessor(m_acc_curr); m_acc_curr = 0; + if (m_cache.enabled()) + m_cache.invalidateAll(); } else err = OCSD_ERR_INVALID_PARAM_VAL; + if (m_cache.enabled()) + m_cache.logAndClearCounts(); return err; } @@ -107,6 +158,16 @@ void TrcMemAccMapper::LogMessage(const std::string &msg) m_err_log->LogMessage(ITraceErrorLog::HANDLE_GEN_INFO,OCSD_ERR_SEV_INFO,msg); } +void TrcMemAccMapper::LogWarn(const ocsd_err_t err, const std::string &msg) +{ + if (m_err_log) + { + ocsdError err_ocsd(OCSD_ERR_SEV_WARN,err,msg); + m_err_log->LogError(ITraceErrorLog::HANDLE_GEN_INFO, &err_ocsd); + } +} + + /************************************************************************************/ /* mappers global address space class - no differentiation in core trace IDs */ /************************************************************************************/ diff --git a/decoder/source/ocsd_code_follower.cpp b/decoder/source/ocsd_code_follower.cpp index 229859e4733..4386eb4c1d9 100644 --- a/decoder/source/ocsd_code_follower.cpp +++ b/decoder/source/ocsd_code_follower.cpp @@ -41,6 +41,7 @@ OcsdCodeFollower::OcsdCodeFollower() m_instr_info.pe_type.profile = profile_Unknown; m_instr_info.isa = ocsd_isa_unknown; m_instr_info.dsb_dmb_waypoints = 0; + m_instr_info.wfi_wfe_branch = 0; m_instr_info.instr_addr = 0; m_instr_info.opcode = 0; m_pMemAccess = 0; diff --git a/decoder/source/ocsd_dcd_tree.cpp b/decoder/source/ocsd_dcd_tree.cpp index 0cce1340c75..cf75e569d72 100644 --- a/decoder/source/ocsd_dcd_tree.cpp +++ b/decoder/source/ocsd_dcd_tree.cpp @@ -105,6 +105,7 @@ DecodeTree::DecodeTree() : DecodeTree::~DecodeTree() { + destroyMemAccMapper(); for(uint8_t i = 0; i < 0x80; i++) { destroyDecodeElement(i); @@ -314,7 +315,36 @@ ocsd_err_t DecodeTree::addBinFileRegionMemAcc(const ocsd_file_mem_region_t *regi return err; } -ocsd_err_t DecodeTree::addCallbackMemAcc(const ocsd_vaddr_t st_address, const ocsd_vaddr_t en_address, const ocsd_mem_space_acc_t mem_space, Fn_MemAcc_CB p_cb_func, const void *p_context) +ocsd_err_t DecodeTree::updateBinFileRegionMemAcc(const ocsd_file_mem_region_t *region_array, const int num_regions, const ocsd_mem_space_acc_t mem_space, const std::string &filepath) +{ + if (!hasMemAccMapper()) + return OCSD_ERR_NOT_INIT; + + if ((region_array == 0) || (num_regions == 0) || (filepath.length() == 0)) + return OCSD_ERR_INVALID_PARAM_VAL; + + TrcMemAccessorFile *pAcc = TrcMemAccessorFile::getExistingFileAccessor(filepath); + if (!pAcc) + return OCSD_ERR_INVALID_PARAM_VAL; + + int curr_region_idx = 0; + while (curr_region_idx < num_regions) + { + // check "new" range + if (!pAcc->addrStartOfRange(region_array[curr_region_idx].start_address)) + { + // ensure adds cleanly + if (!pAcc->AddOffsetRange(region_array[curr_region_idx].start_address, + region_array[curr_region_idx].region_size, + region_array[curr_region_idx].file_offset)) + return OCSD_ERR_INVALID_PARAM_VAL; // otherwise bail out + } + curr_region_idx++; + } + return OCSD_OK; +} +ocsd_err_t DecodeTree::initCallbackMemAcc(const ocsd_vaddr_t st_address, const ocsd_vaddr_t en_address, + const ocsd_mem_space_acc_t mem_space, void *p_cb_func, bool IDfn, const void *p_context) { if(!hasMemAccMapper()) return OCSD_ERR_NOT_INIT; @@ -329,7 +359,11 @@ ocsd_err_t DecodeTree::addCallbackMemAcc(const ocsd_vaddr_t st_address, const oc TrcMemAccCB *pCBAcc = dynamic_cast(p_accessor); if(pCBAcc) { - pCBAcc->setCBIfFn(p_cb_func, p_context); + if (IDfn) + pCBAcc->setCBIDIfFn((Fn_MemAccID_CB)p_cb_func, p_context); + else + pCBAcc->setCBIfFn((Fn_MemAcc_CB)p_cb_func, p_context); + err = m_default_mapper->AddAccessor(p_accessor,0); } else @@ -341,6 +375,16 @@ ocsd_err_t DecodeTree::addCallbackMemAcc(const ocsd_vaddr_t st_address, const oc return err; } +ocsd_err_t DecodeTree::addCallbackMemAcc(const ocsd_vaddr_t st_address, const ocsd_vaddr_t en_address, const ocsd_mem_space_acc_t mem_space, Fn_MemAcc_CB p_cb_func, const void *p_context) +{ + return initCallbackMemAcc(st_address, en_address, mem_space, (void *)p_cb_func, false, p_context); +} + +ocsd_err_t DecodeTree::addCallbackIDMemAcc(const ocsd_vaddr_t st_address, const ocsd_vaddr_t en_address, const ocsd_mem_space_acc_t mem_space, Fn_MemAccID_CB p_cb_func, const void *p_context) +{ + return initCallbackMemAcc(st_address, en_address, mem_space, (void *)p_cb_func, true, p_context); +} + ocsd_err_t DecodeTree::removeMemAccByAddress(const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space) { if(!hasMemAccMapper()) diff --git a/decoder/source/ocsd_error.cpp b/decoder/source/ocsd_error.cpp index cd417a2212c..251964b7a4b 100644 --- a/decoder/source/ocsd_error.cpp +++ b/decoder/source/ocsd_error.cpp @@ -61,6 +61,7 @@ static const char *s_errorCodeDescs[][2] = { {"OCSD_ERR_DATA_DECODE_FATAL", "A decoder in the data path has returned a fatal error."}, /* frame deformatter errors */ {"OCSD_ERR_DFMTR_NOTCONTTRACE", "Trace input to deformatter none-continuous"}, + {"OCSD_ERR_DFMTR_BAD_FHSYNC", "Bad frame or half frame sync in trace deformatter"}, /* packet processor errors - protocol issues etc */ {"OCSD_ERR_BAD_PACKET_SEQ","Bad packet sequence"}, {"OCSD_ERR_INVALID_PCKT_HDR","Invalid packet header"}, @@ -79,6 +80,7 @@ static const char *s_errorCodeDescs[][2] = { {"OCSD_ERR_MEM_ACC_OVERLAP","Attempted to set an overlapping range in memory access map."}, {"OCSD_ERR_MEM_ACC_FILE_NOT_FOUND","Memory access file could not be opened."}, {"OCSD_ERR_MEM_ACC_FILE_DIFF_RANGE","Attempt to re-use the same memory access file for a different address range."}, + {"OCSD_ERR_MEM_ACC_BAD_LEN","Memory accessor returned a bad read length value (larger than requested."}, {"OCSD_ERR_MEM_ACC_RANGE_INVALID","Address range in accessor set to invalid values."}, /* test errors - errors generated only by the test code, not the library */ {"OCSD_ERR_TEST_SNAPSHOT_PARSE", "Test snapshot file parse error"}, @@ -90,7 +92,7 @@ static const char *s_errorCodeDescs[][2] = { {"OCSD_ERR_DCDREG_NAME_UNKNOWN","Attempted to find a decoder with a name that is not known in the library."}, {"OCSD_ERR_DCDREG_TYPE_UNKNOWN","Attempted to find a decoder with a type that is not known in the library."}, /* decoder config */ - {"OCSD_ERR_DCD_INTERFACE_UNUSED","Attempt to connect or use and inteface not supported by this decoder."}, + {"OCSD_ERR_DCD_INTERFACE_UNUSED","Attempt to connect or use and interface not supported by this decoder."}, /* end marker*/ {"OCSD_ERR_LAST", "No error - error code end marker"} }; diff --git a/decoder/source/ocsd_error_logger.cpp b/decoder/source/ocsd_error_logger.cpp index 2b109d605b0..42794a78431 100644 --- a/decoder/source/ocsd_error_logger.cpp +++ b/decoder/source/ocsd_error_logger.cpp @@ -35,7 +35,7 @@ #include "common/ocsd_error_logger.h" -#include +//#include #include ocsdDefaultErrorLogger::ocsdDefaultErrorLogger() : diff --git a/decoder/source/ocsd_version.cpp b/decoder/source/ocsd_version.cpp index 33990a02c99..6dd3f2c7069 100644 --- a/decoder/source/ocsd_version.cpp +++ b/decoder/source/ocsd_version.cpp @@ -32,7 +32,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "ocsd_if_version.h" +#include "opencsd/ocsd_if_version.h" #include "common/ocsd_version.h" const uint32_t ocsdVersion::vers_num() diff --git a/decoder/source/ptm/trc_pkt_decode_ptm.cpp b/decoder/source/ptm/trc_pkt_decode_ptm.cpp index aa426889fd5..94ed5acc243 100644 --- a/decoder/source/ptm/trc_pkt_decode_ptm.cpp +++ b/decoder/source/ptm/trc_pkt_decode_ptm.cpp @@ -179,6 +179,7 @@ ocsd_err_t TrcPktDecodePtm::onProtocolConfig() m_instr_info.pe_type.profile = m_config->coreProfile(); m_instr_info.pe_type.arch = m_config->archVersion(); m_instr_info.dsb_dmb_waypoints = m_config->dmsbWayPt() ? 1 : 0; + m_instr_info.wfi_wfe_branch = 0; return err; } @@ -576,10 +577,11 @@ ocsd_datapath_resp_t TrcPktDecodePtm::processAtomRange(const ocsd_atm_val A, con } m_output_elem.setType(OCSD_GEN_TRC_ELEM_INSTR_RANGE); - m_output_elem.setLastInstrInfo((A == ATOM_E),m_instr_info.type, m_instr_info.sub_type); + m_output_elem.setLastInstrInfo((A == ATOM_E),m_instr_info.type, m_instr_info.sub_type,m_instr_info.instr_size); m_output_elem.setISA(m_curr_pe_state.isa); if(m_curr_packet_in->hasCC()) m_output_elem.setCycleCount(m_curr_packet_in->getCCVal()); + m_output_elem.setLastInstrCond(m_instr_info.is_conditional); resp = outputTraceElementIdx(m_index_curr_pkt,m_output_elem); m_curr_pe_state.instr_addr = m_instr_info.instr_addr; @@ -594,8 +596,9 @@ ocsd_datapath_resp_t TrcPktDecodePtm::processAtomRange(const ocsd_atm_val A, con { // some trace before we were out of memory access range m_output_elem.setType(OCSD_GEN_TRC_ELEM_INSTR_RANGE); - m_output_elem.setLastInstrInfo(true,m_instr_info.type, m_instr_info.sub_type); + m_output_elem.setLastInstrInfo(true,m_instr_info.type, m_instr_info.sub_type,m_instr_info.instr_size); m_output_elem.setISA(m_curr_pe_state.isa); + m_output_elem.setLastInstrCond(m_instr_info.is_conditional); resp = outputTraceElementIdx(m_index_curr_pkt,m_output_elem); } } @@ -612,6 +615,7 @@ ocsd_err_t TrcPktDecodePtm::traceInstrToWP(bool &bWPFound, const waypoint_trace_ ocsd_mem_space_acc_t mem_space = (m_pe_context.security_level == ocsd_sec_secure) ? OCSD_MEM_SPACE_S : OCSD_MEM_SPACE_N; m_output_elem.st_addr = m_output_elem.en_addr = m_instr_info.instr_addr; + m_output_elem.num_instr_range = 0; bWPFound = false; @@ -634,6 +638,7 @@ ocsd_err_t TrcPktDecodePtm::traceInstrToWP(bool &bWPFound, const waypoint_trace_ // update the range decoded address in the output packet. m_output_elem.en_addr = m_instr_info.instr_addr; + m_output_elem.num_instr_range++; m_output_elem.last_i_type = m_instr_info.type; // either walking to match the next instruction address or a real waypoint diff --git a/decoder/source/trc_core_arch_map.cpp b/decoder/source/trc_core_arch_map.cpp index 66513113d4b..70a25eef035 100644 --- a/decoder/source/trc_core_arch_map.cpp +++ b/decoder/source/trc_core_arch_map.cpp @@ -39,9 +39,17 @@ static struct _ap_map_elements { ocsd_arch_profile_t ap; } ap_map_array[] = { + { "Cortex-A77", { ARCH_V8r3, profile_CortexA } }, + { "Cortex-A76", { ARCH_V8r3, profile_CortexA } }, + { "Cortex-A75", { ARCH_V8r3, profile_CortexA } }, + { "Cortex-A73", { ARCH_V8, profile_CortexA } }, { "Cortex-A72", { ARCH_V8, profile_CortexA } }, + { "Cortex-A65", { ARCH_V8r3, profile_CortexA } }, { "Cortex-A57", { ARCH_V8, profile_CortexA } }, + { "Cortex-A55", { ARCH_V8r3, profile_CortexA } }, { "Cortex-A53", { ARCH_V8, profile_CortexA } }, + { "Cortex-A35", { ARCH_V8, profile_CortexA } }, + { "Cortex-A32", { ARCH_V8, profile_CortexA } }, { "Cortex-A17", { ARCH_V7, profile_CortexA } }, { "Cortex-A15", { ARCH_V7, profile_CortexA } }, { "Cortex-A12", { ARCH_V7, profile_CortexA } }, @@ -49,9 +57,13 @@ static struct _ap_map_elements { { "Cortex-A8", { ARCH_V7, profile_CortexA } }, { "Cortex-A7", { ARCH_V7, profile_CortexA } }, { "Cortex-A5", { ARCH_V7, profile_CortexA } }, + { "Cortex-R52", { ARCH_V8, profile_CortexR } }, + { "Cortex-R8", { ARCH_V7, profile_CortexR } }, { "Cortex-R7", { ARCH_V7, profile_CortexR } }, { "Cortex-R5", { ARCH_V7, profile_CortexR } }, { "Cortex-R4", { ARCH_V7, profile_CortexR } }, + { "Cortex-M33", { ARCH_V8, profile_CortexM } }, + { "Cortex-M23", { ARCH_V8, profile_CortexM } }, { "Cortex-M0", { ARCH_V7, profile_CortexM } }, { "Cortex-M0+", { ARCH_V7, profile_CortexM } }, { "Cortex-M3", { ARCH_V7, profile_CortexM } }, diff --git a/decoder/source/trc_frame_deformatter.cpp b/decoder/source/trc_frame_deformatter.cpp index b4f40a2f9b4..4d46854a655 100644 --- a/decoder/source/trc_frame_deformatter.cpp +++ b/decoder/source/trc_frame_deformatter.cpp @@ -393,9 +393,21 @@ bool TraceFmtDcdImpl::checkForSync() uint32_t TraceFmtDcdImpl::findfirstFSync() { - uint32_t unsynced = m_in_block_size; // consider entire block as unsynced at present. - //**TBD - handle fsync patterns in TPIU captured code - return unsynced; + uint32_t processed = 0; + const uint32_t FSYNC_PATTERN = 0x7FFFFFFF; // LE host pattern for FSYNC + const uint8_t *dataPtr = m_in_block_base; + + while (processed < (m_in_block_size - 3)) + { + if (*((uint32_t *)(dataPtr)) == FSYNC_PATTERN) + { + m_frame_synced = true; + break; + } + processed++; + dataPtr++; + } + return processed; } void TraceFmtDcdImpl::outputUnsyncedBytes(uint32_t /*num_bytes*/) @@ -453,7 +465,7 @@ bool TraceFmtDcdImpl::extractFrame() bool cont_process = true; // continue processing after extraction. uint32_t f_sync_bytes = 0; // skipped f sync bytes uint32_t h_sync_bytes = 0; // skipped h sync bytes - uint32_t ex_bytes = 0; // extracted bytes + uint32_t ex_bytes = 0; // extracted this pass (may be filling out part frame) // memory aligned sources are always multiples of frames, aligned to start. if( m_cfgFlags & OCSD_DFRMTR_FRAME_MEM_ALIGN) @@ -512,8 +524,6 @@ bool TraceFmtDcdImpl::extractFrame() f_sync_bytes += 4; dataPtr += 4; cont_process = (bool)(dataPtr < eodPtr); - - // TBD: output raw FSYNC data on raw frame channel. } } @@ -526,6 +536,7 @@ bool TraceFmtDcdImpl::extractFrame() if(*((uint32_t *)(dataPtr)) == FSYNC_PATTERN) { // throw an illegal FSYNC error + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_DFMTR_BAD_FHSYNC, m_trc_curr_idx, "Bad FSYNC in frame."); } } @@ -546,12 +557,11 @@ bool TraceFmtDcdImpl::extractFrame() m_ex_frm_n_bytes-=2; ex_bytes -= 2; h_sync_bytes+=2; - - // TBD: output raw HSYNC data on raw frame channel. } else { // throw illegal HSYNC error. + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_DFMTR_BAD_FHSYNC, m_trc_curr_idx, "Bad HSYNC in frame."); } } @@ -565,22 +575,25 @@ bool TraceFmtDcdImpl::extractFrame() cont_process = true; } + // total bytes processed this pass + uint32_t total_processed = ex_bytes + f_sync_bytes + h_sync_bytes; + // output raw data on raw frame channel - packed raw. - if ((m_ex_frm_n_bytes == OCSD_DFRMTR_FRAME_SIZE) && m_b_output_packed_raw) + if (((m_ex_frm_n_bytes == OCSD_DFRMTR_FRAME_SIZE) || !cont_process) && m_b_output_packed_raw) { outputRawMonBytes( OCSD_OP_DATA, m_trc_curr_idx, OCSD_FRM_PACKED, - ex_bytes + f_sync_bytes + h_sync_bytes, + total_processed, m_in_block_base+m_in_block_processed, 0); } // update the processed count for the buffer - m_in_block_processed += m_ex_frm_n_bytes + f_sync_bytes + h_sync_bytes; + m_in_block_processed += total_processed; // update index past the processed data - m_trc_curr_idx += m_ex_frm_n_bytes + f_sync_bytes + h_sync_bytes; + m_trc_curr_idx += total_processed; return cont_process; } @@ -817,7 +830,7 @@ ocsd_err_t TraceFormatterFrameDecoder::Configure(uint32_t cfg_flags) m_pDecoder = new (std::nothrow) TraceFmtDcdImpl(); if(!m_pDecoder) return OCSD_ERR_MEM; } - m_pDecoder->m_cfgFlags = cfg_flags; + m_pDecoder->DecodeConfigure(cfg_flags); return OCSD_OK; } diff --git a/decoder/source/trc_gen_elem.cpp b/decoder/source/trc_gen_elem.cpp index d2536f5758a..b3ec75f059d 100644 --- a/decoder/source/trc_gen_elem.cpp +++ b/decoder/source/trc_gen_elem.cpp @@ -49,7 +49,7 @@ static const char *s_elem_descs[][2] = {"OCSD_GEN_TRC_ELEM_ADDR_NACC","Tracing in inaccessible memory area."}, {"OCSD_GEN_TRC_ELEM_ADDR_UNKNOWN","Tracing unknown address area."}, {"OCSD_GEN_TRC_ELEM_EXCEPTION","Exception"}, - {"OCSD_GEN_TRC_ELEM_EXCEPTION_RET","Expection return"}, + {"OCSD_GEN_TRC_ELEM_EXCEPTION_RET","Exception return"}, {"OCSD_GEN_TRC_ELEM_TIMESTAMP","Timestamp - preceding elements happeded before this time."}, {"OCSD_GEN_TRC_ELEM_CYCLE_COUNT","Cycle count - cycles since last cycle count value - associated with a preceding instruction range."}, {"OCSD_GEN_TRC_ELEM_EVENT","Event - numbered event or trigger"}, @@ -71,7 +71,8 @@ static const char *instr_sub_type[] = { "--- ", "b+link ", "A64:ret ", - "A64:eret " + "A64:eret ", + "V7:impl ret", }; #define ST_SIZE (sizeof(instr_sub_type) / sizeof(const char *)) @@ -105,12 +106,16 @@ void OcsdTraceElement::toString(std::string &str) const { case OCSD_GEN_TRC_ELEM_INSTR_RANGE: oss << "exec range=0x" << std::hex << st_addr << ":[0x" << en_addr << "] "; + oss << "num_i(" << std::dec << num_instr_range << ") "; + oss << "last_sz(" << last_instr_sz << ") "; oss << "(ISA=" << s_isa_str[(int)isa] << ") "; oss << ((last_instr_exec == 1) ? "E " : "N "); if((int)last_i_type < T_SIZE) oss << instr_type[last_i_type]; if((last_i_subtype != OCSD_S_INSTR_NONE) && ((int)last_i_subtype < ST_SIZE)) oss << instr_sub_type[last_i_subtype]; + if (last_instr_cond) + oss << " "; break; case OCSD_GEN_TRC_ELEM_ADDR_NACC: @@ -118,9 +123,14 @@ void OcsdTraceElement::toString(std::string &str) const break; case OCSD_GEN_TRC_ELEM_EXCEPTION: - if(excep_ret_addr == 1) + if (excep_ret_addr == 1) { - oss << "pref ret addr:0x" << std::hex << en_addr << "; "; + oss << "pref ret addr:0x" << std::hex << en_addr; + if (excep_ret_addr_br_tgt) + { + oss << " [addr also prev br tgt]"; + } + oss << "; "; } oss << "excep num (0x" << std::setfill('0') << std::setw(2) << std::hex << exception_number << ") "; break; @@ -150,6 +160,13 @@ void OcsdTraceElement::toString(std::string &str) const printSWInfoPkt(oss); break; + case OCSD_GEN_TRC_ELEM_EVENT: + if(trace_event.ev_type == EVENT_TRIGGER) + oss << " Trigger; "; + else if(trace_event.ev_type == EVENT_NUMBERED) + oss << " Numbered:" << std::dec << trace_event.ev_number << "; "; + break; + default: break; } if(has_cc) diff --git a/decoder/source/trc_printable_elem.cpp b/decoder/source/trc_printable_elem.cpp index 2fd49d97357..88c7bb226f4 100644 --- a/decoder/source/trc_printable_elem.cpp +++ b/decoder/source/trc_printable_elem.cpp @@ -63,12 +63,15 @@ void trcPrintableElem::getValStr(std::string &valStr, const int valTotalBitSize, int validChars = valValidBits / 4; if((valValidBits % 4) > 0) validChars++; - int QM = numHexChars - validChars; - while(QM) - { - QM--; - valStr += "?"; - } + if (validChars < numHexChars) + { + int QM = numHexChars - validChars; + while (QM) + { + QM--; + valStr += "?"; + } + } if(valValidBits > 32) { sprintf(szFormatBuffer,"%%0%dllX",validChars); // create the format diff --git a/decoder/tests/auto-fdo/autofdo.md b/decoder/tests/auto-fdo/autofdo.md new file mode 100644 index 00000000000..b1f22417b50 --- /dev/null +++ b/decoder/tests/auto-fdo/autofdo.md @@ -0,0 +1,523 @@ +AutoFDO and ARM Trace {#AutoFDO} +===================== + +@brief Using CoreSight trace and perf with OpenCSD for AutoFDO. + +## Introduction + +Feedback directed optimization (FDO, also know as profile guided +optimization - PGO) uses a profile of a program's execution to guide the +optmizations performed by the compiler. Traditionally, this involves +building an instrumented version of the program, which records a profile of +execution as it runs. The instrumentation adds significant runtime +overhead, possibly changing the behaviour of the program and it may not be +possible to run the instrumented program in a production environment +(e.g. where performance criteria must be met). + +AutoFDO uses facilities in the hardware to sample the behaviour of the +program in the production environment and generate the execution profile. +An improved profile can be obtained by including the branch history +(i.e. a record of the last branches taken) when generating an instruction +samples. On Arm systems, the ETM can be used to generate such records. + +The process can be broken down into the following steps: + +* Record execution trace of the program +* Convert the execution trace to instruction samples with branch histories +* Convert the instruction samples to source level profiles +* Use the source level profile with the compiler + +This article describes how to enable ETM trace on Arm targets running Linux +and use the ETM trace to generate AutoFDO profiles and compile an optimized +program. + + +## Execution trace on Arm targets + +Debug and trace of Arm targets is provided by CoreSight. This consists of +a set of components that allow access to debug logic, record (trace) the +execution of a processor and route this data through the system, collecting +it into a store. + +To record the execution of a processor, we require the following +components: + +* A trace source. The core contains a trace unit, called an ETM that emits + data describing the instructions executed by the core. +* Trace links. The trace data generated by the ETM must be moved through + the system to the component that collects the data (sink). Links + include: + * Funnels: merge multiple streams of data + * FIFOs: buffer data to smooth out bursts + * Replicators: send a stream of data to multiple components +* Sinks. These receive the trace data and store it or send it to an + external device: + * ETB: A small circular buffer (64-128 kilobytes) that stores the most + recent data + * ETR: A larger (several megabytes) buffer that uses system RAM to + store data + * TPIU: Sends data to an off-chip capture device (e.g. Arm DSTREAM) + +Each Arm SoC design may have a different layout (topology) of components. +This topology is described to the OS drivers by the platform's devicetree +or (in future) ACPI firmware. + +For application profiling, we need to store several megabytes of data +within the system, so will use ETR with the capture tool (perf) +periodically draining the buffer to a file. + +Even though we have a large capture buffer, the ETM can still generate a +lot of data very quickly - typically an ETM will generate ~1 bit of data +per instruction (depending on the workload), which results in 256Mbytes per +second for a core running at 2GHz. This leads to problems storing and +decoding such large volumes of data. AutoFDO uses samples of program +execution, so we can avoid this problem by using the ETM's features to +only record small slices of execution - e.g. collect ~5000 cycles of data +every 50M cycles. This reduces the data rate to a manageable level - a few +megabytes per minute. This technique is known as 'strobing'. + + +## Enabling trace + +### Driver support + +To collect ETM trace, the CoreSight drivers must be included in the +kernel. Some of the driver support is not yet included in the mainline +kernel and many targets are using older kernels. To enable CoreSight trace +on these targets, Arm have provided backports of the latest CoreSight +drivers and ETM strobing patch at: + + + +This repository can be cloned with: + +``` +git clone git://linux-arm.org/linux-coresight-backports.git +``` + +You can include these backports in your kernel by either merging the +appropriate branch using git or generating patches (using `git +format-patch`). + +For 4.9 based kernels, use the `coresight-4.9-etr-etm_strobe` branch: + +``` +git merge coresight-4.9-etr-etm_strobe +``` + +or + +``` +git format-patch --output-directory /output/dir v4.9..coresight-4.9-etr-etm_strobe +cd my_kernel +git am /output/dir/*.patch # or patch -p1 /output/dir/*.patch if not using git +``` + +For 4.14 based kernels, use the `coresight-4.14-etm_strobe` branch: + +``` +git merge coresight-4.14-etm_strobe +``` + +or + +``` +git format-patch --output-directory /output/dir v4.14..coresight-4.14-etm_strobe +cd my_kernel +git am /output/dir/*.patch # or patch -p1 /output/dir/*.patch if not using git +``` + +The CoreSight trace drivers must also be enabled in the kernel +configuration. This can be done using the configuration menu (`make +menuconfig`), selecting `Kernel hacking` / `CoreSight Tracing Support` and +enabling all options, or by setting the following in the configuration +file: + +``` +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_SINK_TPIU=y +CONFIG_CORESIGHT_SOURCE_ETM4X=y +CONFIG_CORESIGHT_DYNAMIC_REPLICATOR=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_CATU=y +``` + +Compile the kernel for your target in the usual way, e.g. + +``` +make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- +``` + +Each target may have a different layout of CoreSight components. To +collect trace into a sink, the kernel drivers need to know which other +devices need to be configured to route data from the source to the sink. +This is described in the devicetree (and in future, the ACPI tables). The +device tree will define which CoreSight devices are present in the system, +where they are located and how they are connected together. The devicetree +for some platforms includes a description of the platform's CoreSight +components, but in other cases you may have to ask the platform/SoC vendor +to supply it or create it yourself (see Appendix: Describing CoreSight in +Devicetree). + +Once the target has been booted with the devicetree describing the +CoreSight devices, you should find the devices in sysfs: + +``` +# ls /sys/bus/coresight/devices/ +28440000.etm 28540000.etm 28640000.etm 28740000.etm +28c03000.funnel 28c04000.etf 28c05000.replicator 28c06000.etr +28c07000.tpiu +``` + +### Perf tools + +The perf tool is used to capture execution trace, configuring the trace +sources to generate trace, routing the data to the sink and collecting the +data from the sink. + +Arm recommends to use the perf version corresponding to the kernel running +on the target. This can be built from the same kernel sources with + +``` +make -C tools/perf ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- +``` + +If the post-processing (`perf inject`) of the captured data is not being +done on the target, then the OpenCSD library is not required for this build +of perf. + +Trace is captured by collecting the `cs_etm` event from perf. The sink +to collect data into is specified as a parameter of this event. Trace can +also be restricted to user space or kernel space with 'u' or 'k' +parameters. For example: + +``` +perf record -e cs_etm/@28c06000.etr/u --per-thread -- /bin/ls +``` + +Will record the userspace execution of '/bin/ls' into the ETR located at +0x28c06000. Note the `--per-thread` option is required - perf currently +only supports trace of a single thread of execution. CPU wide trace is a +work in progresss. + + +## Processing trace and profiles + +perf is also used to convert the execution trace an instruction profile. +This requires a different build of perf, using the version of perf from +Linux v4.17 or later, as the trace processing code isn't included in the +driver backports. Trace decode is provided by the OpenCSD library +(), v0.9.1 or later. This is packaged +for debian testing (install the libopencsd0, libopencsd-dev packages) or +can be compiled from source and installed. + +The autoFDO tool is used to convert the +instruction profiles to source profiles for the GCC and clang/llvm +compilers. + + +## Recording and profiling + +Once trace collection using perf is working, we can now use it to profile +an application. + +The application must be compiled to include sufficient debug information to +map instructions back to source lines. For GCC, use the `-g1` or `-gmlt` +options. For clang/llvm, also add the `-fdebug-info-for-profiling` option. + +perf identifies the active program or library using the build identifier +stored in the elf file. This should be added at link time with the compiler +flag `-Wl,--build-id=sha1`. + +The next step is to record the execution trace of the application using the +perf tool. The ETM strobing should be configured before running the perf +tool. There are two parameters: + + * window size: A number of CPU cycles (W) + * period: Trace is enabled for W cycle every _period_ * W cycles. + +For example, a typical configuration is to use a window size of 5000 cycles +and a period of 10000 - this will collect 5000 cycles of trace every 50M +cycles. With these proof-of-concept patches, the strobe parameters are +configured via sysfs - each ETM will have `strobe_window` and +`strobe_period` parameters in `/sys/bus/coresight/devices/NNNNNNNN.etm` and +these values will have to be written to each (In a future version, this +will be integrated into the drivers and perf tool). The `record.sh` +script in this directory [`/decoder/tests/auto-fdo`] automates this process. + +To collect trace from an application using ETM strobing, run: + +``` +taskset -c 0 ./record.sh --strobe 5000 10000 28c06000.etr ./my_application arg1 arg2 +``` + +The taskset command is used to ensure the process stays on the same CPU +during execution. + +The raw trace can be examined using the `perf report` command: + +``` +perf report -D -i perf.data --stdio +``` + +For example: + +``` +0x1d370 [0x30]: PERF_RECORD_AUXTRACE size: 0x2003c0 offset: 0 ref: 0x39ba881d145f8639 idx: 0 tid: 4551 cpu: -1 + +. ... CoreSight ETM Trace data: size 2098112 bytes + Idx:0; ID:12; I_ASYNC : Alignment Synchronisation. + Idx:12; ID:12; I_TRACE_INFO : Trace Info.; INFO=0x0 + Idx:17; ID:12; I_ADDR_L_64IS0 : Address, Long, 64 bit, IS0.; Addr=0xFFFF000008A4991C; + Idx:48; ID:14; I_ASYNC : Alignment Synchronisation. + Idx:60; ID:14; I_TRACE_INFO : Trace Info.; INFO=0x0 + Idx:65; ID:14; I_ADDR_L_64IS0 : Address, Long, 64 bit, IS0.; Addr=0xFFFF000008A4991C; + Idx:96; ID:14; I_ASYNC : Alignment Synchronisation. + Idx:108; ID:14; I_TRACE_INFO : Trace Info.; INFO=0x0 + Idx:113; ID:14; I_ADDR_L_64IS0 : Address, Long, 64 bit, IS0.; Addr=0xFFFF000008A4991C; + Idx:122; ID:14; I_TRACE_ON : Trace On. + Idx:123; ID:14; I_ADDR_CTXT_L_64IS0 : Address & Context, Long, 64 bit, IS0.; Addr=0x0000000000407B00; Ctxt: AArch64,EL0, NS; + Idx:134; ID:14; I_ATOM_F3 : Atom format 3.; ENN + Idx:135; ID:14; I_ATOM_F5 : Atom format 5.; NENEN + Idx:136; ID:14; I_ATOM_F5 : Atom format 5.; ENENE + Idx:137; ID:14; I_ATOM_F5 : Atom format 5.; NENEN + Idx:138; ID:14; I_ATOM_F3 : Atom format 3.; ENN + Idx:139; ID:14; I_ATOM_F3 : Atom format 3.; NNE + Idx:140; ID:14; I_ATOM_F1 : Atom format 1.; E +..... +``` + +The execution trace is then converted to an instruction profile using +the perf build with trace decode support. This may be done on a different +machine than that which collected the trace (e.g. when cross compiling for +an embedded target). The `perf inject` command +decodes the execution trace and generates periodic instruction samples, +with branch histories: + +``` +perf inject -i perf.data -o inj.data --itrace=i100000il +``` + +The `--itrace` option configures the instruction sample behaviour: + +* `i100000i` generates an instruction sample every 100000 instructions + (only instruction count periods are currently supported, future versions + may support time or cycle count periods) +* `l` includes the branch histories on each sample +* `b` generates a sample on each branch (not used here) + +Perf requires the original program binaries to decode the execution trace. +If running the `inject` command on a different system than the trace was +captured on, then the binary and any shared libraries must be added to +perf's cache with: + +``` +perf buildid-cache -a /path/to/binary_or_library +``` + +`perf report` can also be used to show the instruction samples: + +``` +perf report -D -i inj.data --stdio +....... +0x1528 [0x630]: PERF_RECORD_SAMPLE(IP, 0x2): 4551/4551: 0x434b98 period: 3093 addr: 0 +... branch stack: nr:64 +..... 0: 0000000000434b58 -> 0000000000434b68 0 cycles P 0 +..... 1: 0000000000436a88 -> 0000000000434b4c 0 cycles P 0 +..... 2: 0000000000436a64 -> 0000000000436a78 0 cycles P 0 +..... 3: 00000000004369d0 -> 0000000000436a60 0 cycles P 0 +..... 4: 000000000043693c -> 00000000004369cc 0 cycles P 0 +..... 5: 00000000004368a8 -> 0000000000436928 0 cycles P 0 +..... 6: 000000000042d070 -> 00000000004368a8 0 cycles P 0 +..... 7: 000000000042d108 -> 000000000042d070 0 cycles P 0 +....... +..... 57: 0000000000448ee0 -> 0000000000448f24 0 cycles P 0 +..... 58: 0000000000448ea4 -> 0000000000448ebc 0 cycles P 0 +..... 59: 0000000000448e20 -> 0000000000448e94 0 cycles P 0 +..... 60: 0000000000448da8 -> 0000000000448ddc 0 cycles P 0 +..... 61: 00000000004486f4 -> 0000000000448da8 0 cycles P 0 +..... 62: 00000000004480fc -> 00000000004486d4 0 cycles P 0 +..... 63: 0000000000448658 -> 00000000004480ec 0 cycles P 0 + ... thread: program1:4551 + ...... dso: /home/root/program1 +....... +``` + +The instruction samples produced by `perf inject` is then passed to the +autofdo tool to generate source level profiles for the compiler. For +clang/LLVM: + +``` +create_llvm_prof -binary=/path/to/binary -profile=inj.data -out=program.llvmprof +``` + +And for GCC: + +``` +create_gcov -binary=/path/to/binary -profile=inj.data -gcov_version=1 -gcov=program.gcov +``` + +The profiles can be viewed with: + +``` +llvm-profdata show -sample program.llvmprof +``` + +Or, for GCC: + +``` +dump_gcov -gcov_version=1 program.gcov +``` + +## Using profile in the compiler + +The profile produced by the above steps can then be passed to the compiler +to optimize the next build of the program. + +For GCC, use the `-fauto-profile` option: + +``` +gcc -O2 -fauto-profile=program.gcov -o program program.c +``` + +For Clang, use the `-fprofile-sample-use` option: + +``` +clang -O2 -fprofile-sample-use=program.llvmprof -o program program.c +``` + + +## Summary + +The basic commands to run an application and create a compiler profile are: + +``` +taskset -c 0 ./record.sh --strobe 5000 10000 28c06000.etr ./my_application arg1 arg2 +perf inject -i perf.data -o inj.data --itrace=i100000il +create_llvm_prof -binary=/path/to/binary -profile=inj.data -out=program.llvmprof +``` + +Use `create_gcov` for gcc. + + +## References + +* AutoFDO tool: +* GCC's wiki on autofdo: , +* Google paper: +* CoreSight kernel docs: Documentation/trace/coresight.txt + + +## Appendix: Describing CoreSight in Devicetree + + +Each component has an entry in the device tree that describes its: + +* type: The `compatible` field defines which driver to use +* location: A `reg` defines the component's address and size on the bus +* clocks: The `clocks` and `clock-names` fields state which clock provides + the `apb_pclk` clock. +* connections to other components: `port` and `ports` field link the + component to ports of other components + +To create the device tree, some information about the platform is required: + +* The memory address of the CoreSight components. This is the address in + the CPU's address space where the CPU can access each CoreSight + component. +* The connections between the components. + +This information can be found in the SoC's reference manual or you may need +to ask the platform/SoC vendor to supply it. + +An ETMv4 source is declared with a section like this: + +``` + etm0: etm@22040000 { + compatible = "arm,coresight-etm4x", "arm,primecell"; + reg = <0 0x22040000 0 0x1000>; + + cpu = <&A72_0>; + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + port { + cluster0_etm0_out_port: endpoint { + remote-endpoint = <&cluster0_funnel_in_port0>; + }; + }; + }; +``` + +This describes an ETMv4 attached to core A72_0, located at 0x22040000, with +its output linked to port 0 of a funnel. The funnel is described with: + +``` + funnel@220c0000 { /* cluster0 funnel */ + compatible = "arm,coresight-funnel", "arm,primecell"; + reg = <0 0x220c0000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + cluster0_funnel_out_port: endpoint { + remote-endpoint = <&main_funnel_in_port0>; + }; + }; + + port@1 { + reg = <0>; + cluster0_funnel_in_port0: endpoint { + slave-mode; + remote-endpoint = <&cluster0_etm0_out_port>; + }; + }; + + port@2 { + reg = <1>; + cluster0_funnel_in_port1: endpoint { + slave-mode; + remote-endpoint = <&cluster0_etm1_out_port>; + }; + }; + }; + }; +``` + +This describes a funnel located at 0x220c0000, receiving data from 2 ETMs +and sending the merged data to another funnel. We continue describing +components with similar blocks until we reach the sink (an ETR): + +``` + etr@20070000 { + compatible = "arm,coresight-tmc", "arm,primecell"; + reg = <0 0x20070000 0 0x1000>; + iommus = <&smmu_etr 0>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + port { + etr_in_port: endpoint { + slave-mode; + remote-endpoint = <&replicator_out_port1>; + }; + }; + }; +``` + +Full descriptions of the properties of each component can be found in the +Linux source at Documentation/devicetree/bindings/arm/coresight.txt. +The Arm Juno platform's devicetree (arch/arm64/boot/dts/arm) provides an example +description of CoreSight description. + +Many systems include a TPIU for off-chip trace. While this isn't required +for self-hosted trace, it should still be included in the devicetree. This +allows the drivers to access it to ensure it is put into a disabled state, +otherwise it may limit the trace bandwidth causing data loss. diff --git a/decoder/tests/auto-fdo/record.sh b/decoder/tests/auto-fdo/record.sh new file mode 100755 index 00000000000..16d4ba22db3 --- /dev/null +++ b/decoder/tests/auto-fdo/record.sh @@ -0,0 +1,68 @@ +#!/bin/sh + +BUFFER_ETF_A53=ec802000.etf +BUFFER_ETF_A73=ed002000.etf +BUFFER_ETF_SYS=ec036000.etf +BUFFER_ETR=ec033000.etr + +OUT_FILE=perf.data + +STROBE= + +while :; do + case $1 in + --strobe) + STROBE=y + WINDOW=$2 + PERIOD=$3 + shift 3 + ;; + + *) + break ;; + esac +done + +case $1 in + etr) + BUFFER=$BUFFER_ETR + ;; + + etf-sys) + BUFFER=$BUFFER_ETF_SYS + ;; + + "") + BUFFER=$BUFFER_ETR + ;; + + *) + BUFFER=$1 + ;; +esac + +shift 1 + +case $0 in + /*) F=$0 ;; + *) F=$(pwd)/$0 ;; +esac + +SCRIPT_DIR=$(dirname $F) + +if [ "$STROBE" ]; then + for e in /sys/bus/coresight/devices/*.etm/; do + printf "%x" $WINDOW | sudo tee $e/strobe_window > /dev/null + printf "%x" $PERIOD | sudo tee $e/strobe_period > /dev/null + done +fi + +PERF=$SCRIPT_DIR/perf + +export LD_LIBRARY_PATH=$SCRIPT_DIR:$LD_LIBRARY_PATH + +sudo LD_LIBRARY_PATH=$SCRIPT_DIR:$LD_LIBRARY_PATH $PERF record $PERF_ARGS -e cs_etm/@$BUFFER/u --per-thread "$@" + +sudo chown $(id -u):$(id -g) $OUT_FILE + + diff --git a/decoder/tests/build/linux/c_api_pkt_print_test/makefile b/decoder/tests/build/linux/c_api_pkt_print_test/makefile index 9fe72477249..b0b56044e03 100644 --- a/decoder/tests/build/linux/c_api_pkt_print_test/makefile +++ b/decoder/tests/build/linux/c_api_pkt_print_test/makefile @@ -33,9 +33,7 @@ # CC := $(MASTER_CC) -CC_FLAGS := $(MASTER_CC_FLAGS) -Wno-switch LINKER := $(MASTER_LINKER) -LINKER_FLAGS := $(MASTER_LINKER_FLAGS) PROG = c_api_pkt_print_test @@ -53,22 +51,21 @@ OBJECTS = $(BUILD_DIR)/c_api_pkt_print_test.o LIBS = -L$(LIB_TARGET_DIR) -l$(LIB_BASE_NAME) -l$(LIB_CAPI_NAME) \ -L$(LIB_TEST_TARGET_DIR) -l_echo_test_dcd -all: build_dir test_app copy_libs +all: build_dir copy_libs -test_app: $(OBJECTS) $(BIN_TEST_TARGET_DIR)/$(PROG) +test_app: $(BIN_TEST_TARGET_DIR)/$(PROG) - $(BIN_TEST_TARGET_DIR)/$(PROG): + $(BIN_TEST_TARGET_DIR)/$(PROG): $(OBJECTS) mkdir -p $(BIN_TEST_TARGET_DIR) - $(LINKER) $(LINKER_FLAGS) $(OBJECTS) -Wl,--start-group $(LIBS) -Wl,--end-group -o $(BIN_TEST_TARGET_DIR)/$(PROG) + $(LINKER) $(LDFLAGS) $(OBJECTS) -Wl,--start-group $(LIBS) -Wl,--end-group -o $(BIN_TEST_TARGET_DIR)/$(PROG) cp $(LIB_TARGET_DIR)/*.so . build_dir: mkdir -p $(BUILD_DIR) .PHONY: copy_libs - -copy_libs: +copy_libs: $(BIN_TEST_TARGET_DIR)/$(PROG) cp $(LIB_TARGET_DIR)/*.so $(BIN_TEST_TARGET_DIR)/. @@ -81,12 +78,14 @@ DEPS := $(OBJECTS:%.o=%.d) ## object compile $(BUILD_DIR)/%.o : %.c - $(CC) $(CC_FLAGS) $(CC_INCLUDES) -MMD $< -o $@ + $(CC) $(CFLAGS) $(CC_INCLUDES) -MMD $< -o $@ #### clean .PHONY: clean clean : - rm -f $(BIN_TEST_TARGET_DIR)/$(PROG) $(OBJECTS) - rm -f $(DEPS) + -rm $(BIN_TEST_TARGET_DIR)/$(PROG) $(OBJECTS) + -rm $(DEPS) + -rm ./*.so + -rmdir $(BUILD_DIR) # end of file makefile diff --git a/decoder/tests/build/linux/echo_test_dcd_lib/makefile b/decoder/tests/build/linux/echo_test_dcd_lib/makefile index 2483170dc0d..31ca38fe12e 100644 --- a/decoder/tests/build/linux/echo_test_dcd_lib/makefile +++ b/decoder/tests/build/linux/echo_test_dcd_lib/makefile @@ -31,13 +31,8 @@ # CC := $(MASTER_CC) -LINKER := $(MASTER_LINKER) LIB := $(MASTER_LIB) -CC_FLAGS := $(MASTER_CC_FLAGS) -fpic -Wno-switch -LIB_FLAGS := $(MASTER_LIB_FLAGS) -LINKER_FLAGS := $(MASTER_LINKER_FLAGS) -shared - LIB_NAME = lib_echo_test_dcd BUILD_DIR=./$(PLAT_DIR) @@ -53,21 +48,15 @@ CC_INCLUDES = \ OBJECTS = $(BUILD_DIR)/ext_dcd_echo_test.o \ $(BUILD_DIR)/ext_dcd_echo_test_fact.o -all: build_dir $(OBJECTS) $(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a +all: build_dir $(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a -$(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a: +$(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a: $(OBJECTS) mkdir -p $(LIB_TEST_TARGET_DIR) - $(LIB) $(LIB_FLAGS) $(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a $(OBJECTS) + $(LIB) $(ARFLAGS) $(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a $(OBJECTS) build_dir: mkdir -p $(BUILD_DIR) -.PHONY: copy_libs - -copy_libs: - cp $(LIB_TARGET_DIR)/*.so $(BIN_TEST_TARGET_DIR)/. - - #### build rules ## object dependencies DEPS := $(OBJECTS:%.o=%.d) @@ -76,13 +65,14 @@ DEPS := $(OBJECTS:%.o=%.d) ## object compile $(BUILD_DIR)/%.o : %.c - $(CC) $(CC_FLAGS) $(CC_INCLUDES) -MMD $< -o $@ + $(CC) $(CFLAGS) $(CC_INCLUDES) -MMD $< -o $@ #### clean .PHONY: clean clean: - rm -f $(OBJECTS) - rm -f $(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a - rm -f $(DEPS) + -rm $(OBJECTS) + -rm $(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a + -rm $(DEPS) + -rmdir $(BUILD_DIR) $(LIB_TEST_TARGET_DIR) # end of file makefile diff --git a/decoder/tests/build/linux/mem_buffer_eg/makefile b/decoder/tests/build/linux/mem_buffer_eg/makefile new file mode 100644 index 00000000000..850ed497daf --- /dev/null +++ b/decoder/tests/build/linux/mem_buffer_eg/makefile @@ -0,0 +1,90 @@ +######################################################## +# Copyright 2019 ARM Limited. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +################################################################################# + +######## +# RCTDL - test makefile for snapshot lister test. +# + +CXX := $(MASTER_CXX) +LINKER := $(MASTER_LINKER) + +PROG = mem-buffer-eg + +BUILD_DIR=./$(PLAT_DIR) + +VPATH = $(OCSD_TESTS)/source + +CXX_INCLUDES = \ + -I$(OCSD_TESTS)/source \ + -I$(OCSD_INCLUDE) \ + -I$(OCSD_TESTS)/snapshot_parser_lib/include + +OBJECTS = $(BUILD_DIR)/mem_buff_demo.o + +LIBS = -L$(LIB_TEST_TARGET_DIR) -lsnapshot_parser \ + -L$(LIB_TARGET_DIR) -l$(LIB_BASE_NAME) + +all: build_dir copy_libs + +test_app: $(BIN_TEST_TARGET_DIR)/$(PROG) + + + $(BIN_TEST_TARGET_DIR)/$(PROG): $(OBJECTS) + mkdir -p $(BIN_TEST_TARGET_DIR) + $(LINKER) $(LDFLAGS) $(OBJECTS) -Wl,--start-group $(LIBS) -Wl,--end-group -o $(BIN_TEST_TARGET_DIR)/$(PROG) + +build_dir: + mkdir -p $(BUILD_DIR) + +.PHONY: copy_libs +copy_libs: $(BIN_TEST_TARGET_DIR)/$(PROG) + cp $(LIB_TARGET_DIR)/*.so* $(BIN_TEST_TARGET_DIR)/. + + + +#### build rules +## object dependencies +DEPS := $(OBJECTS:%.o=%.d) + +-include $(DEPS) + +## object compile +$(BUILD_DIR)/%.o : %.cpp + $(CXX) $(CXXFLAGS) $(CXX_INCLUDES) -MMD $< -o $@ + +#### clean +.PHONY: clean +clean : + -rm $(BIN_TEST_TARGET_DIR)/$(PROG) $(OBJECTS) + -rm $(DEPS) + -rm $(BIN_TEST_TARGET_DIR)/*.so* + -rmdir $(BUILD_DIR) + +# end of file makefile diff --git a/decoder/tests/build/linux/snapshot_parser_lib/makefile b/decoder/tests/build/linux/snapshot_parser_lib/makefile index ae3863f3932..295bab61780 100644 --- a/decoder/tests/build/linux/snapshot_parser_lib/makefile +++ b/decoder/tests/build/linux/snapshot_parser_lib/makefile @@ -32,15 +32,14 @@ # ######################################################################## -CPP := $(MASTER_CPP) +CXX := $(MASTER_CXX) LINKER := $(MASTER_LINKER) LIB := $(MASTER_LIB) # avoid build warnings in donated test code WSUPPRESS= -Wno-deprecated-declarations -Wno-unused-variable -Wno-reorder -CPP_FLAGS := $(MASTER_CPP_FLAGS) -fpic -Wno-switch $(WSUPPRESS) -LIB_FLAGS := $(MASTER_LIB_FLAGS) +CXXFLAGS += $(WSUPPRESS) LIB_NAME = libsnapshot_parser @@ -53,7 +52,7 @@ PARSER_INCLUDE=$(PARSER_ROOT)/include VPATH= $(PARSER_SOURCE) -CPP_INCLUDES= \ +CXX_INCLUDES= \ -I$(PARSER_INCLUDE) \ -I$(OCSD_INCLUDE) @@ -64,11 +63,11 @@ OBJECTS=$(BUILD_DIR)/device_info.o \ $(BUILD_DIR)/snapshot_reader.o \ $(BUILD_DIR)/ss_to_dcdtree.o -all: build_dir $(OBJECTS) $(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a +all: build_dir $(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a -$(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a: +$(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a: $(OBJECTS) mkdir -p $(LIB_TEST_TARGET_DIR) - $(LIB) $(LIB_FLAGS) $(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a $(OBJECTS) + $(LIB) $(ARFLAGS) $(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a $(OBJECTS) build_dir: mkdir -p $(BUILD_DIR) @@ -82,11 +81,12 @@ DEPS := $(OBJECTS:%.o=%.d) ## object compile $(BUILD_DIR)/%.o : %.cpp - $(CPP) $(CPP_FLAGS) $(CPP_INCLUDES) -MMD $< -o $@ + $(CXX) $(CXXFLAGS) $(CXX_INCLUDES) -MMD $< -o $@ ### clean .PHONY: clean clean: - rm -f $(OBJECTS) - rm -f $(DEPS) - rm -f $(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a + -rm $(OBJECTS) + -rm $(DEPS) + -rm $(LIB_TEST_TARGET_DIR)/$(LIB_NAME).a + -rmdir $(BUILD_DIR) $(LIB_TEST_TARGET_DIR) diff --git a/decoder/tests/build/linux/trc_pkt_lister/makefile b/decoder/tests/build/linux/trc_pkt_lister/makefile index 3447b930c9c..54ce27d351a 100644 --- a/decoder/tests/build/linux/trc_pkt_lister/makefile +++ b/decoder/tests/build/linux/trc_pkt_lister/makefile @@ -32,10 +32,8 @@ # RCTDL - test makefile for snapshot lister test. # -CPP := $(MASTER_CPP) -CPP_FLAGS := $(MASTER_CPP_FLAGS) -Wno-switch +CXX := $(MASTER_CXX) LINKER := $(MASTER_LINKER) -LINKER_FLAGS := $(MASTER_LINKER_FLAGS) PROG = trc_pkt_lister @@ -43,7 +41,7 @@ BUILD_DIR=./$(PLAT_DIR) VPATH = $(OCSD_TESTS)/source -CPP_INCLUDES = \ +CXX_INCLUDES = \ -I$(OCSD_TESTS)/source \ -I$(OCSD_INCLUDE) \ -I$(OCSD_TESTS)/snapshot_parser_lib/include @@ -53,22 +51,21 @@ OBJECTS = $(BUILD_DIR)/trc_pkt_lister.o LIBS = -L$(LIB_TEST_TARGET_DIR) -lsnapshot_parser \ -L$(LIB_TARGET_DIR) -l$(LIB_BASE_NAME) -all: build_dir test_app copy_libs +all: build_dir copy_libs -test_app: $(OBJECTS) $(BIN_TEST_TARGET_DIR)/$(PROG) +test_app: $(BIN_TEST_TARGET_DIR)/$(PROG) - $(BIN_TEST_TARGET_DIR)/$(PROG): + $(BIN_TEST_TARGET_DIR)/$(PROG): $(OBJECTS) mkdir -p $(BIN_TEST_TARGET_DIR) - $(LINKER) $(LINKER_FLAGS) $(OBJECTS) -Wl,--start-group $(LIBS) -Wl,--end-group -o $(BIN_TEST_TARGET_DIR)/$(PROG) + $(LINKER) $(LDFLAGS) $(OBJECTS) -Wl,--start-group $(LIBS) -Wl,--end-group -o $(BIN_TEST_TARGET_DIR)/$(PROG) build_dir: mkdir -p $(BUILD_DIR) .PHONY: copy_libs - -copy_libs: - cp $(LIB_TARGET_DIR)/*.so $(BIN_TEST_TARGET_DIR)/. +copy_libs: $(BIN_TEST_TARGET_DIR)/$(PROG) + cp $(LIB_TARGET_DIR)/*.so* $(BIN_TEST_TARGET_DIR)/. @@ -80,12 +77,14 @@ DEPS := $(OBJECTS:%.o=%.d) ## object compile $(BUILD_DIR)/%.o : %.cpp - $(CPP) $(CPP_FLAGS) $(CPP_INCLUDES) -MMD $< -o $@ + $(CXX) $(CXXFLAGS) $(CXX_INCLUDES) -MMD $< -o $@ #### clean .PHONY: clean clean : - rm -f $(BIN_TEST_TARGET_DIR)/$(PROG) $(OBJECTS) - rm -f $(DEPS) + -rm $(BIN_TEST_TARGET_DIR)/$(PROG) $(OBJECTS) + -rm $(DEPS) + -rm $(BIN_TEST_TARGET_DIR)/*.so* + -rmdir $(BUILD_DIR) # end of file makefile diff --git a/decoder/tests/build/win-vs2015/mem-buffer-eg/mem-buffer-eg.vcxproj b/decoder/tests/build/win-vs2015/mem-buffer-eg/mem-buffer-eg.vcxproj new file mode 100644 index 00000000000..08f93d47d08 --- /dev/null +++ b/decoder/tests/build/win-vs2015/mem-buffer-eg/mem-buffer-eg.vcxproj @@ -0,0 +1,154 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + {BC090130-2C53-4CF6-8AD4-37BF72B8D01A} + membuffereg + 8.1 + + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\..\bin\win$(PlatformArchitecture)\dbg\ + $(Platform)\$(Configuration)\ + + + ..\..\..\bin\win$(PlatformArchitecture)\dbg\ + $(Platform)\$(Configuration)\ + + + ..\..\..\bin\win$(PlatformArchitecture)\rel\ + + + ..\..\..\bin\win$(PlatformArchitecture)\dbg\ + + + + Level3 + Disabled + + + ..\..\..\..\include + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + ..\..\..\..\lib\win$(PlatformArchitecture)\dbg\;..\..\..\..\tests\lib\win$(PlatformArchitecture)\dbg\ + lib$(LIB_BASE_NAME).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + true + + + + + Level3 + Disabled + true + ..\..\..\..\include + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + ..\..\..\..\lib\win$(PlatformArchitecture)\dbg\;..\..\..\..\tests\lib\win$(PlatformArchitecture)\dbg\ + + + + + Level3 + MaxSpeed + true + true + true + ..\..\..\..\include + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + true + true + ..\..\..\..\lib\win$(PlatformArchitecture)\rel\;..\..\..\..\tests\lib\win$(PlatformArchitecture)\rel\ + + + + + Level3 + MaxSpeed + true + true + true + ..\..\..\..\include + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + true + true + ..\..\..\..\lib\win$(PlatformArchitecture)\rel\;..\..\..\..\tests\lib\win$(PlatformArchitecture)\rel\ + + + + + + \ No newline at end of file diff --git a/decoder/tests/build/win-vs2015/mem-buffer-eg/mem-buffer-eg.vcxproj.filters b/decoder/tests/build/win-vs2015/mem-buffer-eg/mem-buffer-eg.vcxproj.filters new file mode 100644 index 00000000000..ce99a9eb1d7 --- /dev/null +++ b/decoder/tests/build/win-vs2015/mem-buffer-eg/mem-buffer-eg.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/decoder/tests/ext_dcd_test_eg/c_api_echo_test/ext_dcd_echo_test.c b/decoder/tests/ext_dcd_test_eg/c_api_echo_test/ext_dcd_echo_test.c index 32365efb216..e2ad0454e4e 100644 --- a/decoder/tests/ext_dcd_test_eg/c_api_echo_test/ext_dcd_echo_test.c +++ b/decoder/tests/ext_dcd_test_eg/c_api_echo_test/ext_dcd_echo_test.c @@ -330,7 +330,7 @@ void print_test_cov_results(echo_decoder_t *decoder) if (coverage[TEST_COV_MSGLOG_CB] == TEST_RES_OK) /* check we can use the msg logger for outputting the results */ lib_cb_LogMsg(p_fns, OCSD_ERR_SEV_ERROR, coverage_message); else - printf(coverage_message); + printf("%s", coverage_message); } } diff --git a/decoder/tests/run_pkt_decode_tests.bash b/decoder/tests/run_pkt_decode_tests.bash new file mode 100755 index 00000000000..56b1cbfd38b --- /dev/null +++ b/decoder/tests/run_pkt_decode_tests.bash @@ -0,0 +1,78 @@ +#!/bin/bash +################################################################################# +# Copyright 2018 ARM. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +################################################################################# +# OpenCSD library: Test script. +# +# Test script to run packet lister on each of the snapshots retained with the repository. +# No attempt is made to compare output results to previous versions, (output formatting +# may change due to bugfix / enhancements) or assess the validity of the trace output. +# +################################################################################# + +OUT_DIR=./results +SNAPSHOT_DIR=./snapshots +BIN_DIR=./bin/linux64/rel + +# directories for tests using full decode +declare -a test_dirs_decode=( "juno-ret-stck" + "a57_single_step" + "bugfix-exact-match" + "juno-uname-001" + "juno-uname-002" + "juno_r1_1" + "tc2-ptm-rstk-t32" + "trace_cov_a15" + "stm_only" + "stm_only-2" + "stm_only-juno" + "TC2" + "Snowball" + "test-file-mem-offsets" + ) + + +echo "Running trc_pkt_lister on snapshot directories." + +mkdir -p ${OUT_DIR} + +# === test the decode set === +export LD_LIBRARY_PATH=${BIN_DIR}/. + +for test_dir in "${test_dirs_decode[@]}" +do + echo "Testing $test_dir..." + ${BIN_DIR}/trc_pkt_lister -ss_dir "${SNAPSHOT_DIR}/$test_dir" -decode -logfilename "${OUT_DIR}/$test_dir.ppl" + echo "Done : Return $?" +done + +# === test the TPIU deformatter === +echo "Testing a55-test-tpiu..." +${BIN_DIR}/trc_pkt_lister -ss_dir "${SNAPSHOT_DIR}/a55-test-tpiu" -dstream_format -o_raw_packed -o_raw_unpacked -logfilename "${OUT_DIR}/a55-test-tpiu.ppl" +echo "Done : Return $?" diff --git a/decoder/tests/snapshot_parser_lib/source/ss_to_dcdtree.cpp b/decoder/tests/snapshot_parser_lib/source/ss_to_dcdtree.cpp index 81ae82ba815..4eeec732c15 100644 --- a/decoder/tests/snapshot_parser_lib/source/ss_to_dcdtree.cpp +++ b/decoder/tests/snapshot_parser_lib/source/ss_to_dcdtree.cpp @@ -79,15 +79,19 @@ bool CreateDcdTreeFromSnapShot::createDecodeTree(const std::string &SourceName, if(m_pReader->getTraceBufferSourceTree(SourceName, tree)) { int numDecodersCreated = 0; // count how many we create - if none then give up. + uint32_t formatter_flags = OCSD_DFRMTR_FRAME_MEM_ALIGN; /* make a note of the trace binary file name + path to ss directory */ m_BufferFileName = m_pReader->getSnapShotDir() + tree.buffer_info.dataFileName; ocsd_dcd_tree_src_t src_format = tree.buffer_info.dataFormat == "source_data" ? OCSD_TRC_SRC_SINGLE : OCSD_TRC_SRC_FRAME_FORMATTED; + if (tree.buffer_info.dataFormat == "dstream_coresight") + formatter_flags = OCSD_DFRMTR_HAS_FSYNCS; + /* create the initial device tree */ // TBD: handle syncs / hsyncs data from TPIU - m_pDecodeTree = DecodeTree::CreateDecodeTree(src_format,OCSD_DFRMTR_FRAME_MEM_ALIGN); + m_pDecodeTree = DecodeTree::CreateDecodeTree(src_format, formatter_flags); if(m_pDecodeTree == 0) { LogError("Failed to create decode tree object\n"); @@ -507,15 +511,24 @@ void CreateDcdTreeFromSnapShot::processDumpfiles(std::vector &d while(it != dumps.end()) { dumpFilePathName = m_pReader->getSnapShotDir() + it->path; - if(!TrcMemAccessorFile::isExistingFileAccessor(dumpFilePathName)) - { - ocsd_err_t err = m_pDecodeTree->addBinFileMemAcc(it->address,OCSD_MEM_SPACE_ANY,dumpFilePathName); - if(err != OCSD_OK) - { - std::ostringstream oss; - oss << "Failed to create memory accessor for file " << dumpFilePathName << "."; - LogError(ocsdError(OCSD_ERR_SEV_ERROR,err,oss.str())); - } + ocsd_file_mem_region_t region; + ocsd_err_t err = OCSD_OK; + + region.start_address = it->address; + region.file_offset = it->offset; + region.region_size = it->length; + + // ensure we respect optional length and offset parameter and + // allow multiple dump entries with same file name to define regions + if (!TrcMemAccessorFile::isExistingFileAccessor(dumpFilePathName)) + err = m_pDecodeTree->addBinFileRegionMemAcc(®ion, 1, OCSD_MEM_SPACE_ANY, dumpFilePathName); + else + err = m_pDecodeTree->updateBinFileRegionMemAcc(®ion, 1, OCSD_MEM_SPACE_ANY, dumpFilePathName); + if(err != OCSD_OK) + { + std::ostringstream oss; + oss << "Failed to create memory accessor for file " << dumpFilePathName << "."; + LogError(ocsdError(OCSD_ERR_SEV_ERROR,err,oss.str())); } it++; } diff --git a/decoder/tests/source/c_api_pkt_print_test.c b/decoder/tests/source/c_api_pkt_print_test.c index 85bd9bf258b..02c589e4f27 100644 --- a/decoder/tests/source/c_api_pkt_print_test.c +++ b/decoder/tests/source/c_api_pkt_print_test.c @@ -63,12 +63,17 @@ /* path to test snapshots, relative to tests/bin// build output dir */ #ifdef _WIN32 -const char *default_path_to_snapshot = "..\\..\\..\\snapshots\\juno_r1_1\\"; -const char *tc2_snapshot = "..\\..\\..\\snapshots\\TC2\\"; +const char *default_base_snapshot_path="..\\..\\..\\snapshots"; +const char *juno_snapshot = "\\juno_r1_1\\"; +const char *tc2_snapshot = "\\TC2\\"; #else -const char *default_path_to_snapshot = "../../../snapshots/juno_r1_1/"; -const char *tc2_snapshot = "../../../snapshots/TC2/"; +const char *default_base_snapshot_path = "../../snapshots"; +const char *juno_snapshot = "/juno_r1_1/"; +const char *tc2_snapshot = "/TC2/"; #endif +static const char *selected_snapshot; +static const char *usr_snapshot_path = 0; +#define MAX_TRACE_FILE_PATH_LEN 512 /* trace data and memory file dump names and values - taken from snapshot metadata */ const char *trace_data_filename = "cstrace.bin"; @@ -80,6 +85,7 @@ const ocsd_vaddr_t mem_dump_address_tc2=0xC0008000; /* test variables - set by command line to feature test API */ static int using_mem_acc_cb = 0; /* test the memory access callback function */ static int use_region_file = 0; /* test multi region memory files */ +static int using_mem_acc_cb_id = 0; /* test the mem acc callback with trace ID parameter */ /* buffer to handle a packet string */ #define PACKET_STR_LEN 1024 @@ -114,6 +120,7 @@ static int test_lib_printers = 0; static int process_cmd_line(int argc, char *argv[]) { int idx = 1; + int len = 0; while(idx < argc) { @@ -137,13 +144,13 @@ static int process_cmd_line(int argc, char *argv[]) else if(strcmp(argv[idx],"-etmv3") == 0) { test_protocol = OCSD_PROTOCOL_ETMV3; - default_path_to_snapshot = tc2_snapshot; + selected_snapshot = tc2_snapshot; mem_dump_address = mem_dump_address_tc2; } else if(strcmp(argv[idx],"-ptm") == 0) { test_protocol = OCSD_PROTOCOL_PTM; - default_path_to_snapshot = tc2_snapshot; + selected_snapshot = tc2_snapshot; mem_dump_address = mem_dump_address_tc2; } else if(strcmp(argv[idx],"-stm") == 0) @@ -156,6 +163,12 @@ static int process_cmd_line(int argc, char *argv[]) using_mem_acc_cb = 1; use_region_file = 0; } + else if (strcmp(argv[idx], "-test_cb_id") == 0) + { + using_mem_acc_cb = 1; + use_region_file = 0; + using_mem_acc_cb_id = 1; + } else if(strcmp(argv[idx],"-test_region_file") == 0) { use_region_file = 1; @@ -181,6 +194,26 @@ static int process_cmd_line(int argc, char *argv[]) { test_lib_printers = 1; } + else if(strcmp(argv[idx],"-ss_path") == 0) + { + idx++; + if((idx >= argc) || (strlen(argv[idx]) == 0)) + { + printf("-ss_path: Missing path parameter or zero length\n"); + return -1; + } + else + { + len = strlen(argv[idx]); + if(len > (MAX_TRACE_FILE_PATH_LEN - 32)) + { + printf("-ss_path: path too long\n"); + return -1; + } + usr_snapshot_path = argv[idx]; + } + + } else if(strcmp(argv[idx],"-help") == 0) { return -1; @@ -199,7 +232,8 @@ static void print_cmd_line_help() printf("-decode | -decode_only : full decode + trace packets / full decode packets only (default trace packets only)\n"); printf("-raw / -raw_packed: print raw unpacked / packed data;\n"); printf("-test_printstr | -test_libprint : ttest lib printstr callback | test lib based packet printers\n"); - printf("-test_region_file | -test_cb : mem accessor - test multi region file API | test callback API (default single memory file)\n\n"); + printf("-test_region_file | -test_cb | -test_cb_id : mem accessor - test multi region file API | test callback API [with trcid] (default single memory file)\n\n"); + printf("-ss_path : path from cwd to /snapshots/ directory. Test prog will append required test subdir\n"); } /************************************************************************/ @@ -211,11 +245,13 @@ static ocsd_mem_space_acc_t dump_file_mem_space = OCSD_MEM_SPACE_ANY; /* memor static long mem_file_size = 0; /* size of the memory file */ static ocsd_vaddr_t mem_file_en_address = 0; /* end address last inclusive address in file. */ +/* log the memacc output */ +/* #define LOG_MEMACC_CB */ /* decode memory access using a CallBack function * tests CB API and add / remove mem acc API. */ -static uint32_t mem_acc_cb(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer) +static uint32_t do_mem_acc_cb(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint8_t trc_id, const uint32_t reqBytes, uint8_t *byteBuffer) { uint32_t read_bytes = 0; size_t file_read_bytes; @@ -248,9 +284,24 @@ static uint32_t mem_acc_cb(const void *p_context, const ocsd_vaddr_t address, c if(file_read_bytes < read_bytes) read_bytes = file_read_bytes; } +#ifdef LOG_MEMACC_CB + sprintf(packet_str, "mem_acc_cb(addr 0x%08llX, size %d, trcID 0x%02X)\n", address, reqBytes, trc_id); + ocsd_def_errlog_msgout(packet_str); +#endif return read_bytes; } +static uint32_t mem_acc_cb(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer) +{ + return do_mem_acc_cb(p_context, address, mem_space, 0xff, reqBytes, byteBuffer); +} + +static uint32_t mem_acc_id_cb(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint8_t trc_id, const uint32_t reqBytes, uint8_t *byteBuffer) +{ + return do_mem_acc_cb(p_context, address, mem_space, trc_id, reqBytes, byteBuffer); +} + + /* Create the memory accessor using the callback function and attach to decode tree */ static ocsd_err_t create_mem_acc_cb(dcd_tree_handle_t dcd_tree_h, const char *mem_file_path) { @@ -262,8 +313,12 @@ static ocsd_err_t create_mem_acc_cb(dcd_tree_handle_t dcd_tree_h, const char *me mem_file_size = ftell(dump_file); mem_file_en_address = mem_dump_address + mem_file_size - 1; - err = ocsd_dt_add_callback_mem_acc(dcd_tree_h, - mem_dump_address,mem_file_en_address,dump_file_mem_space,&mem_acc_cb,0); + if (using_mem_acc_cb_id) + err = ocsd_dt_add_callback_trcid_mem_acc(dcd_tree_h, mem_dump_address, + mem_file_en_address, dump_file_mem_space, &mem_acc_id_cb, 0); + else + err = ocsd_dt_add_callback_mem_acc(dcd_tree_h, mem_dump_address, + mem_file_en_address, dump_file_mem_space, &mem_acc_cb, 0); if(err != OCSD_OK) { fclose(dump_file); @@ -290,15 +345,19 @@ static void destroy_mem_acc_cb(dcd_tree_handle_t dcd_tree_h) static ocsd_err_t create_test_memory_acc(dcd_tree_handle_t handle) { ocsd_err_t ret = OCSD_OK; - char mem_file_path[512]; + char mem_file_path[MAX_TRACE_FILE_PATH_LEN]; uint32_t i0adjust = 0x100; int i = 0; - + /* region list to test multi region memory file API */ ocsd_file_mem_region_t region_list[4]; /* path to the file containing the memory image traced - raw binary data in the snapshot */ - strcpy(mem_file_path,default_path_to_snapshot); + if(usr_snapshot_path != 0) + strcpy(mem_file_path,usr_snapshot_path); + else + strcpy(mem_file_path,default_base_snapshot_path); + strcat(mem_file_path,selected_snapshot); strcat(mem_file_path,memory_dump_filename); /* @@ -899,10 +958,13 @@ int process_trace_data(FILE *pf) int main(int argc, char *argv[]) { FILE *trace_data; - char trace_file_path[512]; + char trace_file_path[MAX_TRACE_FILE_PATH_LEN]; int ret = 0, i, len; char message[512]; + /* default to juno */ + selected_snapshot = juno_snapshot; + /* command line params */ if(process_cmd_line(argc,argv) != 0) { @@ -911,8 +973,13 @@ int main(int argc, char *argv[]) } /* trace data file path */ - strcpy(trace_file_path,default_path_to_snapshot); + if(usr_snapshot_path != 0) + strcpy(trace_file_path,usr_snapshot_path); + else + strcpy(trace_file_path,default_base_snapshot_path); + strcat(trace_file_path,selected_snapshot); strcat(trace_file_path,trace_data_filename); + printf("opening %s trace data file\n",trace_file_path); trace_data = fopen(trace_file_path,"rb"); if(trace_data != NULL) diff --git a/decoder/tests/source/mem_buff_demo.cpp b/decoder/tests/source/mem_buff_demo.cpp new file mode 100644 index 00000000000..cacc227e941 --- /dev/null +++ b/decoder/tests/source/mem_buff_demo.cpp @@ -0,0 +1,416 @@ +/* +* \file mem_buff_demo.cpp +* \brief OpenCSD: using the library with memory buffers for data. +* +* \copyright Copyright (c) 2018, ARM Limited. All Rights Reserved. +*/ + +/* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its contributors +* may be used to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Example showing techniques to drive library using only memory buffers as input data + * and image data, avoiding file i/o in main processing routines. (File I/O used to + * initially populate buffers but this can be replaced if data is generated by a client + * environment running live.) + */ + +#include +#include +#include +#include +#include + +#include "opencsd.h" // the library + +// uncomment below to use callback function for program memory image +// #define EXAMPLE_USE_MEM_CALLBACK + +/* Input trace buffer */ +static uint8_t *input_trace_data = 0; +static uint32_t input_trace_data_size = 0; + +/* program memory image for decode */ +static uint8_t *program_image_buffer = 0; // buffer for image data. +static uint32_t program_image_size = 0; // size of program image data. +static ocsd_vaddr_t program_image_address = 0; // load address on target of program image. + +/* a message logger to pass to the error logger / decoder. */ +static ocsdMsgLogger logger; + +/* logger callback function - print out error strings */ +class logCallback : public ocsdMsgLogStrOutI +{ +public: + logCallback() {}; + virtual ~logCallback() {}; + virtual void printOutStr(const std::string &outStr) + { + std::cout << outStr.c_str(); + } +}; +static logCallback logCB; + +/* Decode tree is the main decoder framework - contains the frame unpacker, + packet and trace stream decoders, plus memory image references */ +static DecodeTree *pDecoder = 0; + +/* an error logger - Decode tree registers all components with the error logger +so that errors can be correctly attributed and printed if required +*/ +static ocsdDefaultErrorLogger err_log; + +/* callbacks used by the library */ +#ifdef EXAMPLE_USE_MEM_CALLBACK +// program memory image callback definition +uint32_t mem_access_callback_fn(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer); +#endif + +// callback for the decoder output elements +class DecoderOutputProcessor : public ITrcGenElemIn +{ +public: + DecoderOutputProcessor() {}; + virtual ~DecoderOutputProcessor() {}; + + virtual ocsd_datapath_resp_t TraceElemIn(const ocsd_trc_index_t index_sop, + const uint8_t trc_chan_id, + const OcsdTraceElement &elem) + { + // must fully process or make a copy of data in here. + // element reference only valid for scope of call. + + // for the example program we will stringise and print - + // but this is a client program implmentation dependent. + std::string elemStr; + std::ostringstream oss; + oss << "Idx:" << index_sop << "; ID:" << std::hex << (uint32_t)trc_chan_id << "; "; + elem.toString(elemStr); + oss << elemStr << std::endl; + logger.LogMsg(oss.str()); + return OCSD_RESP_CONT; + } +}; +static DecoderOutputProcessor output; + +/* for test purposes we are initialising from files, but this could be generated test data as + part of a larger program and / or compiled in memory images. + + We have hardcoded in one of the snapshots supplied with the library + */ +static int initDataBuffers() +{ + FILE *fp; + std::string filename; + long size; + + /* the file names to create the data buffers */ +#ifdef _WIN32 + static const char *default_base_snapshot_path = "..\\..\\..\\snapshots"; + static const char *juno_snapshot = "\\juno_r1_1\\"; +#else + static const char *default_base_snapshot_path = "../../../snapshots"; + static const char *juno_snapshot = "/juno_r1_1/"; +#endif + + /* trace data and memory file dump names and values - taken from snapshot metadata */ + static const char *trace_data_filename = "cstrace.bin"; + static const char *memory_dump_filename = "kernel_dump.bin"; + static ocsd_vaddr_t mem_dump_address = 0xFFFFFFC000081000; + + /* load up the trace data */ + filename = default_base_snapshot_path; + filename += (std::string)juno_snapshot; + filename += (std::string)trace_data_filename; + + fp = fopen(filename.c_str(), "rb"); + if (!fp) + return OCSD_ERR_FILE_ERROR; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + input_trace_data_size = (uint32_t)size; + input_trace_data = new (std::nothrow) uint8_t[input_trace_data_size]; + if (!input_trace_data) { + fclose(fp); + return OCSD_ERR_MEM; + } + rewind(fp); + fread(input_trace_data, 1, input_trace_data_size, fp); + fclose(fp); + + /* load up a memory image */ + filename = default_base_snapshot_path; + filename += (std::string)juno_snapshot; + filename += (std::string)memory_dump_filename; + + fp = fopen(filename.c_str(), "rb"); + if (!fp) + return OCSD_ERR_FILE_ERROR; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + program_image_size = (uint32_t)size; + program_image_buffer = new (std::nothrow) uint8_t[program_image_size]; + if (!program_image_buffer) { + fclose(fp); + return OCSD_ERR_MEM; + } + rewind(fp); + fread(program_image_buffer, 1, program_image_size, fp); + fclose(fp); + program_image_address = mem_dump_address; + return OCSD_OK; +} + +static ocsd_err_t createETMv4StreamDecoder() +{ + ocsd_etmv4_cfg trace_config; + ocsd_err_t err = OCSD_OK; + EtmV4Config *pCfg = 0; + + /* + * populate the ETMv4 configuration structure with + * hard coded values from snapshot .ini files. + */ + + trace_config.arch_ver = ARCH_V8; + trace_config.core_prof = profile_CortexA; + + trace_config.reg_configr = 0x000000C1; + trace_config.reg_traceidr = 0x00000010; /* this is the trace ID -> 0x10, change this to analyse other streams in snapshot.*/ + trace_config.reg_idr0 = 0x28000EA1; + trace_config.reg_idr1 = 0x4100F403; + trace_config.reg_idr2 = 0x00000488; + trace_config.reg_idr8 = 0x0; + trace_config.reg_idr9 = 0x0; + trace_config.reg_idr10 = 0x0; + trace_config.reg_idr11 = 0x0; + trace_config.reg_idr12 = 0x0; + trace_config.reg_idr13 = 0x0; + + pCfg = new (std::nothrow) EtmV4Config(&trace_config); + if (!pCfg) + return OCSD_ERR_MEM; + + err = pDecoder->createDecoder(OCSD_BUILTIN_DCD_ETMV4I, /* etm v4 decoder */ + OCSD_CREATE_FLG_FULL_DECODER, /* full trace decode */ + pCfg); + delete pCfg; + return err; +} + +/* Create the decode tree and add the error logger, stream decoder, memory image data to it. + Also register the output callback that processes the decoded trace packets. */ +static ocsd_err_t initialiseDecoder() +{ + ocsd_err_t ret = OCSD_OK; + + /* use the creation function to get the type of decoder we want + either OCSD_TRC_SRC_SINGLE : single trace source - not frame formatted + OCSD_TRC_SRC_FRAME_FORMATTED :multi source - CoreSight trace frame + and set the config flags for operation + OCSD_DFRMTR_FRAME_MEM_ALIGN: input data mem aligned -> no syncs + + For this test we create a decode that can unpack frames and is not expecting sync packets. + */ + pDecoder = DecodeTree::CreateDecodeTree(OCSD_TRC_SRC_FRAME_FORMATTED, OCSD_DFRMTR_FRAME_MEM_ALIGN); + if (!pDecoder) + return OCSD_ERR_MEM; + + /* set up decoder logging - the message logger for output, and the error logger for the library */ + logger.setLogOpts(ocsdMsgLogger::OUT_STR_CB); /* no IO from the logger, just a string callback. */ + logger.setStrOutFn(&logCB); /* set the callback - in this example it will go to stdio but this is up to the implementor. */ + + // for debugging - stdio and file +// logger.setLogOpts(ocsdMsgLogger::OUT_FILE | ocsdMsgLogger::OUT_STDOUT); + + err_log.initErrorLogger(OCSD_ERR_SEV_INFO); + err_log.setOutputLogger(&logger); /* pass the output logger to the error logger. */ + + pDecoder->setAlternateErrorLogger(&err_log); /* pass the error logger to the decoder, do not use the library version. */ + + /* now set up the elements that the decoder needs */ + + /* we will decode one of the streams in this example + create a Full decode ETMv4 stream decoder */ + ret = createETMv4StreamDecoder(); + if (ret != OCSD_OK) + return ret; + + /* as this has full decode we must supply a memory image. */ + + ret = pDecoder->createMemAccMapper(); // the mapper is needed to add code images to. + if (ret != OCSD_OK) + return ret; + +#ifdef EXAMPLE_USE_MEM_CALLBACK + // in this example we have a single buffer so we demonstrate how to use a callback. + // we are passing the buffer pointer as context as we only have one buffer, but this + // could be a structure that is a list of memory image buffers. Context is entirely + // client defined. + // Always use OCSD_MEM_SPACE_ANY unless there is a reason to restrict the image to a specific + // memory space. + pDecoder->addCallbackMemAcc(program_image_address, program_image_address + program_image_size-1, + OCSD_MEM_SPACE_ANY,mem_access_callback_fn, program_image_buffer); +#else + // or we can use the built in memory buffer interface - split our one buffer into two to + // demonstrate the addition of multiple regions + ocsd_vaddr_t block1_st, block2_st; + uint32_t block1_sz, block2_sz; + uint8_t *p_block1, *p_block2; + + // break our single buffer into 2 buffers for demo purposes + block1_sz = program_image_size / 2; + block1_sz &= ~0x3; // align + block2_sz = program_image_size - block1_sz; + block1_st = program_image_address; // loaded program memory start address of program + block2_st = program_image_address + block1_sz; + p_block1 = program_image_buffer; + p_block2 = program_image_buffer + block1_sz; + + /* how to add 2 "separate" buffers to the decoder */ + // Always use OCSD_MEM_SPACE_ANY unless there is a reason to restrict the image to a specific + // memory space. + ret = pDecoder->addBufferMemAcc(block1_st, OCSD_MEM_SPACE_ANY, p_block1, block1_sz); + if (ret != OCSD_OK) + return ret; + + ret = pDecoder->addBufferMemAcc(block2_st, OCSD_MEM_SPACE_ANY, p_block2, block2_sz); + if (ret != OCSD_OK) + return ret; +#endif + + /* finally we need to provide an output callback to recieve the decoded information */ + pDecoder->setGenTraceElemOutI(&output); + return ret; +} + +/* get rid of the objects we created */ +static void destroyDecoder() +{ + delete pDecoder; + delete [] input_trace_data; + delete [] program_image_buffer; +} + +#ifdef EXAMPLE_USE_MEM_CALLBACK +/* if we set up to use a callback to access memory image then this is what will be called. */ +/* In this case the client must do all the work in determining if the requested address is in the + memory area. */ +uint32_t mem_access_callback_fn(const void *p_context, const ocsd_vaddr_t address, + const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer) +{ + ocsd_vaddr_t buf_end_address = program_image_address + program_image_size - 1; + uint32_t read_bytes = reqBytes; + + /* context should be our memory image buffer - if not return 0 bytes read */ + if (p_context != program_image_buffer) + return 0; + + /* not concerned with memory spaces - assume all global */ + if ((address < program_image_address) || (address > buf_end_address)) + return 0; // requested address not in our buffer. + + // if requested bytes from address more than we have, only read to end of buffer + if ((address + reqBytes - 1) > buf_end_address) + read_bytes = (uint32_t)(buf_end_address - (address - 1)); + + // copy the requested data. + memcpy(byteBuffer, program_image_buffer + (address - program_image_address), read_bytes); + + return read_bytes; +} +#endif + +/* use the decoder to process the global trace data buffer */ +static ocsd_datapath_resp_t processTraceData(uint32_t *bytes_done) +{ + /* process in blocks of fixed size. */ + #define DATA_CHUNK_SIZE 2048 + + ocsd_datapath_resp_t resp = OCSD_RESP_CONT; + uint32_t block_size, buff_offset, bytes_to_do = input_trace_data_size, bytes_processed; + ocsd_trc_index_t index = 0; + + /* process the data in chunks, until either all done or + * error occurs. + */ + while ((resp == OCSD_RESP_CONT) && (bytes_to_do)) + { + /* size up a block of input data */ + block_size = (bytes_to_do >= DATA_CHUNK_SIZE) ? DATA_CHUNK_SIZE : bytes_to_do; + buff_offset = input_trace_data_size - bytes_to_do; + + /* push it through the decoder */ + resp = pDecoder->TraceDataIn(OCSD_OP_DATA, index, block_size, + input_trace_data + buff_offset, &bytes_processed); + + /* adjust counter per bytes processed */ + bytes_to_do -= bytes_processed; + index += bytes_processed; + } + + /* if all done then signal end of trace - flushes out any remaining data */ + if (!bytes_to_do) + resp = pDecoder->TraceDataIn(OCSD_OP_EOT, 0, 0, 0, 0); + + /* return amount processed */ + *bytes_done = input_trace_data_size - bytes_to_do; + return resp; +} + +/* main routine - init input data, decode, finish ... */ +int main(int argc, char* argv[]) +{ + int ret = OCSD_OK; + ocsd_datapath_resp_t retd; + char msg[256]; + uint32_t bytes_done; + + /* initialise all the data needed for decode */ + if ((ret = initDataBuffers()) != OCSD_OK) + { + logger.LogMsg("Failed to create trace data buffers\n"); + return ret; + } + /* initialise a decoder object */ + if ((ret = initialiseDecoder()) == OCSD_OK) + { + retd = processTraceData(&bytes_done); + if (!OCSD_DATA_RESP_IS_CONT(retd)) + { + ret = OCSD_ERR_DATA_DECODE_FATAL; + logger.LogMsg("Processing failed with data error\n"); + } + + /* get rid of the decoder and print a brief result. */ + destroyDecoder(); + sprintf(msg, "Processed %u bytes out of %u\n", bytes_done, input_trace_data_size); + logger.LogMsg(msg); + } + else + logger.LogMsg("Failed to create decoder for trace processing\n"); + return ret; +} diff --git a/decoder/tests/source/trc_pkt_lister.cpp b/decoder/tests/source/trc_pkt_lister.cpp index 854f15817b9..50260a5f8b9 100644 --- a/decoder/tests/source/trc_pkt_lister.cpp +++ b/decoder/tests/source/trc_pkt_lister.cpp @@ -70,6 +70,9 @@ static bool decode = false; static bool no_undecoded_packets = false; static bool pkt_mon = false; static int test_waits = 0; +static bool dstream_format = false; +static bool tpiu_format = false; +static bool has_hsync = false; int main(int argc, char* argv[]) { @@ -180,8 +183,11 @@ void print_help() oss << "-ss_dir Set the directory path to a trace snapshot\n"; oss << "-ss_verbose Verbose output when reading the snapshot\n"; oss << "\nDecode:\n\n"; - oss << "-id Set an ID to list (may be used mutiple times) - default if no id set is for all IDs to be printed\n"; + oss << "-id Set an ID to list (may be used multiple times) - default if no id set is for all IDs to be printed\n"; oss << "-src_name List packets from a given snapshot source name (defaults to first source found)\n"; + oss << "-dstream_format Input is DSTREAM framed."; + oss << "-tpiu Input from TPIU - sync by FSYNC."; + oss << "-tpiu_hsync Input from TPIU - sync by FSYNC and HSYNC."; oss << "-decode Full decode of the packets from the trace snapshot (default is to list undecoded packets only\n"; oss << "-decode_only Does not list the undecoded packets, just the trace decode.\n"; oss << "-o_raw_packed Output raw packed trace frames\n"; @@ -401,6 +407,19 @@ bool process_cmd_line_opts(int argc, char* argv[]) optIdx++; } } + else if (strcmp(argv[optIdx], "-dstream_format") == 0) + { + dstream_format = true; + } + else if (strcmp(argv[optIdx], "-tpiu") == 0) + { + tpiu_format = true; + } + else if (strcmp(argv[optIdx], "-tpiu_hsync") == 0) + { + has_hsync = true; + tpiu_format = true; + } else { std::ostringstream errstr; @@ -488,9 +507,19 @@ void ConfigureFrameDeMux(DecodeTree *dcd_tree, RawFramePrinter **framePrinter) if(pDeformatter != 0) { // configuration - memory alinged buffer - uint32_t configFlags = OCSD_DFRMTR_FRAME_MEM_ALIGN; + uint32_t configFlags = pDeformatter->getConfigFlags(); - pDeformatter->Configure(configFlags); + // check for TPIU FSYNC & HSYNC + if (tpiu_format) configFlags |= OCSD_DFRMTR_HAS_FSYNCS; + if (has_hsync) configFlags |= OCSD_DFRMTR_HAS_HSYNCS; + // if FSYNC (& HSYNC) - cannot be mem frame aligned. + if (tpiu_format) configFlags &= ~OCSD_DFRMTR_FRAME_MEM_ALIGN; + + if (!configFlags) + { + configFlags = OCSD_DFRMTR_FRAME_MEM_ALIGN; + pDeformatter->Configure(configFlags); + } if (outRawPacked || outRawUnpacked) { if (outRawPacked) configFlags |= OCSD_DFRMTR_PACKED_RAW_OUT; @@ -554,7 +583,12 @@ void ListTracePackets(ocsdDefaultErrorLogger &err_logger, SnapShotReader &reader // process the file, a buffer load at a time while(!in.eof() && !OCSD_DATA_RESP_IS_FATAL(dataPathResp)) { - in.read((char *)&trace_buffer[0],bufferSize); // load a block of data into the buffer + if (dstream_format) + { + in.read((char *)&trace_buffer[0], 512 - 8); + } + else + in.read((char *)&trace_buffer[0],bufferSize); // load a block of data into the buffer std::streamsize nBuffRead = in.gcount(); // get count of data loaded. std::streamsize nBuffProcessed = 0; // amount processed in this buffer. @@ -597,6 +631,22 @@ void ListTracePackets(ocsdDefaultErrorLogger &err_logger, SnapShotReader &reader dataPathResp = dcd_tree->TraceDataIn(OCSD_OP_FLUSH,0,0,0,0); } } + + /* dump dstream footers */ + if (dstream_format) { + in.read((char *)&trace_buffer[0], 8); + if (outRawPacked) + { + std::ostringstream oss; + oss << "DSTREAM footer ["; + for (int i = 0; i < 8; i++) + { + oss << "0x" << std::hex << (int)trace_buffer[i] << " "; + } + oss << "]\n"; + logger.LogMsg(oss.str()); + } + } } // fatal error - no futher processing