├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE ├── README.md └── src ├── Makefile ├── analysis.c ├── arch ├── cpudisp.h ├── x86 │ └── ptrace_ia32.c └── x86_64 │ ├── asm │ ├── avx2.S │ └── sse2.S │ ├── cpudisp_amd64.h │ ├── dispatcher.c │ └── ptrace_amd64.c ├── array.c ├── benchs ├── Makefile ├── bench.c ├── plot.R └── run-benchs.sh ├── breakpoint.c ├── dwarf.c ├── function.c ├── hashtable.c ├── highlight.c ├── include ├── analysis.h ├── array.h ├── breakpoint.h ├── dwarf_helper.h ├── function.h ├── hashtable.h ├── highlight.h ├── line.h ├── optparse.h ├── pbd.h ├── ptrace.h ├── util.h └── variable.h ├── line.c ├── main.c ├── man └── man1 │ └── pbd.1 ├── ptrace.c ├── tests ├── Makefile ├── outputs │ ├── test_func1_expected │ └── test_func3_expected_sa ├── pbd.supp ├── run-tests.sh └── test.c └── variable.c /.gitignore: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019 Davidson Francis 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | # Default rules. 24 | *.o 25 | src/pbd 26 | src/benchs/bench 27 | src/benchs/*.png 28 | src/benchs/csv_times 29 | src/benchs/gdb_script 30 | src/tests/test 31 | src/tests/outputs/test_func1_out 32 | src/tests/outputs/test_func1_out_sa 33 | src/tests/outputs/test_func3_out_sa 34 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/sparse"] 2 | path = src/sparse 3 | url = git://git.kernel.org/pub/scm/devel/sparse/sparse.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: bionic 2 | sudo: required 3 | language: c 4 | 5 | jobs: 6 | include: 7 | # Tests x86_64, GCC 8 | - stage: "test" 9 | name: "x86_64" 10 | compiler: gcc 11 | before_install: 12 | - sudo apt-get install -y libdwarf-dev valgrind 13 | script: 14 | - cd src/ && make test V=1 -j$((`nproc`+1)) 15 | - file ./pbd 16 | 17 | # Tests x86_64, Clang 18 | - stage: "test" 19 | name: "x86_64" 20 | compiler: clang 21 | before_install: 22 | - sudo apt-get install -y libdwarf-dev valgrind 23 | script: 24 | - cd src/ && make test V=1 -j$((`nproc`+1)) 25 | - file ./pbd 26 | 27 | # Tests x86, GCC 28 | - stage: "test" 29 | name: "x86" 30 | compiler: gcc 31 | before_install: 32 | - sudo dpkg --add-architecture i386 33 | - sudo apt update 34 | - sudo apt-get install -y gcc-multilib valgrind libdwarf-dev:i386 libc6-dbg:i386 35 | script: 36 | - cd src/ && CFLAGS=-m32 make test V=1 -j$((`nproc`+1)) 37 | - file ./pbd 38 | 39 | # Tests x86, Clang 40 | - stage: "test" 41 | name: "x86" 42 | compiler: clang 43 | before_install: 44 | - sudo dpkg --add-architecture i386 45 | - sudo apt update 46 | - sudo apt-get install -y gcc-multilib valgrind libdwarf-dev:i386 libc6-dbg:i386 47 | script: 48 | - cd src/ && CFLAGS=-m32 make test V=1 -j$((`nproc`+1)) 49 | - file ./pbd 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Davidson Francis 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PBD 2 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Build Status](https://travis-ci.com/Theldus/PBD.svg?branch=master)](https://travis-ci.com/Theldus/PBD) 3 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/450c403f49794de483759f77c8ad8921)](https://www.codacy.com/manual/Theldus/PBD?utm_source=github.com&utm_medium=referral&utm_content=Theldus/PBD&utm_campaign=Badge_Grade) 4 | 5 | PBD is a C debugger for Linux systems, which aims to monitor all global and 6 | local variables (of a specific function) and nicely print the old/new values on 7 | the screen, as well as the place where the change has occurred. 8 | 9 | ## Introduction 10 | Although there are several debuggers for C, most of them are somewhat complicated 11 | to use, have bad interfaces and end up driving the user away from the language, 12 | especially a beginner. 13 | 14 | PBD's main purpose is to debug variables, i.e: monitor all occurrences of changes 15 | and display them on screen, but in an uncomplicated and transparent way for the 16 | user. Like most debuggers, PBD is an _external debugger_ and therefore does not 17 | require any changes to the source code nor does binary instrumentation. 18 | 19 |

20 | 21 | PBD example 22 |
23 | PBD example 24 |

25 | 26 | 27 | ## Features 28 | PBD is in earlier stages but is capable to debug all primitive types, _typedefs_ to 29 | primitive types, pointers* and arrays up to 8 dimensions (`[][][][][]`....). For each 30 | supported type, PBD will monitor and print on the screen the old and new values as 31 | soon as the variable gets changed. 32 | 33 | PBD also allows the user to track only globals, only locals, selectively watch/ignore 34 | a predefined variables list, show lines with context and with syntax highlighting 35 | (with themes support): 36 | ``` 37 | Usage: ./pbd [options] executable function_name [executable_options] 38 | Options: 39 | -------- 40 | -h --help Display this information 41 | -v --version Display the PBD version 42 | -s --show-lines Shows the debugged source code portion in the output 43 | -x --context Shows num lines before and after the code portion. This option is meant to be 44 | used in conjunction with -s option 45 | 46 | -l --only-locals Monitors only local variables (default: global + local) 47 | -g --only-globals Monitors only global variables (default: global + local) 48 | -i --ignore-list Ignores a specified list of variables names 49 | -w --watch-list Monitors a specified list of variables names 50 | -o --output Sets an output file for PBD output. Useful to not mix PBD and 51 | executable outputs 52 | --args Delimits executable arguments from this point. All arguments onwards 53 | will be treated as executable program arguments 54 | 55 | Static Analysis options: 56 | ------------------------ 57 | PBD is able to do a previous static analysis in the C source code that belongs to the monitored 58 | function, and thus, greatly improving the debugging time. Note however, that this is an 59 | experimental feature. 60 | 61 | -S --static Enables static analysis 62 | 63 | Optional flags: 64 | -D sym[=val] Defines 'sym' with value 'val' 65 | -U sym Undefines 'sym' 66 | -I dir Add 'dir' to the include path 67 | --std= Defines the language standard, supported values are: c89, gnu89, c99, 68 | gnu99, c11 and gnu11. (Default: gnu11) 69 | 70 | Syntax highlighting options: 71 | ---------------------------- 72 | -c --color Enables syntax highlight, this option only takes effect while used 73 | together with --show-lines, Also note that this option requires a 74 | 256-color compatible terminal. 75 | 76 | -t --theme Select a theme file for the highlighting 77 | 78 | Notes: 79 | ------ 80 | - Options -i and -w are mutually exclusive! 81 | 82 | - The executable *must* be built without any optimization and with at least -gdwarf-2 and no PIE! 83 | (if PIE enabled by default) 84 | 85 | The following options are for PBD internals: 86 | -d --dump-all Dump all information gathered by the executable 87 | 88 | 89 | 'Unsafe' options: 90 | ----------------- 91 | The options below are meant to be used with caution, since they could lead 92 | to wrong output. 93 | 94 | --avoid-equal-statements If enabled, PBD will ignore all line statements that are 'duplicated', 95 | i.e: belongs to the same liner number, regardless its address. 96 | ``` 97 | 98 | ## Performance 99 | Some might say: _Why should I worry about PBD? My GDB already does this with the `watch` command!_ 100 | 101 | Yes, indeed, and it has helped me a lot over the years, but GDB has one problem: 102 | _software watchpoint_. GDB (and possibly others as well) has 2 main ways to monitor 103 | variables: 104 | 1) Hardware Watchpoint 105 | 2) Software Watchpoint 106 | 107 | The former is GDB's default behavior, and coupled with special processor registers 108 | (DR0-DR3 on Intel x86) it achieves excellent performance with virtually no overhead, 109 | but has one major problem: 110 | regardless of architecture, there will always be a physical limit on how many addresses 111 | the processor will support. For Intel processors, the limit is 4 addresses only, i.e: 112 | GDB can monitor (with HW assistance) up to 4 64-bit variables. 113 | 114 | When the amount of data exceeds 32 bytes (64 * 4 bits), GDB is forced to use a software 115 | watchpoint, which is quite simple: the software runs single-step and each monitored 116 | address is checked at each break, which means It's ridiculously slow and costly for the 117 | machine. 118 | 119 | Since 32 bytes is an extremely limiting value, PBD does not use hardware watchpoint but 120 | manages to be smarter than performing single steps at all times: instead, PBD places 121 | breakpoints in strategic locations of the executable, so that execution is not interrupted 122 | at all times, but only when necessary. 123 | 124 | PBD has 2 operating modes, default mode, and static analysis mode, which differ in the 125 | strategy used for the breakpoints insertion: 126 | - In default mode, PBD places breakpoints at each start of the statement. Note that not every 127 | statement has a variable change, but every variable change is within a statement!. 128 | 129 | - In static analysis mode (-S), PBD (thanks to [libsparse](https://git.kernel.org/pub/scm/devel/sparse/sparse.git/)) 130 | is able to perform a pre-analysis of the source code that corresponds to the compiled binary 131 | and thus, is able to accurately identify statements that have variable changes and thus, 132 | add breakpoints only to them. 133 | 134 | The graph below compares GDB (v7.12) with PBD (v0.7 x64) in three types of workloads that 135 | illustrate the best (work1), medium (work2), and worst (work3) execution cases for the PBD. 136 | All of them were run in the two PBD modes and the following Linux environment: Kernel v4.4.38 137 | and GCC v5.3.

138 | 139 | From the data above, it can be seen that the PBD manages to be about 13x, 341x and 7785x faster 140 | than the GDB for the worst (work3), medium (work2), and best (work1) case respectively. 141 | 142 | The following table summarizes the speedups: 143 | | Speedup PBD (v0.7 x64) vs GDB (v7.12 x64) | PBD default | PBD w/ static analysis | 144 | |-------------------------------------------|-------------|------------------------| 145 | | work1 (best) | 27.05x | 7785.69x | 146 | | work2 (avg) | 40.33x | 340.95x | 147 | | work3 (worst) | 13.38x | 13.48x | 148 | 149 | The tests can be performed with: `make bench` (GDB, Rscript and bc are required, in order 150 | to execute and plot the graphs). 151 | 152 | ## Limitations 153 | At the moment PBD has some limitations, such as features, compilers, operating systems, of which: 154 | 155 | ### Operating Systems 156 | Exclusive support for Linux (and possibly Unix-like as well) environments. I do not intend to 157 | support Windows, but I can accept contributions in this regard. 158 | 159 | ### Compilers 160 | PBD was designed exclusively for GCC and Clang and has not been tested for other compilers, so 161 | I cannot guarantee it will work. 162 | 163 | Also, it only supports the DWARF-2 standard (you *must* build your executable with `-gdwarf-2` 164 | instead of `-g`) and does not support PIE executables, so you need to disable (`-no-pie` is 165 | enough) if enabled by default on your compiler. 166 | 167 | ### Architecture 168 | Supports x86 and x86_64. Since PBD is focused on 'desktop' environments, support for other 169 | architectures such as OpenRISC, RISC-V, ARM... will only be considered if there is some 170 | kind of significant demand for this. 171 | 172 | ### C Features 173 | PBD lacks the following C features: 174 | - Structs / Unions [(#3)](https://github.com/Theldus/PBD/issues/3) 175 | - Pointers* [(#5)](https://github.com/Theldus/PBD/issues/5) 176 | 177 | Pointer support is partially supported: PBD will monitor only the value of the pointer, rather 178 | than the content pointed to by it. Please check the issues for a detailed explanation and to 179 | follow the implementation progress. 180 | 181 | Any input on one of these points is most welcome, ;-). 182 | 183 | ## Installing 184 | ### Pre-built packages 185 | You can check for static pre-built binaries in the 186 | [releases](https://github.com/Theldus/PBD/releases) page. 187 | 188 | ### Build from source 189 | The latest changes are here, if you want to build from source, you are just required to install 190 | `libdwarf` first. 191 | 192 | #### Ubuntu/Debian 193 | `sudo apt install libdwarf-dev` 194 | 195 | #### Slackware 196 | ``` 197 | wget https://www.prevanders.net/libdwarf-20190529.tar.gz 198 | wget https://slackbuilds.org/slackbuilds/14.2/development/dwarf.tar.gz 199 | tar xf dwarf.tar.gz 200 | mv libdwarf* dwarf/ 201 | sudo sh dwarf/dwarf.SlackBuild 202 | sudo installpkg /tmp/dwarf-*.tgz 203 | ``` 204 | #### Your distro here 205 | `sudo something...` 206 | 207 | After the dependencies are met, just recursively clone (libsparse is a submodule) 208 | and install as usual: 209 | ``` 210 | git clone --recursive https://github.com/Theldus/PBD.git 211 | cd PBD/src/ 212 | make # Build 213 | make test # Tests, I highly recommend you to _not_ skip this! 214 | sudo make install # Install 215 | ``` 216 | 217 | ## Contributing 218 | The PBD is always open to the community and willing to accept contributions, whether with issues, 219 | documentation, testing, new features, bugfixes, typos... welcome aboard. 220 | 221 | ## License and Authors 222 | PBD is licensed under MIT License. Written by Davidson Francis and (hopefully) others 223 | [contributors](https://github.com/Theldus/PBD/graphs/contributors). 224 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2020 Davidson Francis 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC ?= gcc 24 | INCLUDE = $(CURDIR)/include 25 | 26 | # Sparse lib 27 | INCLUDE_SPARSE += $(CURDIR)/sparse/ 28 | 29 | # Some distros like Debian Buster (10) and Ubuntu Bionic (18.04) 30 | # uses a different path for libdwarf, so lets use it. 31 | INCLUDE_DWARF = /usr/include/libdwarf/ 32 | 33 | # Flags 34 | SPARSECFLAGS = -O2 -g 35 | TMP := $(CFLAGS) 36 | CFLAGS = $(TMP) -Wall -Wextra -Werror 37 | CFLAGS += -I $(INCLUDE) -I $(INCLUDE_DWARF) -I $(INCLUDE_SPARSE) 38 | CFLAGS += -std=c99 -g -O3 39 | LDFLAGS = -ldwarf -lm 40 | 41 | # Machine architecture 42 | ARCH := $(shell uname -m) 43 | ifeq ($(ARCH), x86_64) 44 | ifneq ($(filter -m32,$(CFLAGS)),) 45 | $(warning Enabling cross-compiling mode (x86_64 building 32 bit...)) 46 | ARCH = x86 47 | SPARSECFLAGS += -m32 48 | EXTRAFLAGS = -m32 49 | else 50 | ARCH = x86_64 51 | endif 52 | else 53 | ifneq ($(filter %86,$(ARCH)),) 54 | ARCH = x86 55 | else 56 | $(error PBD currently only supports x86/x86_64 architectures!) 57 | endif 58 | endif 59 | 60 | INCLUDE += -I $(CURDIR)/arch/ 61 | INCLUDE += -I $(CURDIR)/arch/$(ARCH)/ 62 | C_SRC = $(wildcard *.c) 63 | C_SRC += $(wildcard arch/$(ARCH)/*.c) 64 | 65 | # Conditionally add ASM files to the identified arch 66 | ifeq ($(ARCH), x86_64) 67 | AVX2_SUPPORT := $(shell printf "vpcmpeqb %%ymm1, %%ymm1, %%ymm1\n" \ 68 | | if (gcc -x assembler -c -o .avx2.o -) &>/dev/null; \ 69 | then \ 70 | echo "yes"; \ 71 | else \ 72 | echo "no"; \ 73 | fi; \ 74 | rm .avx2.o 2> /dev/null) 75 | 76 | ifeq ($(AVX2_SUPPORT), yes) 77 | CFLAGS += -DCAN_BUILD_AVX2 78 | ASM_SRC += $(wildcard arch/x86_64/asm/*.S) 79 | else 80 | ASM_SRC += $(wildcard arch/x86_64/asm/sse2.S) 81 | endif 82 | endif 83 | 84 | OBJ = $(C_SRC:.c=.o) 85 | OBJ += $(ASM_SRC:.S=.o) 86 | 87 | # Paths 88 | PREFIX ?= /usr/local 89 | BINDIR = $(PREFIX)/bin 90 | MANDIR = $(PREFIX)/man 91 | 92 | # Pretty print 93 | Q := @ 94 | ifeq ($(V), 1) 95 | Q := 96 | endif 97 | 98 | # C Files 99 | %.o: %.c 100 | @echo " CC $@" 101 | $(Q)$(CC) $< $(CFLAGS) -c -o $@ 102 | 103 | # ASM Files 104 | %.o: %.S 105 | @echo " CC $@" 106 | $(Q)$(CC) $< $(CFLAGS) -c -o $@ 107 | 108 | all: pbd 109 | 110 | # Sparse library 111 | sparse/libsparse.a: sparse/Makefile 112 | $(MAKE) -C sparse/ HAVE_SQLITE=no CFLAGS="$(SPARSECFLAGS)" libsparse.a 113 | 114 | sparse/Makefile: 115 | $(warning Sparse submodule not fetched, fetching...) 116 | git submodule update --init 117 | 118 | # Main program 119 | pbd: $(OBJ) sparse/libsparse.a 120 | @echo " LD $@" 121 | $(Q)$(CC) $^ $(CFLAGS) $(LDFLAGS) -o $@ 122 | 123 | # Tests 124 | test: pbd 125 | $(MAKE) -C tests/ CFLAGS="$(EXTRAFLAGS)" 126 | 127 | # Benchs 128 | bench: pbd 129 | $(MAKE) -C benchs/ CFLAGS="$(EXTRAFLAGS)" 130 | 131 | # Install rules 132 | install: pbd 133 | @# Binary file 134 | install -d $(DESTDIR)$(BINDIR) 135 | install -m 755 pbd $(DESTDIR)$(BINDIR) 136 | @#Manpages 137 | install -d $(DESTDIR)$(MANDIR)/man1 138 | install -m 0644 $(CURDIR)/man/man1/pbd.1 $(DESTDIR)$(MANDIR)/man1/ 139 | gzip $(DESTDIR)$(MANDIR)/man1/pbd.1 140 | 141 | # Uninstall rules 142 | uninstall: 143 | rm -f $(DESTDIR)$(BINDIR)/pbd 144 | rm -f $(DESTDIR)$(MANDIR)/man1/pbd.1.gz 145 | 146 | clean: 147 | @echo " CLEAN" 148 | @rm -f $(OBJ) pbd 149 | @$(MAKE) clean -C tests/ 150 | @$(MAKE) clean -C benchs/ 151 | @$(MAKE) clean -C sparse/ HAVE_SQLITE=no 152 | -------------------------------------------------------------------------------- /src/analysis.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "analysis.h" 26 | #include "pbd.h" 27 | 28 | /* Arguments. */ 29 | struct analysis_struct analysis_arguments; 30 | 31 | /* Compound statement function scope. */ 32 | static struct scope *f_scope; 33 | 34 | /* Target function. */ 35 | static const char *function; 36 | static const char *filename; 37 | 38 | /** 39 | * Hash table breakpoints list. 40 | */ 41 | static struct hashtable *breakpoints; 42 | 43 | /** 44 | * Lines list. 45 | */ 46 | static struct array *lines; 47 | 48 | /** 49 | * If dump_all flag is enabled, verbose_assign() 50 | * will output to stdout all the assignment/declaration 51 | * statements that is found during the file parsing. 52 | * 53 | * @param line_no stmt/expr line number. 54 | * @param name variable name. 55 | * @param is_assign 1 if assignment statement, 0 otherwise. 56 | * @param is_decl 1 if declaration statement, 0 otherwise. 57 | * @param is_ignored 1 if the statement/expr is ignored*, 58 | * 0 otherwise. 59 | * 60 | * @note *An statement/expression will be ignored if the 61 | * variable is not one of the types tracked (currently: 62 | * basetype, arrays and pointers) or if outside the valid 63 | * scopes, i.e: not global and/or not in the function 64 | * scope. 65 | */ 66 | static inline void verbose_assign( 67 | int line_no, 68 | const char *name, 69 | int is_assign, 70 | int is_decl, 71 | int is_ignored) 72 | { 73 | if (args.flags & FLG_DUMP_ALL) 74 | { 75 | fprintf(pbd_output, 76 | "===static=analysis=== [%03d] %15s (is_assign: %d) %s%s\n", 77 | line_no, 78 | name, 79 | is_assign, 80 | (is_decl ? "(decl) " : ""), 81 | (is_ignored ? "(ignored) " : "") 82 | ); 83 | } 84 | } 85 | 86 | /** 87 | * If dump_all flag is enabled, verbose_function_call() 88 | * will output to stdout all the function call statements 89 | * that is found during the file parsing. 90 | * 91 | * @param line_no Function call line number. 92 | */ 93 | static inline void verbose_function_call(int line_no) 94 | { 95 | if (args.flags & FLG_DUMP_ALL) 96 | { 97 | fprintf(pbd_output, 98 | "===static=analysis=== [%03d] %15s (func call)\n", 99 | line_no, ""); 100 | } 101 | } 102 | 103 | /** 104 | * Do a binary search on the lines list looking for a given @p 105 | * line_no and returns the leftmost ocurrence, if there is more 106 | * than one. 107 | * 108 | * @param lines Lines list. 109 | * @param line_no Line to be searched. 110 | * 111 | * @return Returns the line index, if one. 112 | */ 113 | static int binsearch_lines(struct array *lines, unsigned line_no) 114 | { 115 | struct dw_line *line; 116 | size_t size; 117 | int l; 118 | int r; 119 | int m; 120 | 121 | size = array_size(&lines); 122 | l = 0; 123 | r = size - 1; 124 | 125 | /* Returns the leftmost element if duplicate. */ 126 | while (l < r) 127 | { 128 | m = (r + l) / 2; 129 | line = array_get(&lines, m, NULL); 130 | 131 | if (line->line_no < line_no) 132 | l = m + 1; 133 | else 134 | r = m; 135 | } 136 | 137 | line = array_get(&lines, l, NULL); 138 | if (line && line->line_no == line_no) 139 | return (l); 140 | else 141 | return (-1); 142 | } 143 | 144 | /** 145 | * For a given symbol @p sym, checks if the symbol is global 146 | * or local. 147 | * 148 | * @param sym Symbol to be checked. 149 | * 150 | * @return Returns 1 if global and 0 otherwise. 151 | * 152 | * @note Adapted from storage() in test-dissect.c, 153 | * lib sparse source code. 154 | */ 155 | static inline int is_symbol_global(struct symbol *sym) 156 | { 157 | unsigned m; 158 | m = sym->ctype.modifiers; 159 | 160 | /* 161 | * Since static variables do not resides in the stack, 162 | * they're equals to global variables, from the PBD 163 | * point-of-view. 164 | */ 165 | if (m & MOD_STATIC || m & MOD_NONLOCAL) 166 | return (1); 167 | else 168 | return (0); 169 | } 170 | 171 | /** 172 | * Checks if the symbol @p sym is one of the types supported 173 | * to be monitored. 174 | * 175 | * @param sym Symbol to be checked. 176 | * 177 | * @return Returns 1 if supported and 0 otherwise. 178 | */ 179 | static inline int is_var_symbol(struct symbol *sym) 180 | { 181 | int type; 182 | type = get_sym_type(sym); 183 | return (type == SYM_BASETYPE || type == SYM_ARRAY 184 | || type == SYM_PTR); 185 | } 186 | 187 | /** 188 | * For a given symbol @p sym, checks if the symbol elegible 189 | * to be monitored or not. 190 | * 191 | * @param sym Symbol to be checked. 192 | * 193 | * @return Returns 1 if elegible and 0 otherwise. 194 | */ 195 | static inline int should_watch_sym(struct symbol *sym) 196 | { 197 | return 198 | ( 199 | is_var_symbol(sym) && 200 | (is_symbol_global(sym) || sym->scope == f_scope) 201 | ); 202 | } 203 | 204 | /** 205 | * Given a line number @p line_no, tries to add the all the 206 | * matching lines in the line list in to the breakpoint 207 | * list. 208 | * 209 | * @param line_no Line to be added. 210 | */ 211 | static void try_add_symbol(unsigned line_no) 212 | { 213 | int idx; 214 | int size; 215 | struct dw_line *line; 216 | struct breakpoint *bp; 217 | 218 | idx = binsearch_lines(lines, line_no); 219 | size = (int) array_size(&lines); 220 | 221 | /* Skip lines that do not exist in dwarf structures. */ 222 | if (idx == -1) 223 | return; 224 | 225 | /* Loop through all (possible) repeated lines. */ 226 | while (idx < size) 227 | { 228 | line = array_get(&lines, idx, NULL); 229 | idx++; 230 | 231 | /* If different, we should break, since the list is ordered. */ 232 | if (line->line_no != line_no) 233 | break; 234 | 235 | /* Skip lines that are not begin of statement. */ 236 | if (line->line_type != LBEGIN_STMT) 237 | continue; 238 | 239 | /* 240 | * Check if the value already exists, break* if so. 241 | * 242 | * *We can safely break the loop here because the addresses 243 | * is (I really hope) guaranteed to not repeat, so, if the 244 | * first line ocurrence already exists for the same address 245 | * we can break the entire loop, instead of just skip. 246 | */ 247 | if (hashtable_get(&breakpoints, (void*)line->addr) != NULL) 248 | break; 249 | 250 | /* Allocates a new breakpoint. */ 251 | bp = malloc(sizeof(struct breakpoint)); 252 | bp->addr = line->addr; 253 | bp->original_byte = 0; 254 | bp->line_no = line->line_no; 255 | 256 | /* Add to our hashtable. */ 257 | hashtable_add(&breakpoints, (void*)bp->addr, bp); 258 | } 259 | } 260 | 261 | /** 262 | * Recursively expands a given expression until find a EXPR_SYMBOL, 263 | * EXPR_CALL or a non-supported expression type. 264 | * 265 | * @param expr Expression to be expanded. 266 | * 267 | * @param is_assignment Signals if a previous expression of type 268 | * EXPR_ASSIGNMENT was found. The first call to handle_expr() 269 | * is_assignment should be '0'. 270 | */ 271 | void handle_expr(struct expression *expr, int is_assignment) 272 | { 273 | /* Skip NULL expressions. */ 274 | if (!expr) 275 | return; 276 | 277 | switch (expr->type) 278 | { 279 | default: 280 | break; 281 | 282 | /* Pre-operator. */ 283 | case EXPR_PREOP: 284 | handle_expr(expr->unop, is_assignment); 285 | break; 286 | 287 | /* 288 | * In my tests, postop seems to be used to handle things 289 | * like: foo++, foo--. So, lets consider this as an assignment 290 | * expression. 291 | */ 292 | case EXPR_POSTOP: 293 | handle_expr(expr->unop, 1); 294 | break; 295 | 296 | /* Left/Right expressions. */ 297 | case EXPR_COMPARE: 298 | case EXPR_LOGICAL: 299 | case EXPR_BINOP: 300 | case EXPR_COMMA: 301 | handle_expr(expr->left, is_assignment); 302 | 303 | /* 304 | * Since we're looking for the left-most symbol that 305 | * belongs to an assignment expression, if the next 306 | * left expression is of type EXPR_SYMBOL _and_ we're 307 | * inside an assignment, we walk through it as usual 308 | * and reset the assignment state before proceed to 309 | * the right side. 310 | */ 311 | if (expr->left->type == EXPR_SYMBOL && is_assignment) 312 | handle_expr(expr->right, 0); 313 | else 314 | handle_expr(expr->right, is_assignment); 315 | break; 316 | 317 | /* We can have assignments inside cast expressions. */ 318 | case EXPR_CAST: 319 | case EXPR_IMPLIED_CAST: 320 | case EXPR_FORCE_CAST: 321 | handle_expr(expr->cast_expression, is_assignment); 322 | break; 323 | 324 | /* Almost there. */ 325 | case EXPR_ASSIGNMENT: 326 | handle_expr(expr->left, 1); 327 | handle_expr(expr->right, 0); 328 | break; 329 | 330 | /* Ternary operator, I think... */ 331 | case EXPR_CONDITIONAL: 332 | case EXPR_SELECT: 333 | handle_expr(expr->conditional, 0); 334 | handle_expr(expr->cond_true, 0); 335 | handle_expr(expr->cond_false, 0); 336 | break; 337 | 338 | /* Function call, no need to proceed further. */ 339 | case EXPR_CALL: 340 | verbose_function_call(expr->pos.line); 341 | try_add_symbol(expr->pos.line); 342 | break; 343 | 344 | /* Symbol found, yay. */ 345 | case EXPR_SYMBOL: 346 | if (is_assignment && should_watch_sym(expr->symbol)) 347 | { 348 | verbose_assign(expr->pos.line, expr->symbol_name->name, 349 | is_assignment, 0, 0); 350 | 351 | try_add_symbol(expr->pos.line); 352 | } 353 | else 354 | { 355 | verbose_assign(expr->pos.line, expr->symbol_name->name, 356 | is_assignment, 0, 1); 357 | } 358 | break; 359 | } 360 | } 361 | 362 | /** 363 | * Recursively expands a given statement until all statements are 364 | * expanded or a non-supported statement is found. 365 | * 366 | * @param stmt Statement to be expanded. 367 | */ 368 | void handle_stmt(struct statement *stmt) 369 | { 370 | struct symbol *sym; 371 | struct statement *s; 372 | 373 | /* Skip NULL statements. */ 374 | if (!stmt) 375 | return; 376 | 377 | switch (stmt->type) 378 | { 379 | default: 380 | break; 381 | 382 | /* Expressions. */ 383 | case STMT_EXPRESSION: 384 | handle_expr(stmt->expression, 0); 385 | break; 386 | 387 | case STMT_IF: 388 | handle_expr(stmt->if_conditional, 0); 389 | handle_stmt(stmt->if_true); 390 | handle_stmt(stmt->if_false); 391 | break; 392 | 393 | /* 394 | * Loops. 395 | * 396 | * I'm not worried here of which kind of loop is being 397 | * analyzed, so I'm just handling all statements and 398 | * expressions inside. 399 | * 400 | * Nulled pointers are handled nicely at the top of 401 | * each handle, so I'm also not worried with it. 402 | */ 403 | case STMT_ITERATOR: 404 | /* Expressions. */ 405 | handle_expr(stmt->iterator_pre_condition, 0); 406 | handle_expr(stmt->iterator_post_condition, 0); 407 | /* Statements. */ 408 | handle_stmt(stmt->iterator_pre_statement); 409 | handle_stmt(stmt->iterator_statement); 410 | handle_stmt(stmt->iterator_post_statement); 411 | break; 412 | 413 | /* Switch statements are composed of switch, label, 414 | * and goto statements. */ 415 | case STMT_SWITCH: 416 | handle_expr(stmt->switch_expression, 0); 417 | handle_stmt(stmt->switch_statement); 418 | break; 419 | case STMT_CASE: 420 | handle_expr(stmt->case_expression, 0); 421 | handle_expr(stmt->case_to, 0); 422 | handle_stmt(stmt->case_statement); 423 | break; 424 | case STMT_LABEL: 425 | handle_stmt(stmt->label_statement); 426 | break; 427 | case STMT_GOTO: 428 | handle_expr(stmt->goto_expression, 0); 429 | break; 430 | 431 | /* Declarations. */ 432 | case STMT_DECLARATION: 433 | /* 434 | * If a symbol has an initializer _and_ is one of the 435 | * should be watched varibles _or_ has an assignment 436 | * expression inside initializer. 437 | */ 438 | FOR_EACH_PTR(stmt->declaration, sym) { 439 | if (sym->initializer) 440 | { 441 | /* 442 | * If declared symbol is: 443 | * global _or_ function scope variable 444 | * of type: basetype, arrays or pointers. 445 | */ 446 | if (should_watch_sym(sym)) 447 | { 448 | verbose_assign(sym->pos.line, sym->ident->name, 1, 1, 0); 449 | try_add_symbol(sym->pos.line); 450 | } 451 | 452 | /* 453 | * Cases like: 454 | * int foo = 0xbeef; 455 | * { 456 | * int bar = foo++; 457 | * } 458 | * since foo is at function scope and being changed. 459 | */ 460 | else 461 | { 462 | verbose_assign(sym->pos.line, sym->ident->name, 1, 1, 1); 463 | handle_expr(sym->initializer, 0); 464 | } 465 | } 466 | else 467 | verbose_assign(sym->pos.line, sym->ident->name, 0, 1, 1); 468 | 469 | } END_FOR_EACH_PTR(sym); 470 | break; 471 | 472 | /* Compound statements. */ 473 | case STMT_COMPOUND: 474 | FOR_EACH_PTR(stmt->stmts, s) { 475 | handle_stmt(s); 476 | } END_FOR_EACH_PTR(s); 477 | break; 478 | } 479 | } 480 | 481 | /** 482 | * For a symbol @p sym belonging to our target function, 483 | * save it's scope and handles all it's statements. 484 | * 485 | * @param sym Function symbol. 486 | */ 487 | static void process_function(struct symbol *sym) 488 | { 489 | struct symbol *type; 490 | struct statement *stmt; 491 | 492 | type = sym->ctype.base_type; 493 | stmt = sym->ctype.modifiers & MOD_INLINE 494 | ? type->inline_stmt : type->stmt; 495 | 496 | /* Check if we have a compound statement and a return. */ 497 | if (stmt->type == STMT_COMPOUND && type->stmt->ret) 498 | { 499 | f_scope = type->stmt->ret->scope; 500 | struct statement *s; 501 | FOR_EACH_PTR(stmt->stmts, s) { 502 | handle_stmt(s); 503 | } END_FOR_EACH_PTR(s); 504 | } 505 | } 506 | 507 | /** 508 | * Given a symmbol list @p sym_list, iterates through 509 | * all the symbols and searches for the target 510 | * function. 511 | * 512 | * @param sym_list Symbol list. 513 | */ 514 | static void process(struct symbol_list *sym_list) 515 | { 516 | struct symbol *sym; 517 | 518 | FOR_EACH_PTR(sym_list, sym) { 519 | if (sym->type == SYM_NODE && get_base_type(sym)->type == SYM_FN) 520 | { 521 | if (strcmp(sym->ident->name, function) == 0) 522 | { 523 | process_function(sym); 524 | break; 525 | } 526 | } 527 | } END_FOR_EACH_PTR(sym); 528 | } 529 | 530 | /** 531 | * Init the static analysis internal structure argument. 532 | */ 533 | int static_analysis_init(void) 534 | { 535 | /* Initialize argument list. */ 536 | if (array_init(&analysis_arguments.args) < 0) 537 | return (-1); 538 | 539 | /* Initial arguments. */ 540 | array_add(&analysis_arguments.args, "pbd"); 541 | array_add(&analysis_arguments.args, "-Wno-strict-prototypes"); 542 | array_add(&analysis_arguments.args, "-Wno-decl"); 543 | array_add(&analysis_arguments.args, "-Wno-newline-eof"); 544 | return (0); 545 | } 546 | 547 | /** 548 | * Adds the argument @p arg and its value @p value into the 549 | * argument list for the static analysis. 550 | * 551 | * @param arg Argument. 552 | * @param value Argument value. 553 | * 554 | * @return Returns 0 if success and a negative value 555 | * otherwise. 556 | */ 557 | int static_analysis_add_arg(const char *arg, const char *value) 558 | { 559 | char *str; 560 | 561 | /* If not valid. */ 562 | if (!arg || !value) 563 | return (-1); 564 | 565 | /* Make room and copy. */ 566 | str = malloc(sizeof(char) * (strlen(arg) + strlen(value) + 1)); 567 | if (!str) 568 | return (-1); 569 | 570 | strcpy(str, arg); 571 | strcat(str, value); 572 | return ( array_add(&analysis_arguments.args, str) ); 573 | } 574 | 575 | /** 576 | * Finishes all allocated resources. 577 | * 578 | * @note There is no problem to call this function twice, since 579 | * array_finish nicely handle NULLed arguments. 580 | */ 581 | void static_analysis_finish(void) 582 | { 583 | size_t args_count; 584 | size_t args_end; 585 | args_count = array_size(&analysis_arguments.args); 586 | 587 | /* 588 | * If exists extra parameters, i.e, more than 5: 589 | * [0] 1st: pbd 590 | * [1] 2nd: -Wno-strict-prototypes 591 | * [2] 3rd: -Wno-decl 592 | * [3] 4th: -Wno-newline-eof 593 | * [.] (optional arguments here) 594 | * [4] 4th: filename 595 | * [5] 5th: (NULL) 596 | * lets deallocate them. 597 | */ 598 | if (args_count > 6) 599 | { 600 | args_end = (args_count - 6) + 4; 601 | for (size_t i = 4; i < args_end; i++) 602 | free( array_get(&analysis_arguments.args, i, NULL) ); 603 | } 604 | array_finish(&analysis_arguments.args); 605 | analysis_arguments.args = NULL; 606 | } 607 | 608 | /** 609 | * Do a static analysis in the C source code and returns a 610 | * hashtable with the breakpoint list. 611 | * 612 | * @param file Source to be analyzed. 613 | * @param func Function to be analyzed. 614 | * @param lines_l Lines list. 615 | * @param flags Program flags. 616 | * 617 | * @return Returns a breakpoint list containing all the lines 618 | * that PBD should break. 619 | * 620 | * @note This function is intended to be similar of what bp_createlist() 621 | * does, but in a 'smarter' way. 622 | */ 623 | struct hashtable *static_analysis(const char *file, const char *func, 624 | struct array *lines_l, uintptr_t firstbreak) 625 | { 626 | char *file_cur; 627 | struct dw_line *line; 628 | struct breakpoint *b; 629 | struct symbol_list *list; 630 | struct string_list *filelist; 631 | 632 | /* 633 | * Enable our default standard if none is set. 634 | * 'gnu11' standard seems to be the most used 635 | * by default between multiples versions of GCC 636 | * and Clang. 637 | */ 638 | if (!(args.flags & FLG_SANALYSIS_SETSTD)) 639 | static_analysis_add_arg("-std=", "gnu11"); 640 | 641 | /* Append our target file into the list. */ 642 | array_add(&analysis_arguments.args, (char *)file); 643 | array_add(&analysis_arguments.args, NULL); 644 | 645 | /* Initialize breakpoint hashtable. */ 646 | hashtable_init(&breakpoints, NULL); 647 | 648 | /* 649 | * Allocate our first breakpoint to the first function 650 | * instruction. Since this is an special case, there is 651 | * no need to known the line number. 652 | */ 653 | b = malloc(sizeof(struct breakpoint)); 654 | b->addr = firstbreak; 655 | b->original_byte = 0; 656 | b->line_no = 0; 657 | 658 | /* 659 | * Set our first breakpoint to the very 660 | * first instruction, PBD's main loop 661 | * expect this. 662 | */ 663 | hashtable_add(&breakpoints, (void*)b->addr, b); 664 | 665 | /* Last line. */ 666 | line = array_get(&lines_l, array_size(&lines_l) - 1, NULL); 667 | 668 | /* Allocate our second breakpoint. */ 669 | b = malloc(sizeof(struct breakpoint)); 670 | b->addr = line->addr; 671 | b->original_byte = 0; 672 | b->line_no = line->line_no; 673 | 674 | /* Last instruction, or so. */ 675 | hashtable_add(&breakpoints, (void*)b->addr, b); 676 | 677 | /* Fill some infos. */ 678 | filename = file; 679 | function = func; 680 | lines = lines_l; 681 | 682 | /* Init Sparse. */ 683 | filelist = NULL; 684 | sparse_initialize(array_size(&analysis_arguments.args), 685 | (char **)analysis_arguments.args->buf, &filelist); 686 | 687 | /* Analyze. */ 688 | FOR_EACH_PTR(filelist, file_cur) { 689 | list = sparse(file_cur); 690 | process(list); 691 | } END_FOR_EACH_PTR(file_cur); 692 | 693 | /* Since analysis is done, we can safely 694 | * dealloc our arguments. */ 695 | static_analysis_finish(); 696 | return (breakpoints); 697 | } 698 | -------------------------------------------------------------------------------- /src/arch/cpudisp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef CPUDISP_H 26 | #define CPUDISP_H 27 | 28 | #include "variable.h" 29 | 30 | #if defined(__x86_64__) 31 | #include "cpudisp_amd64.h" 32 | #endif 33 | 34 | /* CPU dispatcher. */ 35 | #if defined(__x86_64__) 36 | void select_cpu(void); 37 | #else 38 | /* Generic dispatcher for any arch. */ 39 | inline static void select_cpu(void) 40 | { 41 | offmemcmp = offmemcmp_generic; 42 | } 43 | #endif 44 | 45 | #endif /* CPUDISP_H */ 46 | -------------------------------------------------------------------------------- /src/arch/x86/ptrace_ia32.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "ptrace.h" 26 | 27 | /** 28 | * Architecture dependent ptrace helper functions. 29 | */ 30 | 31 | #if !defined(__i386__) 32 | #error "ptrace_ia32.c should only be included on x86 builds!" 33 | #endif 34 | 35 | /** 36 | * @brief Reads the current program counter (EIP in x86) from 37 | * the child process. 38 | * 39 | * @param child Child process. 40 | * 41 | * @return Returns the child program counter. 42 | */ 43 | uintptr_t pt_readregister_pc(pid_t child) 44 | { 45 | return (ptrace(PTRACE_PEEKUSER, child, 4 * EIP, NULL)); 46 | } 47 | 48 | /** 49 | * @brief Sets the program counter @p pc for the specified 50 | * @p child. 51 | * 52 | * @param child Child process. 53 | * @param pc New program counter. 54 | */ 55 | void pt_setregister_pc(pid_t child, uintptr_t pc) 56 | { 57 | ptrace(PTRACE_POKEUSER, child, 4 * EIP, pc); 58 | } 59 | 60 | /** 61 | * @brief Reads the current base pointer (EBP in x86) from 62 | * the child process. 63 | * 64 | * @param child Child process. 65 | * 66 | * @return Returns the child base pointer. 67 | */ 68 | uintptr_t pt_readregister_bp(pid_t child) 69 | { 70 | return (ptrace(PTRACE_PEEKUSER, child, 4 * EBP, NULL)); 71 | } 72 | 73 | /** 74 | * @brief Considering the child process is inside 75 | * the function prologue, retrieves the returning 76 | * address. 77 | * 78 | * @param child Child process. 79 | * 80 | * @return Returns the 'return' address. 81 | */ 82 | uintptr_t pt_readreturn_address(pid_t child) 83 | { 84 | uintptr_t sp; 85 | sp = ptrace(PTRACE_PEEKUSER, child, 4 * UESP, NULL); 86 | return (ptrace(PTRACE_PEEKDATA, child, sp, NULL)); 87 | } 88 | -------------------------------------------------------------------------------- /src/arch/x86_64/asm/avx2.S: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #define ALIGNED 0 26 | 27 | #if ALIGNED == 1 28 | #define VMOVDQx vmovdqa 29 | #else 30 | #define VMOVDQx vmovdqu 31 | #endif 32 | 33 | .macro eight_byte_comparison 34 | mov (%rsi, %r9, 1), %r8 35 | cmp %r8, (%rdi, %r9, 1) 36 | jne .found_8byte 37 | 38 | add $8, %r9 39 | .endm 40 | 41 | .macro one_byte_comparison 42 | movb (%rsi, %r9, 1), %r8b 43 | cmpb %r8b, (%rdi, %r9, 1) 44 | jne .found_1byte 45 | 46 | add $1, %r9 47 | .endm 48 | 49 | .macro avx2_comparison reg2 50 | add $32, %r9 51 | 52 | VMOVDQx -32(%rdi,%r9,1), %\reg2 53 | vpcmpeqb %ymm1, %\reg2, %\reg2 54 | vpmovmskb %\reg2, %r8 55 | cmp %r11, %r8 56 | jne .found_32byte 57 | .endm 58 | 59 | /* 60 | * Memory Comparison (with byte offset) routine (SSE2 Version) 61 | * 62 | * int64_t offmemcmp_avx2 (void *src, void *dest, size_t block_size, 63 | * size_length) 64 | * 65 | * @p src Src pointer 66 | * @p dst Dst pointer 67 | * @p block_size Memory block size, in bytes. 68 | * @p length Memory length. 69 | * 70 | * This function performs similar as the usual memcmp but returns 71 | * the offset @p block_size aligned. 72 | * 73 | * For instance, given two integers (4-byte) arrays that differs 74 | * in the third position, this function would return '12', meaning 75 | * that from the 12 byte, there is a difference. Note also that, 76 | * if the byte 12,13,14 or 15 differs between the two pointers, this 77 | * function still returns '12'. 78 | * 79 | * For block_size equals '1', this function will return 80 | * the exact byte offset. 81 | * 82 | * If both pointers are equal, -1 is returned, otherwise, 83 | * a number greater than or equals 0 if returned. 84 | * 85 | * Implementation details: 86 | * ----------------------- 87 | * 88 | * As can be easily noted, this is an assembly version of offmemcmp 89 | * written in C. This version differs from the former in the sense 90 | * that this routine uses SIMD registers, (AVX2/256-bit registers) 91 | * in order to speedup the comparison. 92 | * 93 | * Although pretty simple, this function tries to align both pointers, 94 | * whether 8-byte per iteration or 1-byte. When aligned (or not), 95 | * the remaining bytes are compared by a 64-byte loop with 2x32-bytes 96 | * comparisons. If there are remaining bytes, 8-bytes/1-byte comparisons 97 | * are made. 98 | * 99 | * Performance 100 | * ----------- 101 | * 102 | * In contrast to the SSE2 version, the AVX2 manages to keep on par with 103 | * (glibc) memcmp, staying at most 1-5% of performance difference. This 104 | * does not means that offmemcmp_avx2 can completely substitute memcmp 105 | * (in terms of performance) for all kinds of inputs, but I think its a 106 | * good replacement =). 107 | * 108 | * Requirements 109 | * ------------ 110 | * 111 | * Requires an AVX2-compatible processor, i.e: a fourth Intel generation 112 | * processor/Haswell (2013) or equivalent. 113 | * 114 | * 115 | * Register usage: 116 | * --------------- 117 | * rdi = v1 118 | * rsi = v2 119 | * rdx = block_size 120 | * rcx = n 121 | * 122 | * Our 'variables' 123 | * rax = return value, duh 124 | * r8 = temp 125 | * r9 = temp / offset 126 | * r10 = temp / skip counter (for align loop) 127 | * r11 = temp 128 | */ 129 | .globl offmemcmp_avx2 130 | .type offmemcmp_avx2, @function 131 | offmemcmp_avx2: 132 | mov $-1, %rax 133 | mov $0xFFFFFFFF, %r11 134 | 135 | # Check if we can align 136 | mov %rsi, %r9 137 | mov %rdi, %r10 138 | and $0x1F, %r9 139 | and $0x1F, %r10 140 | cmp %r9, %r10 141 | je .align_32byte 142 | 143 | xor %r9, %r9 144 | jmp .cond_loop_32byte # Not 'alignable' =( 145 | 146 | # ------- Align both pointers before SIMD ------- 147 | .align_32byte: 148 | mov $0x1F, %r10 149 | sub %r9 , %r10 # Iteration counter 150 | cmp $0x1F, %r10 151 | je .cond_loop_32byte # If already aligned 152 | 153 | xor %r9, %r9 154 | cmp $8, %rcx # Size should greater or equal 8-byte 155 | jl .cond_loop_align_32byte_1byte # Otherwise, 1-byte comparisons 156 | jmp .cond_loop_align_32byte_8byte # should be made 157 | 158 | # ------- Align 32-byte using 8-byte pointers 159 | .loop_align_32byte_8byte: 160 | eight_byte_comparison 161 | sub $8, %rcx 162 | sub $8, %r10 163 | 164 | .cond_loop_align_32byte_8byte: 165 | cmp $8, %rcx 166 | jl .cond_loop_align_32byte_1byte 167 | cmp $8, %r10 168 | jge .loop_align_32byte_8byte 169 | 170 | # Maybe we need one-byte alignment? 171 | jmp .cond_loop_align_32byte_1byte 172 | 173 | # ------- Align 32-byte using 1-byte pointers 174 | .loop_align_32byte_1byte: 175 | one_byte_comparison 176 | sub $1, %rcx 177 | sub $1, %r10 178 | 179 | .cond_loop_align_32byte_1byte: 180 | cmp $0, %rcx 181 | je .out 182 | cmp $0, %r10 183 | jge .loop_align_32byte_1byte 184 | 185 | # Addresses are aligned 186 | jmp .cond_loop_32byte 187 | 188 | # ------- SIMD 2x32-byte loop ------- 189 | .loop_32byte: 190 | VMOVDQx 0(%rsi,%r9,1), %ymm1 191 | avx2_comparison ymm2 192 | 193 | VMOVDQx 0(%rsi,%r9,1), %ymm1 194 | avx2_comparison ymm2 195 | 196 | sub $64, %rcx 197 | .cond_loop_32byte: 198 | cmp $64, %rcx 199 | jge .loop_32byte 200 | jmp .cond_loop_8byte 201 | 202 | # ------- 8-byte remaining bytes loop ------- 203 | .loop_8byte: 204 | eight_byte_comparison 205 | sub $8, %rcx 206 | 207 | .cond_loop_8byte: 208 | cmp $8, %rcx 209 | jge .loop_8byte 210 | jmp .cond_loop_1byte 211 | 212 | # ------- 1-byte remaining bytes loop ------- 213 | .loop_1byte: 214 | one_byte_comparison 215 | sub $1, %rcx 216 | 217 | .cond_loop_1byte: 218 | cmp $0, %rcx 219 | jne .loop_1byte 220 | jmp .out 221 | 222 | # ------- Return values ------- 223 | .found_32byte: 224 | not %r8 225 | tzcnt %r8, %rax 226 | sub $32, %r9 227 | sub $1, %rdx 228 | not %rdx 229 | and %rdx, %rax 230 | add %r9, %rax 231 | ret 232 | .found_8byte: 233 | mov (%rsi, %r9, 1), %r8 234 | mov (%rdi, %r9, 1), %rax 235 | xor %r8, %rax 236 | tzcnt %rax, %rax # Trailing zeros = a 237 | 238 | mov %rdx, %r11 239 | mov %rdx, %r8 240 | xor %rdx, %rdx 241 | shl $3, %r8 # block_size << 3 = b 242 | 243 | div %r8 # Trailing zeros / (block_size << 3) = c 244 | mul %r11 # c * block_size = d 245 | add %r9, %rax # off = d + a 246 | ret 247 | .found_1byte: 248 | mov %r9, %rax 249 | sub $1, %rdx 250 | not %rdx 251 | and %rdx, %rax 252 | .out: 253 | ret 254 | -------------------------------------------------------------------------------- /src/arch/x86_64/asm/sse2.S: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #define ALIGNED 0 26 | 27 | #if ALIGNED == 1 28 | #define MOVDQx movdqa 29 | #else 30 | #define MOVDQx movdqu 31 | #endif 32 | 33 | .macro eight_byte_comparison 34 | mov (%rsi, %r9, 1), %r8 35 | cmp %r8, (%rdi, %r9, 1) 36 | jne .found_8byte 37 | 38 | add $8, %r9 39 | .endm 40 | 41 | .macro one_byte_comparison 42 | movb (%rsi, %r9, 1), %r8b 43 | cmpb %r8b, (%rdi, %r9, 1) 44 | jne .found_1byte 45 | 46 | add $1, %r9 47 | .endm 48 | 49 | .macro sse2_comparison reg2 50 | add $16, %r9 51 | 52 | MOVDQx -16(%rdi,%r9,1), %\reg2 53 | pcmpeqb %xmm0, %\reg2 54 | pmovmskb %\reg2, %r8 55 | cmp $0xFFFF, %r8 56 | jne .found_16byte 57 | .endm 58 | 59 | /* 60 | * Memory Comparison (with byte offset) routine (SSE2 Version) 61 | * 62 | * int64_t offmemcmp_sse2 (void *src, void *dest, size_t block_size, 63 | * size_length) 64 | * 65 | * @p src Src pointer 66 | * @p dst Dst pointer 67 | * @p block_size Memory block size, in bytes. 68 | * @p length Memory length. 69 | * 70 | * This function performs similar as the usual memcmp but returns 71 | * the offset @p block_size aligned. 72 | * 73 | * For instance, given two integers (4-byte) arrays that differs 74 | * in the third position, this function would return '12', meaning 75 | * that from the 12 byte, there is a difference. Note also that, 76 | * if the byte 12,13,14 or 15 differs between the two pointers, this 77 | * function still returns '12'. 78 | * 79 | * For block_size equals '1', this function will return 80 | * the exact byte offset. 81 | * 82 | * If both pointers are equal, -1 is returned, otherwise, 83 | * a number greater than or equals 0 if returned. 84 | * 85 | * Implementation details: 86 | * ----------------------- 87 | * 88 | * As can be easily noted, this is an assembly version of offmemcmp 89 | * written in C. This version differs from the former in the sense 90 | * that this routine uses SIMD registers, (SSE2/128-bit registers) 91 | * in order to speedup the comparison. 92 | * 93 | * Although pretty simple, this function tries to align both pointers, 94 | * whether 8-byte per iteration or 1-byte. When aligned (or not), 95 | * the remaining bytes are compared by a 32-byte loop with 2x16-bytes 96 | * comparisons. If there are remaining bytes, 8-bytes/1-byte comparisons 97 | * are made. 98 | * 99 | * Performance 100 | * ----------- 101 | * 102 | * This implementation is ~ 41% faster than the original C implementation 103 | * of offmemcmp with -O3. However, glibc's memcmp is ~ 94% faster than the 104 | * C code, so there is a lot room for improvements here. 105 | * 106 | * Requirements 107 | * ------------ 108 | * 109 | * Requires a SSE2-compatible processor, i.e: Pentium 4 or later. 110 | * 111 | * 112 | * Register usage: 113 | * --------------- 114 | * rdi = v1 115 | * rsi = v2 116 | * rdx = block_size 117 | * rcx = n 118 | * 119 | * Our 'variables' 120 | * rax = return value, duh 121 | * r8 = temp 122 | * r9 = temp / offset 123 | * r10 = temp / skip counter (for align loop) 124 | * r11 = temp 125 | */ 126 | .globl offmemcmp_sse2 127 | .type offmemcmp_sse2, @function 128 | offmemcmp_sse2: 129 | mov $-1, %rax 130 | 131 | # Check if we can align 132 | mov %rsi, %r9 133 | mov %rdi, %r10 134 | and $0xF, %r9 135 | and $0xF, %r10 136 | cmp %r9, %r10 137 | je .align_16byte 138 | 139 | xor %r9, %r9 140 | jmp .cond_loop_16byte # Not 'alignable' =( 141 | 142 | # ------- Align both pointers before SIMD ------- 143 | .align_16byte: 144 | mov $0xF, %r10 145 | sub %r9 , %r10 # Iteration counter 146 | cmp $0xF, %r10 147 | je .cond_loop_16byte # If already aligned 148 | 149 | xor %r9, %r9 150 | cmp $8, %rcx # Size should greater or equal 8-byte 151 | jl .cond_loop_align_16byte_1byte # Otherwise, 1-byte comparisons 152 | jmp .cond_loop_align_16byte_8byte # should be made 153 | 154 | # ------- Align 16-byte using 8-byte pointers 155 | .loop_align_16byte_8byte: 156 | eight_byte_comparison 157 | sub $8, %rcx 158 | sub $8, %r10 159 | 160 | .cond_loop_align_16byte_8byte: 161 | cmp $8, %rcx 162 | jl .cond_loop_align_16byte_1byte 163 | cmp $8, %r10 164 | jge .loop_align_16byte_8byte 165 | 166 | # Maybe we need one-byte alignment? 167 | jmp .cond_loop_align_16byte_1byte 168 | 169 | # ------- Align 16-byte using 1-byte pointers 170 | .loop_align_16byte_1byte: 171 | one_byte_comparison 172 | sub $1, %rcx 173 | sub $1, %r10 174 | 175 | .cond_loop_align_16byte_1byte: 176 | cmp $0, %rcx 177 | je .out 178 | cmp $0, %r10 179 | jge .loop_align_16byte_1byte 180 | 181 | # Addresses are aligned 182 | jmp .cond_loop_16byte 183 | 184 | # ------- SIMD 2x16-byte loop ------- 185 | .loop_16byte: 186 | MOVDQx 0(%rsi,%r9,1), %xmm0 187 | sse2_comparison xmm1 188 | 189 | MOVDQx 0(%rsi,%r9,1), %xmm0 190 | sse2_comparison xmm1 191 | 192 | sub $32, %rcx 193 | .cond_loop_16byte: 194 | cmp $32, %rcx 195 | jge .loop_16byte 196 | jmp .cond_loop_8byte 197 | 198 | # ------- 8-byte remaining bytes loop ------- 199 | .loop_8byte: 200 | eight_byte_comparison 201 | sub $8, %rcx 202 | 203 | .cond_loop_8byte: 204 | cmp $8, %rcx 205 | jge .loop_8byte 206 | jmp .cond_loop_1byte 207 | 208 | # ------- 1-byte remaining bytes loop ------- 209 | .loop_1byte: 210 | one_byte_comparison 211 | sub $1, %rcx 212 | 213 | .cond_loop_1byte: 214 | cmp $0, %rcx 215 | jne .loop_1byte 216 | jmp .out 217 | 218 | # ------- Return values ------- 219 | .found_16byte: 220 | not %r8 221 | tzcnt %r8, %rax 222 | sub $16, %r9 223 | sub $1, %rdx 224 | not %rdx 225 | and %rdx, %rax 226 | add %r9, %rax 227 | ret 228 | .found_8byte: 229 | mov (%rsi, %r9, 1), %r8 230 | mov (%rdi, %r9, 1), %rax 231 | xor %r8, %rax 232 | tzcnt %rax, %rax # Trailing zeros = a 233 | 234 | mov %rdx, %r11 235 | mov %rdx, %r8 236 | xor %rdx, %rdx 237 | shl $3, %r8 # block_size << 3 = b 238 | 239 | div %r8 # Trailing zeros / (block_size << 3) = c 240 | mul %r11 # c * block_size = d 241 | add %r9, %rax # off = d + a 242 | ret 243 | .found_1byte: 244 | mov %r9, %rax 245 | sub $1, %rdx 246 | not %rdx 247 | and %rdx, %rax 248 | .out: 249 | ret 250 | -------------------------------------------------------------------------------- /src/arch/x86_64/cpudisp_amd64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef CPUDISP_AMD64_H 26 | #define CPUDISP_AMD64_H 27 | 28 | /* Extern declarations. */ 29 | int64_t offmemcmp_avx2(void *src, void *dest, size_t block_size, 30 | size_t length); 31 | int64_t offmemcmp_sse2(void *src, void *dest, size_t block_size, 32 | size_t length); 33 | 34 | #endif /* CPUDISP_AMD64_H */ 35 | -------------------------------------------------------------------------------- /src/arch/x86_64/dispatcher.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include "cpudisp.h" 28 | 29 | /* Masks. */ 30 | #define OSXSAVE_MASK (1 << 27) 31 | #define AVX2_MASK (1 << 5) 32 | #define XCR0_SSE (1 << 1) 33 | #define XCR0_AVX (1 << 2) 34 | #define XCR0_SIMD_SUPPORT (XCR0_SSE|XCR0_AVX) 35 | 36 | /** 37 | * CPUID instruction. 38 | * 39 | * @param eax EAX Register. 40 | * @param ecx ECX Register. 41 | * @param abcd EAX, EBX, ECX and EDX registers. 42 | * 43 | */ 44 | static void __cpuid(uint32_t eax, uint32_t ecx, uint32_t* abcd) 45 | { 46 | uint32_t ebx, edx; 47 | ebx = 0; 48 | edx = 0; 49 | __asm__ __volatile__ 50 | ( 51 | "cpuid" 52 | : "+b" (ebx), "+a" (eax), "+c" (ecx), "=d" (edx) 53 | ); 54 | 55 | abcd[0] = eax; 56 | abcd[1] = ebx; 57 | abcd[2] = ecx; 58 | abcd[3] = edx; 59 | } 60 | 61 | /** 62 | * Checks if SSE and AVX are enabled in the OS. 63 | * 64 | * @return Returns 1 if enabled and 0 otherwise. 65 | */ 66 | int check_xcr0() 67 | { 68 | uint32_t xcr0; 69 | __asm__ __volatile__ 70 | ( 71 | "xgetbv" 72 | : "=a" (xcr0) 73 | : "c" (0) 74 | : "%edx" 75 | ); 76 | return ((xcr0 & XCR0_SIMD_SUPPORT) == XCR0_SIMD_SUPPORT); 77 | } 78 | 79 | /** 80 | * Checks if the CPU supports AVX2 and the OS 81 | * is able to use. 82 | * 83 | * @return Returns 1 if AVX2 is supported and 0 84 | * otherwise. 85 | */ 86 | int supports_avx2() 87 | { 88 | uint32_t abcd[4]; 89 | 90 | /* OSXSAVE bit, EAX = 01h, ECX = 0h, result in ECX. */ 91 | __cpuid(1, 0, abcd); 92 | if ((abcd[2] & OSXSAVE_MASK) != OSXSAVE_MASK) 93 | return (0); 94 | 95 | /* AVX and SSE must be enabled. */ 96 | if (!check_xcr0()) 97 | return (0); 98 | 99 | /* 100 | * Structured Extended Feature Flags 101 | * AVX2 Support, EAX = 07h, ECX = 0h, result in EBX, bit 5. 102 | */ 103 | __cpuid(7, 0, abcd); 104 | if ((abcd[1] & AVX2_MASK) != AVX2_MASK) 105 | return (0); 106 | 107 | return (1); 108 | } 109 | 110 | /** 111 | * Select the appropriate function pointers 112 | * accordingly with the host CPU. 113 | */ 114 | void select_cpu(void) 115 | { 116 | /* If AVX2 Enabled. */ 117 | #ifdef CAN_BUILD_AVX2 118 | if (supports_avx2()) 119 | offmemcmp = offmemcmp_avx2; 120 | else 121 | #endif 122 | offmemcmp = offmemcmp_sse2; 123 | } 124 | -------------------------------------------------------------------------------- /src/arch/x86_64/ptrace_amd64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "ptrace.h" 26 | 27 | /** 28 | * Architecture dependent ptrace helper functions. 29 | */ 30 | 31 | #if !defined(__x86_64__) 32 | #error "ptrace_amd64.c should only be included on x86_64 builds!" 33 | #endif 34 | 35 | /** 36 | * @brief Reads the current program counter (RIP in x86_64) from 37 | * the child process. 38 | * 39 | * @param child Child process. 40 | * 41 | * @return Returns the child program counter. 42 | */ 43 | uintptr_t pt_readregister_pc(pid_t child) 44 | { 45 | return (ptrace(PTRACE_PEEKUSER, child, 8 * RIP, NULL)); 46 | } 47 | 48 | /** 49 | * @brief Sets the program counter @p pc for the specified 50 | * @p child. 51 | * 52 | * @param child Child process. 53 | * @param pc New program counter. 54 | */ 55 | void pt_setregister_pc(pid_t child, uintptr_t pc) 56 | { 57 | ptrace(PTRACE_POKEUSER, child, 8 * RIP, pc); 58 | } 59 | 60 | /** 61 | * @brief Reads the current base pointer (RBP in x86_64) from 62 | * the child process. 63 | * 64 | * @param child Child process. 65 | * 66 | * @return Returns the child base pointer. 67 | */ 68 | uintptr_t pt_readregister_bp(pid_t child) 69 | { 70 | return (ptrace(PTRACE_PEEKUSER, child, 8 * RBP, NULL)); 71 | } 72 | 73 | /** 74 | * @brief Considering the child process is inside 75 | * the function prologue, retrieves the returning 76 | * address. 77 | * 78 | * @param child Child process. 79 | * 80 | * @return Returns the 'return' address. 81 | */ 82 | uintptr_t pt_readreturn_address(pid_t child) 83 | { 84 | uintptr_t sp; 85 | sp = ptrace(PTRACE_PEEKUSER, child, 8 * RSP, NULL); 86 | return (ptrace(PTRACE_PEEKDATA, child, sp, NULL)); 87 | } 88 | -------------------------------------------------------------------------------- /src/array.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "array.h" 26 | #include "dwarf_helper.h" 27 | 28 | /** 29 | * Initializes the array. 30 | * 31 | * @param array Array structure pointer to be 32 | * initialized. 33 | * 34 | * @return Returns 0 if success and a negative number 35 | * otherwise. 36 | */ 37 | int array_init(struct array **array) 38 | { 39 | struct array *out; 40 | out = calloc(1, sizeof(struct array)); 41 | if (out == NULL) 42 | return (-1); 43 | 44 | out->capacity = ARRAY_UTILS_DEFAULT_SIZE; 45 | out->elements = 0; 46 | out->buf = calloc(out->capacity, sizeof(void *)); 47 | if (out->buf == NULL) 48 | { 49 | free(out); 50 | return (-1); 51 | } 52 | 53 | *array = out; 54 | return (0); 55 | } 56 | 57 | /** 58 | * Deallocates the array. 59 | * 60 | * @return Returns 0 if success. 61 | * 62 | * @note The elements present are not freed and its 63 | * up to the user to free them. 64 | */ 65 | int array_finish(struct array **array) 66 | { 67 | /* Invalid array. */ 68 | if (*array == NULL) 69 | return (-1); 70 | 71 | free((*array)->buf); 72 | free(*array); 73 | return (0); 74 | } 75 | 76 | /** 77 | * Adds a void pointer into the array. 78 | * 79 | * @param ar Array structure. 80 | * @param e Element to be added. 81 | * 82 | * @return Returns 0 if success and a negative 83 | * number otherwise. 84 | */ 85 | int array_add(struct array **ar, void *e) 86 | { 87 | struct array *array; /* Array. */ 88 | void **new_buf; /* New buffer. */ 89 | size_t old_capacity; /* Old capacity. */ 90 | size_t new_capacity; /* New capacity. */ 91 | 92 | array = *ar; 93 | 94 | /* Array exists?. */ 95 | if (array == NULL) 96 | return (-1); 97 | 98 | /* If bigger than capacity. */ 99 | if (array->elements >= array->capacity) 100 | { 101 | old_capacity = array->capacity; 102 | new_capacity = old_capacity * 2; 103 | 104 | /* New memory. */ 105 | new_buf = realloc(array->buf, new_capacity * sizeof(void *)); 106 | if (new_buf) 107 | array->buf = new_buf; 108 | else 109 | return (-1); 110 | 111 | array->capacity = new_capacity; 112 | } 113 | 114 | /* Stores the element. */ 115 | array->buf[array->elements] = e; 116 | array->elements++; 117 | return (0); 118 | } 119 | 120 | /** 121 | * Removes an element from a given position. 122 | * 123 | * @param ar Array structure pointer. 124 | * @param pos Position to be removed. 125 | * @param e Element to be returned. 126 | * 127 | * @return Returns the removed element or NULL if 128 | * invalid position. 129 | */ 130 | void* array_remove(struct array **ar, size_t pos, void **e) 131 | { 132 | struct array *array; /* Array. */ 133 | void *ret; /* Returned element. */ 134 | 135 | array = *ar; 136 | 137 | /* Array exists and has a valid position?. */ 138 | if (array == NULL || pos >= array->elements) 139 | return (NULL); 140 | 141 | ret = array->buf[pos]; 142 | if (e != NULL) 143 | *e = ret; 144 | 145 | /* If middle. */ 146 | if (pos != array->elements - 1) 147 | { 148 | memmove(&(array->buf[pos]), &(array->buf[pos + 1]), 149 | (array->elements - 1 - pos) * sizeof(void *)); 150 | } 151 | 152 | array->elements--; 153 | return (ret); 154 | } 155 | 156 | /** 157 | * Removes the last element from the specified array. 158 | * 159 | * @param ar Array structure pointer. 160 | * @param e Element to be returned. 161 | * 162 | * @return Returns the removed element or NULL if array empty. 163 | */ 164 | void *array_remove_last(struct array **ar, void **e) 165 | { 166 | /* Array exists and has at least one valid element. */ 167 | if (*ar == NULL || (*ar)->elements < 1) 168 | return (NULL); 169 | 170 | return ( array_remove(ar, (*ar)->elements - 1, e) ); 171 | } 172 | 173 | /** 174 | * Gets the element from the given position. 175 | * 176 | * @param array Array structure pointer. 177 | * @param pos Position desired. 178 | * @param e Element to be returned. 179 | * 180 | * @return Returns the element from the given position 181 | * and NULL if invalid position. 182 | */ 183 | void* array_get(struct array **array, size_t pos, void **e) 184 | { 185 | /* Array exists and has a valid position. */ 186 | if (*array == NULL || pos >= (*array)->elements) 187 | return (NULL); 188 | 189 | /* Valid pointer. */ 190 | if (e != NULL) 191 | *e = (*array)->buf[pos]; 192 | 193 | return ( (*array)->buf[pos] ); 194 | } 195 | 196 | /** 197 | * Gets the last element from the specified array. 198 | * 199 | * @param array Array structure pointer. 200 | * @param e Eelement to be returned. 201 | * 202 | * @return Returns the last element or NULL if array empty. 203 | */ 204 | void *array_get_last(struct array **array, void **e) 205 | { 206 | /* Array exists and theres at least one valid element. */ 207 | if (*array == NULL || (*array)->elements < 1) 208 | return (NULL); 209 | 210 | /* Valid pointer. */ 211 | if (e != NULL) 212 | *e = (*array)->buf[(*array)->elements - 1]; 213 | 214 | return ( (*array)->buf[(*array)->elements - 1] ); 215 | } 216 | 217 | /** 218 | * Gets the number of elements currently present in 219 | * the array. 220 | * 221 | * @param array Array structure pointer. 222 | * 223 | * @return Returns the number of elements 224 | * in the array. 225 | * 226 | * @note This function returns 0 even if the @p array 227 | * is invalid!. 228 | */ 229 | size_t array_size(struct array **array) 230 | { 231 | /* Array exists and theres at least one valid element. */ 232 | if (*array == NULL || (*array)->elements < 1) 233 | return (0); 234 | 235 | return ( (*array)->elements ); 236 | } 237 | 238 | /** 239 | * @brief Given an array and a comparator function pointer, 240 | * orders the array. 241 | * 242 | * @param ar Array to be ordered. 243 | * @param cmp Compare function pointer. 244 | * 245 | * @return Returns 0 if success and a negative number 246 | * otherwise. 247 | */ 248 | int array_sort(struct array **ar, 249 | int (*cmp)(const void*,const void*)) 250 | { 251 | struct array *array; /* Array. */ 252 | array = *ar; 253 | 254 | /* Array exists and has elements?. */ 255 | if (array == NULL || !array->elements) 256 | return (-1); 257 | 258 | /* Sort. */ 259 | qsort(array->buf, array->elements, sizeof(void *), cmp); 260 | return (0); 261 | } 262 | 263 | /*============================================================================* 264 | * Tests * 265 | *============================================================================*/ 266 | 267 | /** 268 | * @brief Tries to add @p size elements into the array. 269 | * @param array Target array. 270 | * @param size Elements amount. 271 | * @return Returns 0 if success and a negative number otherwise. 272 | */ 273 | int array_addtest(struct array *array, int size) 274 | { 275 | /* Initialize with sequencial data. */ 276 | for (int i = 0; i < size; i++) 277 | { 278 | int *num = malloc(sizeof(int)); 279 | if (num != NULL) 280 | { 281 | *num = i; 282 | if (array_add(&array, num)) 283 | { 284 | fprintf(stderr, "array_utils: error while adding: %d\n", *num); 285 | fprintf(stderr, " at pos: %zu / capacity: %zu\n", 286 | array->elements, array->capacity); 287 | 288 | return (-1); 289 | } 290 | } 291 | else 292 | { 293 | fprintf(stderr, "array_utils: error while malloc'ing\n"); 294 | return (-1); 295 | } 296 | } 297 | 298 | return (0); 299 | } 300 | 301 | /** 302 | * @brief Checks if the previously added elements are correct. 303 | * @param array Target array. 304 | * @param size Elements amount. 305 | * @return Returns 0 if success and a negative number otherwise. 306 | */ 307 | int array_integritytest(struct array *array, int size) 308 | { 309 | /* Checks if all elements was insert correctly. */ 310 | for (int i = 0; i < size; i++) 311 | { 312 | int *el; 313 | if ( array_get(&array, i, (void *)&el) != NULL ) 314 | { 315 | if (*el != i) 316 | { 317 | fprintf(stderr, "array_utils: element (%d) at pos %d is " 318 | "different from", *el, i); 319 | fprintf(stderr, " expected (%d)\n", i); 320 | return (-1); 321 | } 322 | } 323 | else 324 | { 325 | fprintf(stderr, "array_utils: element does not exist at given" 326 | " pos: %d\n", i); 327 | return (-1); 328 | } 329 | } 330 | 331 | return (0); 332 | } 333 | 334 | /** 335 | * @brief Tries to remove the first half of the array. 336 | * @param array Target array. 337 | * @param size Elements amount. 338 | * @return Returns 0 if success and a negative number otherwise. 339 | */ 340 | int array_removehalf(struct array *array, int size) 341 | { 342 | /* Removes the first half. */ 343 | for (int i = 0; i < size/2; i++) 344 | { 345 | int *el = (int *) array_remove(&array, 0, NULL); 346 | 347 | if (el != NULL) 348 | free(el); 349 | else 350 | { 351 | fprintf(stderr, "array_utils: was not possible to remove at" 352 | " position %d\n", i); 353 | } 354 | } 355 | 356 | return (0); 357 | } 358 | 359 | /** 360 | * @brief Checks if the first half remaining is valid. 361 | * @param array Target array. 362 | * @param size Elements amount. 363 | * @return Returns 0 if success and a negative number otherwise. 364 | */ 365 | int array_checkhalf(struct array *array, int size) 366 | { 367 | /* Checks if the remaining is correct. */ 368 | int start_element = size/2; 369 | for (int i = 0; i < size/2; i++) 370 | { 371 | int *el; 372 | if (array_get(&array, i, (void *)&el) != NULL ) 373 | { 374 | if (*el != start_element + i) 375 | { 376 | fprintf(stderr, "array_utils: element (%d) at pos %d" 377 | " is different from", *el, i); 378 | fprintf(stderr, " expected (%d)\n", start_element + i); 379 | return (-1); 380 | } 381 | } 382 | else 383 | { 384 | fprintf(stderr, "array_utils: element does not exist at" 385 | " given pos: %d\n", i); 386 | return (-1); 387 | } 388 | } 389 | 390 | return (0); 391 | } 392 | 393 | /** 394 | * @brief Tries to remove the remaining half of the array. 395 | * @param array Target array. 396 | * @param size Elements amount. 397 | * @return Returns 0 if success and a negative number otherwise. 398 | */ 399 | int array_remove_rem_half(struct array *array, int size) 400 | { 401 | /* Remove the remaining half. */ 402 | for (int i = 0; i < size/2; i++) 403 | { 404 | int *el = (int *) array_remove(&array, 0, NULL); 405 | 406 | if (el != NULL) 407 | free(el); 408 | else 409 | { 410 | fprintf(stderr, "array_utils: was not possible to remove at" 411 | " position %d\n", i); 412 | } 413 | } 414 | 415 | return (0); 416 | } 417 | 418 | /** 419 | * Runs a series of checks in the available array functions 420 | * in order to test all the funcionalities. 421 | * 422 | * Current tests: 423 | * - Heavy insertions 424 | * - Check for consistent values 425 | * - Remove values 426 | * - Check for consistent values 427 | * 428 | * @return Returns 0 if success and a negative number 429 | * otherwise. 430 | */ 431 | int array_selftest(void) 432 | { 433 | #define ARRAY_SIZE 32000 434 | struct array *array; 435 | 436 | /* Initializes array. */ 437 | if (array_init(&array)) 438 | return (-1); 439 | 440 | /* Tests. */ 441 | printf("Initialize array [%s]\n", 442 | !array_addtest(array, ARRAY_SIZE) ? "PASSED" : "FAILED"); 443 | printf("Checking elements [%s]\n", 444 | !array_integritytest(array, ARRAY_SIZE) ? "PASSED" : "FAILED"); 445 | printf("Remove first half [%s]\n", 446 | !array_removehalf(array, ARRAY_SIZE) ? "PASSED" : "FAILED"); 447 | printf("First half integrity [%s]\n", 448 | !array_checkhalf(array, ARRAY_SIZE) ? "PASSED" : "FAILED"); 449 | printf("Remove half remaining [%s]\n", 450 | !array_remove_rem_half(array, ARRAY_SIZE) ? "PASSED" : "FAILED"); 451 | 452 | /* Free array. */ 453 | array_finish(&array); 454 | 455 | /* Awesome =). */ 456 | return (0); 457 | } 458 | 459 | #if 0 460 | /** 461 | * @brief Execute tests 462 | */ 463 | int main() 464 | { 465 | return (array_selftest()); 466 | } 467 | #endif 468 | -------------------------------------------------------------------------------- /src/benchs/Makefile: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2020 Davidson Francis 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC ?= gcc 24 | override CFLAGS += -Wall -Wextra 25 | override CFLAGS += -std=c99 26 | override CFLAGS += -fno-omit-frame-pointer -O0 -gdwarf-2 27 | 28 | # 29 | # PIE Support check 30 | # 31 | # In order to PBD works as expected, every source intended to be analyzed 32 | # _should_ be built with at least -O0 and -gdwarf-2, this is not optional, 33 | # nonetheless, it's also highly desirable that the frame pointer is not 34 | # ommited, thus: -fno-omit-frame-pointer. 35 | # 36 | # Last but not least, PBD doesn't support 'Position Independent Executables' 37 | # and all the addresses need to be known from before execution. Since some 38 | # compilers build 'PIE' executables (yeah, redundant ;-)) by default, we 39 | # also need to check this in execution time. 40 | # 41 | # --- 42 | # This solution is not the best but at least cover GCC and CLANG... 43 | # 44 | PIE_SUPPORT := $(shell echo "int main(){}" | $(CC) -x c -; file a.out \ 45 | | grep -Ec "shared|pie"; rm a.out &> /dev/null) 46 | 47 | ifeq ($(PIE_SUPPORT), 1) 48 | override CFLAGS += -no-pie 49 | endif 50 | 51 | # Source 52 | C_SRC = $(wildcard *.c) 53 | OBJ = $(C_SRC:.c=.o) 54 | 55 | # Pretty print 56 | Q := @ 57 | ifeq ($(V), 1) 58 | Q := 59 | endif 60 | 61 | %.o: %.c 62 | @echo " CC $@" 63 | $(Q)$(CC) $< $(CFLAGS) -c -o $@ 64 | 65 | all: bench run_benchs 66 | 67 | bench: $(OBJ) 68 | @echo " LD $@" 69 | $(Q)$(CC) $^ $(CFLAGS) -o $@ 70 | 71 | run_benchs: bench 72 | @bash run-benchs.sh 73 | 74 | clean: 75 | @echo " CLEAN" 76 | @rm -f $(OBJ) bench 77 | -------------------------------------------------------------------------------- /src/benchs/bench.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | static uint64_t numb1 = 1; 29 | static uint64_t numb2 = 2; 30 | static uint64_t numb3 = 3; 31 | static uint64_t numb4 = 4; 32 | static uint64_t numb5 = 5; 33 | 34 | static char array[40000]; 35 | static int n; 36 | static int i; 37 | 38 | /** 39 | * Dummy function that just do some 'heavy' computation 40 | * and changes the variables in the last moment. 41 | * 42 | * This routine exercises the best case scenario for 43 | * PBD: the assignment statements will be executed only 44 | * once, since the for loop and if will be ignored. The 45 | * execution time will be nearly identical to the one 46 | * without debugger. 47 | */ 48 | static void do_work1(void) 49 | { 50 | for (int i = 0; i < n; i++) 51 | { 52 | if (i == n - 1) 53 | { 54 | numb1 = 7; 55 | numb2 = 8; 56 | numb3 = 9; 57 | numb4 = 10; 58 | numb5 = 11; 59 | array[39999] = numb1; 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * Do some random 'heavy' computation in a 'real-world'-like 66 | * scenario: monitored breakpoints executes very often _and_ 67 | * there are also statements that will be ignored. 68 | */ 69 | static void do_work2(void) 70 | { 71 | for (int i = 0; i < n; i++) 72 | { 73 | /* 74 | * This loop and the if statement inside will be 75 | * completely ignored by PBD, since all the 76 | * variables changes is outside the function scope. 77 | */ 78 | for (int j = 0; j < 10; j++) 79 | { 80 | if (j % 100 == 0) 81 | { 82 | int var; 83 | var = j * 50; 84 | var++; 85 | } 86 | } 87 | 88 | /* Comparison ignored. */ 89 | if (i % 2 == 0) 90 | { 91 | /* Not ignored. */ 92 | numb1 = 7; 93 | numb2 = 8; 94 | numb3 = 9; 95 | numb4 = 10; 96 | numb5 = 11; 97 | array[39999] = numb1; 98 | } 99 | } 100 | } 101 | 102 | /** 103 | * Random 'heavy' computation in the PBD's worst case 104 | * scenario: nothing or almost nothing will be ignored. 105 | */ 106 | static void do_work3(void) 107 | { 108 | for (i = 0; i < n; i++) 109 | { 110 | /* Not ignored. */ 111 | numb1 = 7; 112 | numb2 = 8; 113 | numb3 = 9; 114 | numb4 = 10; 115 | numb5 = 11; 116 | array[39999] = numb1; 117 | } 118 | } 119 | 120 | int main(int argc, char **argv) 121 | { 122 | int f; 123 | 124 | if (argc < 2) 125 | return (1); 126 | 127 | n = atoi(argv[1]); 128 | f = atoi(argv[2]); 129 | 130 | if (f == 1) 131 | do_work1(); 132 | else if (f == 2) 133 | do_work2(); 134 | else 135 | do_work3(); 136 | } 137 | -------------------------------------------------------------------------------- /src/benchs/plot.R: -------------------------------------------------------------------------------- 1 | # 2 | # MIT License 3 | # 4 | # Copyright (c) 2019-2020 Davidson Francis 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | # "Constants" 26 | DO_WORK1 <- 1 27 | DO_WORK2 <- 2 28 | DO_WORK3 <- 3 29 | LINE_WIDTH <- 2 30 | 31 | data <- read.csv("csv_times", header=TRUE, sep=",") 32 | normal_pbd <- data$normal_pbd 33 | static_pbd <- data$static_pbd 34 | gdb <- data$gdb 35 | 36 | # Divide between works 37 | # normal_pbd[[1]] = do_work1 38 | # normal_pbd[[2]] = do_work2 39 | # normal_pbd[[3]] = do_work3 40 | # and so on.... 41 | normal_pbd <- split(normal_pbd, ceiling(seq_along(normal_pbd)/8)) 42 | static_pbd <- split(static_pbd, ceiling(seq_along(static_pbd)/8)) 43 | gdb <- split(gdb, ceiling(seq_along(gdb)/8)) 44 | 45 | # X axis 46 | x_axis <- seq(from=12500, to=100000, by=12500) 47 | 48 | #PNG Device 49 | png( 50 | filename="bench.png", 51 | width = 895, height = 341, 52 | units = "px", pointsize = 15 53 | ) 54 | 55 | par(oma=c(3,3,3,3),mar=c(3,3,3,1),mfrow=c(1,3)) 56 | par(cex.axis=1.2) 57 | 58 | # Normal PBD 59 | plot( 60 | main="PBD default", 61 | x_axis, normal_pbd[[DO_WORK1]], 62 | type="l", lty=1, col="red", 63 | ylim=c(0, max(unlist(normal_pbd))), lwd=LINE_WIDTH 64 | ) 65 | lines(x_axis, normal_pbd[[DO_WORK2]], col="blue", lwd=LINE_WIDTH, lty=1) 66 | lines(x_axis, normal_pbd[[DO_WORK3]], col="orange", lwd=LINE_WIDTH, lty=1) 67 | grid(NULL, NULL) 68 | legend("topleft", legend=c("work1", "work2", "work3"), 69 | col=c("red", "blue", "orange"), pch=15, bty="o", horiz=TRUE, cex=1.15) 70 | 71 | # Static 72 | plot( 73 | main="PBD w/ static analysis", 74 | x_axis, static_pbd[[DO_WORK1]], 75 | type="l", lty=1, col="red", 76 | ylim=c(0, max(unlist(normal_pbd))), lwd=LINE_WIDTH 77 | ) 78 | lines(x_axis, static_pbd[[DO_WORK2]], col="blue", lwd=LINE_WIDTH, lty=1) 79 | lines(x_axis, static_pbd[[DO_WORK3]], col="orange", lwd=LINE_WIDTH, lty=1) 80 | grid(NULL, NULL) 81 | 82 | # GDB 83 | plot( 84 | main="GDB", 85 | x_axis, gdb[[DO_WORK1]], 86 | type="l", lty=1, col="red", 87 | ylim=c(0, max(unlist(gdb))), lwd=LINE_WIDTH, pch=0 88 | ) 89 | lines(x_axis, gdb[[DO_WORK2]], col="blue", lwd=LINE_WIDTH, lty=1) 90 | lines(x_axis, gdb[[DO_WORK3]], col="orange", lwd=LINE_WIDTH, lty=1) 91 | grid(NULL, NULL) 92 | 93 | mtext(text="Time per iteration (PBD vs GDB)",side=3,line=0,outer=TRUE,font=2) 94 | mtext(text="Number of iterations",side=1,line=0,outer=TRUE) 95 | mtext(text="Time (seconds)",side=2,line=0,outer=TRUE) 96 | 97 | #Device off 98 | dev.off() 99 | -------------------------------------------------------------------------------- /src/benchs/run-benchs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # MIT License 5 | # 6 | # Copyright (c) 2019-2020 Davidson Francis 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in all 16 | # copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | # 26 | if [ ! -x "$(command -v gdb)" ] 27 | then 28 | echo "GDB not found! GDB and Rscript is necessary in order to" 29 | echo "run the benchmarks." 30 | exit 1 31 | fi 32 | 33 | if [ ! -x "$(command -v Rscript)" ] 34 | then 35 | echo "Rscript not found! GDB and Rscript is necessary in order to" 36 | echo "run the benchmarks." 37 | exit 1 38 | fi 39 | 40 | vars=$(../pbd ./bench do_work1 -d | grep "Variable found" | cut -d':' -f2 | tr -d ' ') 41 | IFS=$' 42 | ' 43 | unset IFS 44 | vars=($vars) 45 | 46 | # Create the GDB script file 47 | { 48 | echo "set can-use-hw-watchpoints 0" 49 | echo "set confirm off" 50 | } > gdb_script 51 | 52 | for var in "${vars[@]}" 53 | do 54 | { 55 | echo "watch -l $var" 56 | echo "commands" 57 | echo "c" 58 | echo "end" 59 | } >> gdb_script 60 | done 61 | echo "r" >> gdb_script 62 | 63 | # Setup environment 64 | increment=12500 65 | size=$increment 66 | 67 | # ------------------------ 68 | # PBD work 69 | # Arguments: 70 | # $1 = work number 71 | # $2 = size 72 | # $3 = optional arguments 73 | # ------------------------ 74 | function pbd_work() 75 | { 76 | start_time="$(date -u +%s.%N)" 77 | ../pbd ./bench do_work"$1" "$size" "$1" "$3" &>/dev/null 78 | end_time="$(date -u +%s.%N)" 79 | elapsed_pbd="$(bc <<<"$end_time-$start_time" | awk '{printf "%f", $0}')" 80 | echo "$elapsed_pbd" 81 | } 82 | 83 | # -------------------- 84 | # GDB work 85 | # Arguments 86 | # $1 = work number 87 | # $2 = size 88 | # -------------------- 89 | function gdb_work() 90 | { 91 | start_time="$(date -u +%s.%N)" 92 | gdb -batch -x gdb_script --args ./bench "$size" "$1" &>/dev/null 93 | end_time="$(date -u +%s.%N)" 94 | elapsed_gdb="$(bc <<<"$end_time-$start_time" | awk '{printf "%f", $0}')" 95 | echo "$elapsed_gdb" 96 | } 97 | 98 | # Prepare CSV 99 | echo "size, normal_pbd, static_pbd, gdb" > csv_times 100 | echo -e "\nBeware, this may take up to 3 hours to finish..." 101 | 102 | for (( work=1; work<4; work++ )) 103 | do 104 | echo "> do_work$work..." 105 | 106 | for (( step=1; step<9; step++ )) 107 | do 108 | size=$((step*12500)) 109 | 110 | # Normal PBD 111 | echo " > PBD default...." 112 | time_pbd="$(pbd_work "$work" $size "")" 113 | 114 | # With static analysis PBD 115 | echo " > PBD static...." 116 | time_static="$(pbd_work "$work" $size "-S")" 117 | 118 | # GDB 119 | echo " > GDB...." 120 | time_gdb="$(gdb_work "$work" $size)" 121 | 122 | echo " Times: (size=$size), PBD: $time_pbd / PBD Static Analysis: $time_static / GDB: $time_gdb" 123 | echo "$size, $time_pbd, $time_static, $time_gdb" >> csv_times 124 | echo "" 125 | done 126 | done 127 | 128 | # Plot graph =) 129 | echo "Plotting graph..." 130 | Rscript plot.R &> /dev/null 131 | -------------------------------------------------------------------------------- /src/breakpoint.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "breakpoint.h" 26 | #include "dwarf_helper.h" 27 | 28 | /** 29 | * @brief Creates a breakpoint list by the given @P lines array 30 | * and the @p pid child process. 31 | * 32 | * @param lines Lines array. 33 | * @param pid Child process. 34 | * 35 | * @return Returns a breakpoint list. 36 | */ 37 | struct hashtable *bp_createlist(struct array *lines) 38 | { 39 | struct hashtable *breakpoints; /* Breakpoints list. */ 40 | struct breakpoint *bp; /* Breakpoint. */ 41 | 42 | /* Initialize our list. */ 43 | hashtable_init(&breakpoints, NULL); 44 | 45 | for (int i = 0; i < (int) array_size(&lines); i++) 46 | { 47 | struct dw_line *l; 48 | l = array_get(&lines, i, NULL); 49 | 50 | if (l->line_type != LBEGIN_STMT) 51 | continue; 52 | 53 | /* Allocates a new break point. */ 54 | bp = malloc(sizeof(struct breakpoint)); 55 | bp->addr = l->addr; 56 | bp->original_byte = 0; 57 | bp->line_no = l->line_no; 58 | 59 | /* Add to our hashmap. */ 60 | hashtable_add(&breakpoints, (void*)bp->addr, bp); 61 | } 62 | 63 | return (breakpoints); 64 | } 65 | 66 | /** 67 | * Create and adds a breakpoint given the address, the breakpoint list 68 | * and the child process into the breakpoint list. 69 | * @param addr Target breakpoint address. 70 | * @param bp Breakpoint list. 71 | * @param child Child process. 72 | */ 73 | int bp_createbreakpoint(uintptr_t addr, struct hashtable *bp, pid_t child) 74 | { 75 | struct breakpoint *b; /* Breakpoint. */ 76 | 77 | /* Check if already exists. */ 78 | if (bp_findbreakpoint(addr, bp) != NULL) 79 | return (-1); 80 | 81 | /* Allocate our new breakpoint. */ 82 | b = malloc(sizeof(struct breakpoint)); 83 | b->addr = addr; 84 | b->original_byte = pt_readmemory_long(child, b->addr) & 0xFF; 85 | b->line_no = 0; 86 | 87 | /* Add to our list. */ 88 | hashtable_add(&bp, (void*)b->addr, b); 89 | 90 | /* Insert breakpoint. */ 91 | bp_insertbreakpoint(b, child); 92 | 93 | return (0); 94 | } 95 | 96 | /** 97 | * Insert a breakpoint into a given address. 98 | * @param bp Breakpoint to be added. 99 | * @param child Child process. 100 | * @return Returns 0 if success and a negative number 101 | * otherwise. 102 | */ 103 | int bp_insertbreakpoint(struct breakpoint *bp, pid_t child) 104 | { 105 | long insn; /* Address opcode. */ 106 | 107 | if (bp->addr == 0) 108 | return (-1); 109 | 110 | insn = pt_readmemory_long(child, bp->addr); 111 | insn = (insn & ~0xFF) | BP_OPCODE; 112 | pt_writememory_long(child, bp->addr, insn); 113 | 114 | return (0); 115 | } 116 | 117 | /** 118 | * Insert breakpoints into the child process memory. 119 | * 120 | * @param bp Breakpoints list. 121 | * @param child Child process. 122 | * 123 | * @return Returns 0 if success and a negative number 124 | * otherwise. 125 | * 126 | * @note This function should not be confused with his 127 | * sister bp_insertbreakpoint() (singular) that adds 128 | * a single breakpoint into memory. 129 | */ 130 | int bp_insertbreakpoints(struct hashtable *bp, pid_t child) 131 | { 132 | struct breakpoint *b_k; /* Current breakpoint key. */ 133 | struct breakpoint *b_v; /* Current breakpoint value. */ 134 | ((void)b_k); 135 | 136 | /* If invalid. */ 137 | if (bp == NULL) 138 | return (-1); 139 | 140 | HASHTABLE_FOREACH(bp, b_k, b_v, 141 | { 142 | b_v->original_byte = pt_readmemory_long(child, b_v->addr) & 0xFF; 143 | if (bp_insertbreakpoint(b_v, child)) 144 | return (-1); 145 | }); 146 | 147 | return (0); 148 | } 149 | 150 | /** 151 | * @brief By a given address @p addr, tries to find a breakpoint 152 | * into the breakpoint list @p bp_list. 153 | * 154 | * @param addr Address that will be searched. 155 | * @param bp_list Target breakpoint list. 156 | * 157 | * @return Returns the underlying breakpoint structure if found, 158 | * or NULL otherwise. 159 | */ 160 | struct breakpoint *bp_findbreakpoint( 161 | uintptr_t addr, struct hashtable *bp_list) 162 | { 163 | struct breakpoint *b; 164 | b = hashtable_get(&bp_list, (void*)addr); 165 | return (b); 166 | } 167 | 168 | /** 169 | * @brief If the child process if stopped inside a breakpoint, 170 | * executes the original instruction and enables to breakpoint 171 | * again. 172 | * 173 | * @param bp_list Breakpoint list. 174 | * @param child Child process. 175 | */ 176 | void bp_skipbreakpoint(struct breakpoint *bp, pid_t child) 177 | { 178 | long insn; 179 | 180 | /* If a valid breakpoint, restart instruction. */ 181 | pt_setregister_pc(child, bp->addr); 182 | 183 | /* Insert the original instruction. */ 184 | insn = pt_readmemory_long(child, bp->addr); 185 | insn = (insn & ~0xFF) | bp->original_byte; 186 | pt_writememory_long(child, bp->addr, insn); 187 | 188 | /* Execute and wait. */ 189 | pt_continue_single_step(child); 190 | pt_waitchild(); 191 | 192 | /* Enables the breakpoint again. */ 193 | insn = (insn & ~0xFF) | BP_OPCODE; 194 | pt_writememory_long(child, bp->addr, insn); 195 | } 196 | 197 | /** 198 | * @brief Deallocates all the breakpoints remaining. 199 | * 200 | * @param breakpoints Breakpoints list. 201 | */ 202 | void bp_list_free(struct hashtable *breakpoints) 203 | { 204 | hashtable_finish(&breakpoints, 1); 205 | } 206 | -------------------------------------------------------------------------------- /src/function.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "function.h" 26 | #include "variable.h" 27 | #include "pbd.h" 28 | #include 29 | #include 30 | #include 31 | 32 | /* Static buffer for indent level. */ 33 | static char fn_indent_buff[64 + 1] = {0}; 34 | 35 | /** 36 | * @brief Returns a constant indented string for the 37 | * current indentation level. 38 | * 39 | * @param depth Current indentation level. 40 | * 41 | * @return Returns a constant string indented by 42 | * FUNCTION_INDENT_LEVEL times. 43 | * 44 | * @note Its important to note that for 64 characters (or 16 45 | * levels by using the default FUNCTION_INDENT_LEVEL size, i.e: 4) 46 | * buffer is already pre-allocated, thus, reducing the overhead 47 | * of memory allocation. 48 | * 49 | * For more than 64 characters, fn_get_indent() will allocate a buffer 50 | * with enough room; in this case, its up to the caller to free the 51 | * memory by calling fn_free_indent() which will adequately free 52 | * the memory if thats the case. 53 | */ 54 | char *fn_get_indent(size_t depth) 55 | { 56 | char *buff; 57 | 58 | /* Sanity checking. */ 59 | if (depth < 1) 60 | return (NULL); 61 | 62 | /* Pre-allocated buffer. */ 63 | if (depth < (sizeof(fn_indent_buff) - 1) / FUNCTION_INDENT_LEVEL) 64 | { 65 | depth--; 66 | memset(fn_indent_buff, ' ', depth * FUNCTION_INDENT_LEVEL); 67 | fn_indent_buff[depth * FUNCTION_INDENT_LEVEL] = '\0'; 68 | return (fn_indent_buff); 69 | } 70 | 71 | /* Allocate a new buffer. */ 72 | else 73 | { 74 | depth--; 75 | buff = calloc(1, sizeof(char) * (depth * FUNCTION_INDENT_LEVEL + 1)); 76 | memset(buff, ' ', depth * FUNCTION_INDENT_LEVEL); 77 | buff[depth * FUNCTION_INDENT_LEVEL] = '\0'; 78 | return (buff); 79 | } 80 | } 81 | 82 | /** 83 | * @brief Prints a formatted string with already indentation. 84 | * 85 | * This function is recommended whenever a formatted print 86 | * with indentation is required, since fn_printf() also 87 | * deallocates the buffer provided by fn_get_indent(). 88 | * 89 | * @param depth Function depth. 90 | * @param extra_space Extra space if needed. 91 | * @param fmt Formatted string to be printed. 92 | * 93 | * @return Returns the number of characters printed from 94 | * the formatted string. 95 | */ 96 | int fn_printf(size_t depth, int extra_space, const char* fmt, ...) 97 | { 98 | char *buffer; /* Buffer. */ 99 | va_list args; /* Arguments. */ 100 | 101 | /* Indent level. */ 102 | fputs( (buffer = fn_get_indent(depth)), pbd_output ); 103 | 104 | /* Extra space. */ 105 | fprintf(pbd_output, "%*s", extra_space, ""); 106 | 107 | /* Print formatted string. */ 108 | va_start(args, fmt); 109 | vfprintf(pbd_output, fmt, args); 110 | va_end(args); 111 | 112 | /* Free buffer. */ 113 | fn_free_indent(buffer); 114 | return (0); 115 | } 116 | 117 | /** 118 | * @brief Free the memory alocated by the fn_get_indent() 119 | * regardless of what buffer was used. 120 | * 121 | * @param buff Buffer to be freed. 122 | */ 123 | void fn_free_indent(char *buff) 124 | { 125 | if (fn_indent_buff >= buff && 126 | buff < fn_indent_buff+sizeof(fn_indent_buff)) 127 | return; 128 | 129 | free(buff); 130 | } 131 | 132 | /** 133 | * @brief Deallocates the remaining variables and the 134 | * last function context. 135 | * 136 | * @param f Function context. 137 | */ 138 | void fn_free(struct function *f) 139 | { 140 | if (f == NULL) 141 | return; 142 | 143 | var_array_free(f->vars); 144 | free(f); 145 | } 146 | -------------------------------------------------------------------------------- /src/include/analysis.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef ANALYSIS_H 26 | #define ANALYSIS_H 27 | 28 | #include 29 | #include 30 | #include "dissect.h" 31 | #include "scope.h" 32 | #include "ptrlist.h" 33 | #include "breakpoint.h" 34 | #include "dwarf_helper.h" 35 | 36 | /* 37 | * Arguments structure. 38 | * 39 | */ 40 | struct analysis_struct 41 | { 42 | struct array *args; 43 | }; 44 | 45 | extern int static_analysis_add_arg( 46 | const char *arg1, 47 | const char *arg2); 48 | 49 | extern int static_analysis_init(void); 50 | extern void static_analysis_finish(void); 51 | 52 | extern struct hashtable *static_analysis( 53 | const char *file, 54 | const char *func, 55 | struct array *lines, 56 | uintptr_t firstbreak); 57 | 58 | 59 | #endif /* ANALYSIS_H */ 60 | -------------------------------------------------------------------------------- /src/include/array.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef ARRAY_UTILS_H 26 | #define ARRAY_UTILS_H 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | /** 33 | * Array initial size. 34 | */ 35 | #define ARRAY_UTILS_DEFAULT_SIZE 16 36 | 37 | /** 38 | * Array structure. 39 | */ 40 | struct array 41 | { 42 | void **buf; 43 | size_t capacity; 44 | size_t elements; 45 | }; 46 | 47 | /* External functions. */ 48 | extern int array_init(struct array **array); 49 | extern int array_finish(struct array **array); 50 | extern int array_add(struct array **ar, void *e); 51 | extern void* array_remove(struct array **ar, size_t pos, void **e); 52 | extern void *array_remove_last(struct array **ar, void **e); 53 | extern void* array_get(struct array **array, size_t pos, void **e); 54 | extern void *array_get_last(struct array **array, void **e); 55 | extern size_t array_size(struct array **array); 56 | extern int array_sort(struct array **ar, 57 | int (*cmp)(const void*,const void*)); 58 | extern int array_selftest(void); 59 | 60 | #endif /* ARRAY_UTILS_H */ 61 | -------------------------------------------------------------------------------- /src/include/breakpoint.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef BREAKPOINT_H 26 | #define BREAKPOINT_H 27 | 28 | #include "ptrace.h" 29 | #include "hashtable.h" 30 | #include "array.h" 31 | #include 32 | #include 33 | #include 34 | 35 | /* 36 | * Break point opcode 37 | * Note that this is highly architecture 38 | * dependent.. think a generic way to do 39 | * this. 40 | */ 41 | #define BP_OPCODE 0xCC 42 | 43 | /** 44 | * @brief Breakpoint structure. 45 | */ 46 | struct breakpoint 47 | { 48 | uintptr_t addr; 49 | uint8_t original_byte; 50 | unsigned line_no; 51 | }; 52 | 53 | extern struct hashtable *bp_createlist(struct array *lines); 54 | 55 | extern int bp_createbreakpoint(uintptr_t addr, struct hashtable *bp, pid_t child); 56 | 57 | extern int bp_insertbreakpoint(struct breakpoint *bp, pid_t child); 58 | 59 | extern int bp_insertbreakpoints(struct hashtable *bp, pid_t child); 60 | 61 | extern struct breakpoint *bp_findbreakpoint(uintptr_t addr, 62 | struct hashtable *bp_list); 63 | 64 | extern void bp_skipbreakpoint(struct breakpoint *bp, pid_t child); 65 | 66 | extern void bp_list_free(struct hashtable *breakpoints); 67 | 68 | #endif /* BREAKPOINT_H */ 69 | -------------------------------------------------------------------------------- /src/include/dwarf_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef DWARF_UTILS_H 26 | #define DWARF_UTILS_H 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "array.h" 42 | 43 | /* Variables. */ 44 | #define VLOCAL 0x1 45 | #define VGLOBAL 0x2 46 | 47 | /* Variable type. */ 48 | #define TBASE_TYPE 0x1 49 | #define TARRAY 0x2 50 | #define TSTRUCTURE 0x4 51 | #define TUNION 0x8 52 | #define TENUM 0x10 53 | #define TPOINTER 0x20 54 | 55 | /* Line types. */ 56 | #define LBEGIN_STMT 0x1 57 | #define LEND_SEQ 0x2 58 | #define LBLOCK 0x4 59 | 60 | /* Encoding. */ 61 | #define ENC_UNKNOWN 0x1 62 | #define ENC_SIGNED 0x2 63 | #define ENC_UNSIGNED 0x4 64 | #define ENC_FLOAT 0x10 65 | #define ENC_POINTER 0x20 66 | 67 | /* 68 | * Who uses more than 8 dimensions? more than enough, 69 | * right? 70 | */ 71 | #define MATRIX_MAX_DIMENSIONS 8 72 | 73 | /** 74 | * Function structure. 75 | */ 76 | struct dw_function 77 | { 78 | /* 79 | * Points to the first byte of memory containing 80 | * the function. 81 | */ 82 | uintptr_t low_pc; 83 | 84 | /* 85 | * Points to the last by of memory containing 86 | * the function. 87 | */ 88 | uintptr_t high_pc; 89 | 90 | /* 91 | * Base pointer offset. 92 | * 93 | * Since GCC and CLANG (and perhaps others compilers too) 94 | * uses different schemes to refer to the local variable 95 | * location, bp_offset contains the offset to be added 96 | * into the base pointer in order to get the right 97 | * location. 98 | * 99 | * Since CLANG (until now) do not use loclists (with -gdwarf-2) 100 | * bp_offset will always point to 0. 101 | */ 102 | int bp_offset; 103 | }; 104 | 105 | /** 106 | * Line structure. 107 | */ 108 | struct dw_line 109 | { 110 | /* Line address. */ 111 | uintptr_t addr; 112 | 113 | /* Line number. */ 114 | unsigned line_no; 115 | 116 | /* 117 | * Line type: 118 | * A line can be one (or more) of the three 119 | * types below: 120 | * - Begin statement (that's the one we want) 121 | * - End sequence 122 | * - Line block 123 | */ 124 | unsigned line_type; 125 | }; 126 | 127 | /** 128 | * Dwarf Utils structure. 129 | */ 130 | struct dw_utils 131 | { 132 | int initialized; 133 | int fd; 134 | Dwarf_Debug dbg; 135 | Dwarf_Die cu_die; 136 | Dwarf_Die fn_die; 137 | 138 | /* 139 | * dw_function structure 140 | */ 141 | struct dw_function dw_func; 142 | }; 143 | 144 | /** 145 | * Variable structure 146 | */ 147 | struct dw_variable 148 | { 149 | char *name; 150 | int scope; 151 | 152 | /* 153 | * For TBASE_TYPE variables, the u64_value[2] 154 | * is more than enough to hold the type. If 155 | * not TBASE_TYPE, p_value should be used to 156 | * hold a pointer to the current location. 157 | */ 158 | union var_value 159 | { 160 | /* Arrays of u8 and u64 facilities. */ 161 | uint8_t u8_value[16]; 162 | uint64_t u64_value[2]; 163 | 164 | /* 165 | * Floating point numbers looks like have 166 | * an proper formatting that does not works 167 | * by just casting, so we need these types here. 168 | */ 169 | long double ld_value; 170 | double d_value; 171 | float f_value; 172 | 173 | /* Array, structures... pointer. */ 174 | char *p_value; 175 | } value; 176 | 177 | /* 178 | * Right after the prologue, the variable is likely 179 | * to be not initialized yet, so the scratch_value 180 | * holds the very first variable value read and uses 181 | * it to compare at each variable check. 182 | * 183 | * If the current value differs from scratch value, it 184 | * means that the variable was set for the first time 185 | * and thus, PBD can properly show a comprehensible 186 | * output, instead of trash data. 187 | * 188 | * Yeah, it's kinda waste of memory, spending ~16 bytes 189 | * just to properly initialize the variable, if anyone 190 | * have an a better idea.... 191 | */ 192 | union var_value scratch_value; 193 | 194 | /* 195 | * Flag indicating that the variable has been initialized. 196 | */ 197 | int initialized; 198 | 199 | /* 200 | * If the variable is global or static, 201 | * the address should be used, if local, 202 | * fp_offset. 203 | */ 204 | union location 205 | { 206 | off_t fp_offset; 207 | uintptr_t address; 208 | } location; 209 | 210 | size_t byte_size; 211 | 212 | struct vtype 213 | { 214 | int var_type; 215 | int encoding; 216 | 217 | /* 218 | * Not all variables are arrays, but when so, 219 | * we need some space to it, right?. 220 | * 221 | * Kinda waste of space here, but... 222 | */ 223 | struct arrayt 224 | { 225 | int var_type; 226 | size_t size_per_element; 227 | int dimensions; 228 | int elements_per_dimension[MATRIX_MAX_DIMENSIONS]; 229 | } array; 230 | } type; 231 | }; 232 | 233 | extern int *dw_init(const char *file, struct dw_utils *dw); 234 | 235 | extern void dw_finish(struct dw_utils *dw); 236 | 237 | extern int dw_next_cu_die(struct dw_utils *dw, Dwarf_Die *die); 238 | 239 | extern int dw_get_address_by_function(struct dw_utils *dw, const char *func); 240 | 241 | extern struct array *dw_get_all_variables(struct dw_utils *dw); 242 | 243 | extern struct array *dw_get_all_lines(struct dw_utils *dw); 244 | 245 | extern char *dw_get_source_file(struct dw_utils *dw); 246 | 247 | extern void dw_lines_dump(struct array *lines); 248 | 249 | extern int dw_is_c_language(struct dw_utils *dw); 250 | 251 | extern void dw_lines_array_free(struct array *lines); 252 | 253 | #endif /* DWARF_UTILS_H */ 254 | -------------------------------------------------------------------------------- /src/include/function.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef FUNCTION_H 26 | #define FUNCTION_H 27 | 28 | #include 29 | #include 30 | 31 | /** 32 | * @brief Current function context. 33 | */ 34 | struct function 35 | { 36 | /* All variables found. */ 37 | struct array *vars; 38 | 39 | /* Return address. */ 40 | uintptr_t return_addr; 41 | }; 42 | 43 | /* Amount of spaces to indent. */ 44 | #define FUNCTION_INDENT_LEVEL 4 45 | 46 | extern char *fn_get_indent(size_t depth); 47 | 48 | extern int fn_printf(size_t depth, int extra_space, const char* fmt, ...); 49 | 50 | extern void fn_free_indent(char *buff); 51 | 52 | extern void fn_free(struct function *f); 53 | 54 | #endif /* BREAKPOINT_H */ 55 | -------------------------------------------------------------------------------- /src/include/hashtable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef HASHTABLE_H 26 | #define HASHTABLE_H 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | /** 33 | * @brief Hashtable initial size. 34 | */ 35 | #define HASHTABLE_DEFAULT_SIZE 16 36 | 37 | /** 38 | * @brief Enable additional debug data, like collision counting. 39 | */ 40 | #define HASHTABLE_DEBUG 1 41 | 42 | /** 43 | * Hashtable foreach. 44 | * 45 | * @note This is 'unguarded commas-proof', because we're using varargs 46 | * here and all the arguments will be expanded as expected =). 47 | * 48 | * More info here: https://mort.coffee/home/obscure-c-features/ 49 | */ 50 | #define HASHTABLE_FOREACH(hashtable, key_424242, value_424242, ...)\ 51 | {\ 52 | struct hashtable_iter iter_424242; \ 53 | struct list *list_424242; \ 54 | hashtable_iter_init(&hashtable, &iter_424242); \ 55 | while (hashtable_iter_next(&iter_424242, &list_424242) != -1) \ 56 | {\ 57 | key_424242 = list_424242->key; \ 58 | value_424242 = list_424242->value; \ 59 | __VA_ARGS__ \ 60 | }\ 61 | } 62 | 63 | /** 64 | * @brief Hashtable linked list structure. 65 | */ 66 | struct list 67 | { 68 | void *key; /* Entry key. */ 69 | void *value; /* Entry value. */ 70 | uint64_t hash; /* Entry hash. */ 71 | struct list *next; /* Entry next list. */ 72 | }; 73 | 74 | /** 75 | * @brief Hashtable structure. 76 | */ 77 | struct hashtable 78 | { 79 | struct list **bucket; /* Bucket list. */ 80 | size_t capacity; /* Current capacity. */ 81 | size_t elements; /* Current elements. */ 82 | ssize_t key_size; /* Hash key size. */ 83 | 84 | #if HASHTABLE_DEBUG 85 | size_t collisions; /* Collisions count. */ 86 | #endif 87 | 88 | /* Function pointers. */ 89 | uint64_t (*hash) (const void *key, size_t size); 90 | int (*cmp) (const void *key1, const void *key2); 91 | }; 92 | 93 | /** 94 | * @brief Hashtable iterator 95 | */ 96 | struct hashtable_iter 97 | { 98 | struct hashtable *ht; 99 | size_t bucket_index; 100 | struct list *next; 101 | }; 102 | 103 | /* ==================== External functions ==================== */ 104 | extern int hashtable_init( 105 | struct hashtable **ht, 106 | void (*setup_algorithm)(struct hashtable **ht) 107 | ); 108 | extern int hashtable_iter_init( 109 | struct hashtable **h, 110 | struct hashtable_iter *it 111 | ); 112 | extern int hashtable_iter_next(struct hashtable_iter *it, struct list **li); 113 | extern int hashtable_finish(struct hashtable **ht, int dealloc); 114 | extern int hashtable_add(struct hashtable **ht, void *key, void *value); 115 | extern void* hashtable_get(struct hashtable **ht, void *key); 116 | extern void hashtable_print_stats(struct hashtable **ht); 117 | 118 | /* ==================== Hash functions ==================== */ 119 | extern int hashtable_cmp_ptr(const void *key1, const void *key2); 120 | 121 | /* sdbm. */ 122 | extern void hashtable_sdbm_setup(struct hashtable **ht); 123 | extern uint64_t hashtable_sdbm(const void *key, size_t size); 124 | 125 | /* Splitmix64. */ 126 | extern void hashtable_splitmix64_setup(struct hashtable **ht); 127 | extern uint64_t hashtable_splitmix64_hash(const void *key, size_t size); 128 | 129 | /* MurMur3 Hash. */ 130 | extern void hashtable_MurMur3_setup(struct hashtable **ht); 131 | extern uint64_t hashtable_MurMur3_hash(const void *key, size_t size); 132 | 133 | #endif /* HASHTABLE_H */ 134 | -------------------------------------------------------------------------------- /src/include/highlight.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef HIGHLIGHT_H 26 | #define HIGHLIGHT_H 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "hashtable.h" 34 | 35 | /* Highlighted line. */ 36 | struct highlighted_line 37 | { 38 | size_t idx; 39 | size_t size; 40 | }; 41 | 42 | /** 43 | * Allocates a new Highlighted Buffer line. 44 | * 45 | * A Highlighted Buffer Line is a facility that transparently allows 46 | * the user to append chars and strings inside a buffer without having 47 | * to worry about space, reallocs and so on, something similiar to 48 | * SDS strings. 49 | * 50 | * @returns Return a pointer to the highlighted line. 51 | */ 52 | extern char* highlight_alloc_line(void); 53 | 54 | /** 55 | * Deallocate a Highlighted Line Buffer. 56 | * 57 | * @param line Highlighted Line Buffer to be deallocated. 58 | */ 59 | extern void highlight_free(char *line); 60 | 61 | /** 62 | * For a given line @p line and a (already) allocated 63 | * highlighted line buffer @p hl, highlights the 64 | * line and returns @p hl with the highlighted line. 65 | * 66 | * @param line Line (null terminated string) to be highlighted. 67 | * @param hl Pre-allocated Highlighted Line buffer. 68 | * 69 | * @return Returns a Highlighted Line Buffer. 70 | */ 71 | extern char *highlight_line(const char *line, char *hl); 72 | 73 | /** 74 | * Initialize the syntax highlight engine. 75 | * 76 | * @param theme_file Theme file, if NULL, will use 77 | * the internal theme. 78 | * 79 | * @return Returns 0 if success and 1 otherwise. 80 | */ 81 | extern int highlight_init(const char *theme_file); 82 | 83 | /** 84 | * Finishes the highlight 'engine'. 85 | */ 86 | extern void highlight_finish(void); 87 | 88 | #endif /* HIGHLIGHT_H */ 89 | -------------------------------------------------------------------------------- /src/include/line.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef LINE_H 26 | #define LINE_H 27 | 28 | #define _POSIX_C_SOURCE 200809L 29 | #define _XOPEN_SOURCE 700 30 | 31 | #if defined(__GLIBC__) && \ 32 | (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 10) ) 33 | 34 | #define _GNU_SOURCE 35 | #endif 36 | 37 | #include "dwarf_helper.h" 38 | #include "variable.h" 39 | #include 40 | 41 | /* Output buffer size. */ 42 | #define BS 64 43 | 44 | extern void (*line_output)( 45 | int depth, unsigned line_no, 46 | struct dw_variable *v, union var_value *v_before, 47 | union var_value *v_after, int *array_idxs); 48 | 49 | extern int line_cmp(const void *a, const void *b); 50 | 51 | extern int line_read_source(const char *filename, int highlight, 52 | char *theme_file); 53 | 54 | extern void line_free_source(int highlight); 55 | 56 | extern void line_null_printer(int depth, unsigned line_no, 57 | struct dw_variable *v, union var_value *v_before, 58 | union var_value *v_after, int *array_idxs); 59 | 60 | extern void line_default_printer(int depth, unsigned line_no, 61 | struct dw_variable *v, union var_value *v_before, 62 | union var_value *v_after, int *array_idxs); 63 | 64 | extern void line_detailed_printer(int depth, unsigned line_no, 65 | struct dw_variable *v, union var_value *v_before, 66 | union var_value *v_after, int *array_idxs); 67 | 68 | #endif /* LINE_H */ 69 | -------------------------------------------------------------------------------- /src/include/optparse.h: -------------------------------------------------------------------------------- 1 | /* Optparse --- portable, reentrant, embeddable, getopt-like option parser 2 | * 3 | * This is free and unencumbered software released into the public domain. 4 | * 5 | * To get the implementation, define OPTPARSE_IMPLEMENTATION. 6 | * Optionally define OPTPARSE_API to control the API's visibility 7 | * and/or linkage (static, __attribute__, __declspec). 8 | * 9 | * The POSIX getopt() option parser has three fatal flaws. These flaws 10 | * are solved by Optparse. 11 | * 12 | * 1) Parser state is stored entirely in global variables, some of 13 | * which are static and inaccessible. This means only one thread can 14 | * use getopt(). It also means it's not possible to recursively parse 15 | * nested sub-arguments while in the middle of argument parsing. 16 | * Optparse fixes this by storing all state on a local struct. 17 | * 18 | * 2) The POSIX standard provides no way to properly reset the parser. 19 | * This means for portable code that getopt() is only good for one 20 | * run, over one argv with one option string. It also means subcommand 21 | * options cannot be processed with getopt(). Most implementations 22 | * provide a method to reset the parser, but it's not portable. 23 | * Optparse provides an optparse_arg() function for stepping over 24 | * subcommands and continuing parsing of options with another option 25 | * string. The Optparse struct itself can be passed around to 26 | * subcommand handlers for additional subcommand option parsing. A 27 | * full reset can be achieved by with an additional optparse_init(). 28 | * 29 | * 3) Error messages are printed to stderr. This can be disabled with 30 | * opterr, but the messages themselves are still inaccessible. 31 | * Optparse solves this by writing an error message in its errmsg 32 | * field. The downside to Optparse is that this error message will 33 | * always be in English rather than the current locale. 34 | * 35 | * Optparse should be familiar with anyone accustomed to getopt(), and 36 | * it could be a nearly drop-in replacement. The option string is the 37 | * same and the fields have the same names as the getopt() global 38 | * variables (optarg, optind, optopt). 39 | * 40 | * Optparse also supports GNU-style long options with optparse_long(). 41 | * The interface is slightly different and simpler than getopt_long(). 42 | * 43 | * By default, argv is permuted as it is parsed, moving non-option 44 | * arguments to the end. This can be disabled by setting the `permute` 45 | * field to 0 after initialization. 46 | */ 47 | #ifndef OPTPARSE_H 48 | #define OPTPARSE_H 49 | 50 | #ifndef OPTPARSE_API 51 | # define OPTPARSE_API 52 | #endif 53 | 54 | struct optparse { 55 | char **argv; 56 | int permute; 57 | int optind; 58 | int optopt; 59 | char *optarg; 60 | char errmsg[64]; 61 | int subopt; 62 | }; 63 | 64 | enum optparse_argtype { 65 | OPTPARSE_NONE, 66 | OPTPARSE_REQUIRED, 67 | OPTPARSE_OPTIONAL 68 | }; 69 | 70 | struct optparse_long { 71 | const char *longname; 72 | int shortname; 73 | enum optparse_argtype argtype; 74 | }; 75 | 76 | /** 77 | * Initializes the parser state. 78 | */ 79 | OPTPARSE_API 80 | void optparse_init(struct optparse *options, char **argv); 81 | 82 | /** 83 | * Read the next option in the argv array. 84 | * @param optstring a getopt()-formatted option string. 85 | * @return the next option character, -1 for done, or '?' for error 86 | * 87 | * Just like getopt(), a character followed by no colons means no 88 | * argument. One colon means the option has a required argument. Two 89 | * colons means the option takes an optional argument. 90 | */ 91 | OPTPARSE_API 92 | int optparse(struct optparse *options, const char *optstring); 93 | 94 | /** 95 | * Handles GNU-style long options in addition to getopt() options. 96 | * This works a lot like GNU's getopt_long(). The last option in 97 | * longopts must be all zeros, marking the end of the array. The 98 | * longindex argument may be NULL. 99 | */ 100 | OPTPARSE_API 101 | int optparse_long(struct optparse *options, 102 | const struct optparse_long *longopts, 103 | int *longindex); 104 | 105 | /** 106 | * Used for stepping over non-option arguments. 107 | * @return the next non-option argument, or NULL for no more arguments 108 | * 109 | * Argument parsing can continue with optparse() after using this 110 | * function. That would be used to parse the options for the 111 | * subcommand returned by optparse_arg(). This function allows you to 112 | * ignore the value of optind. 113 | */ 114 | OPTPARSE_API 115 | char *optparse_arg(struct optparse *options); 116 | 117 | /* Implementation */ 118 | #ifdef OPTPARSE_IMPLEMENTATION 119 | 120 | #define OPTPARSE_MSG_INVALID "invalid option" 121 | #define OPTPARSE_MSG_MISSING "option requires an argument" 122 | #define OPTPARSE_MSG_TOOMANY "option takes no arguments" 123 | 124 | static int 125 | optparse_error(struct optparse *options, const char *msg, const char *data) 126 | { 127 | unsigned p = 0; 128 | const char *sep = " -- '"; 129 | while (*msg) 130 | options->errmsg[p++] = *msg++; 131 | while (*sep) 132 | options->errmsg[p++] = *sep++; 133 | while (p < sizeof(options->errmsg) - 2 && *data) 134 | options->errmsg[p++] = *data++; 135 | options->errmsg[p++] = '\''; 136 | options->errmsg[p++] = '\0'; 137 | return '?'; 138 | } 139 | 140 | OPTPARSE_API 141 | void 142 | optparse_init(struct optparse *options, char **argv) 143 | { 144 | options->argv = argv; 145 | options->permute = 1; 146 | options->optind = 1; 147 | options->subopt = 0; 148 | options->optarg = 0; 149 | options->errmsg[0] = '\0'; 150 | } 151 | 152 | static int 153 | optparse_is_dashdash(const char *arg) 154 | { 155 | return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0'; 156 | } 157 | 158 | static int 159 | optparse_is_shortopt(const char *arg) 160 | { 161 | return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0'; 162 | } 163 | 164 | static int 165 | optparse_is_longopt(const char *arg) 166 | { 167 | return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0'; 168 | } 169 | 170 | static void 171 | optparse_permute(struct optparse *options, int index) 172 | { 173 | char *nonoption = options->argv[index]; 174 | int i; 175 | for (i = index; i < options->optind - 1; i++) 176 | options->argv[i] = options->argv[i + 1]; 177 | options->argv[options->optind - 1] = nonoption; 178 | } 179 | 180 | static int 181 | optparse_argtype(const char *optstring, char c) 182 | { 183 | int count = OPTPARSE_NONE; 184 | if (c == ':') 185 | return -1; 186 | for (; *optstring && c != *optstring; optstring++); 187 | if (!*optstring) 188 | return -1; 189 | if (optstring[1] == ':') 190 | count += optstring[2] == ':' ? 2 : 1; 191 | return count; 192 | } 193 | 194 | OPTPARSE_API 195 | int 196 | optparse(struct optparse *options, const char *optstring) 197 | { 198 | int type; 199 | char *next; 200 | char *option = options->argv[options->optind]; 201 | options->errmsg[0] = '\0'; 202 | options->optopt = 0; 203 | options->optarg = 0; 204 | if (option == 0) { 205 | return -1; 206 | } else if (optparse_is_dashdash(option)) { 207 | options->optind++; /* consume "--" */ 208 | return -1; 209 | } else if (!optparse_is_shortopt(option)) { 210 | if (options->permute) { 211 | int index = options->optind++; 212 | int r = optparse(options, optstring); 213 | optparse_permute(options, index); 214 | options->optind--; 215 | return r; 216 | } else { 217 | return -1; 218 | } 219 | } 220 | option += options->subopt + 1; 221 | options->optopt = option[0]; 222 | type = optparse_argtype(optstring, option[0]); 223 | next = options->argv[options->optind + 1]; 224 | switch (type) { 225 | case -1: { 226 | char str[2] = {0, 0}; 227 | str[0] = option[0]; 228 | options->optind++; 229 | return optparse_error(options, OPTPARSE_MSG_INVALID, str); 230 | } 231 | case OPTPARSE_NONE: 232 | if (option[1]) { 233 | options->subopt++; 234 | } else { 235 | options->subopt = 0; 236 | options->optind++; 237 | } 238 | return option[0]; 239 | case OPTPARSE_REQUIRED: 240 | options->subopt = 0; 241 | options->optind++; 242 | if (option[1]) { 243 | options->optarg = option + 1; 244 | } else if (next != 0) { 245 | options->optarg = next; 246 | options->optind++; 247 | } else { 248 | char str[2] = {0, 0}; 249 | str[0] = option[0]; 250 | options->optarg = 0; 251 | return optparse_error(options, OPTPARSE_MSG_MISSING, str); 252 | } 253 | return option[0]; 254 | case OPTPARSE_OPTIONAL: 255 | options->subopt = 0; 256 | options->optind++; 257 | if (option[1]) 258 | options->optarg = option + 1; 259 | else 260 | options->optarg = 0; 261 | return option[0]; 262 | } 263 | return 0; 264 | } 265 | 266 | OPTPARSE_API 267 | char * 268 | optparse_arg(struct optparse *options) 269 | { 270 | char *option = options->argv[options->optind]; 271 | options->subopt = 0; 272 | if (option != 0) 273 | options->optind++; 274 | return option; 275 | } 276 | 277 | static int 278 | optparse_longopts_end(const struct optparse_long *longopts, int i) 279 | { 280 | return !longopts[i].longname && !longopts[i].shortname; 281 | } 282 | 283 | static void 284 | optparse_from_long(const struct optparse_long *longopts, char *optstring) 285 | { 286 | char *p = optstring; 287 | int i; 288 | for (i = 0; !optparse_longopts_end(longopts, i); i++) { 289 | if (longopts[i].shortname) { 290 | int a; 291 | *p++ = longopts[i].shortname; 292 | for (a = 0; a < (int)longopts[i].argtype; a++) 293 | *p++ = ':'; 294 | } 295 | } 296 | *p = '\0'; 297 | } 298 | 299 | /* Unlike strcmp(), handles options containing "=". */ 300 | static int 301 | optparse_longopts_match(const char *longname, const char *option) 302 | { 303 | const char *a = option, *n = longname; 304 | if (longname == 0) 305 | return 0; 306 | for (; *a && *n && *a != '='; a++, n++) 307 | if (*a != *n) 308 | return 0; 309 | return *n == '\0' && (*a == '\0' || *a == '='); 310 | } 311 | 312 | /* Return the part after "=", or NULL. */ 313 | static char * 314 | optparse_longopts_arg(char *option) 315 | { 316 | for (; *option && *option != '='; option++); 317 | if (*option == '=') 318 | return option + 1; 319 | else 320 | return 0; 321 | } 322 | 323 | static int 324 | optparse_long_fallback(struct optparse *options, 325 | const struct optparse_long *longopts, 326 | int *longindex) 327 | { 328 | int result; 329 | char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */ 330 | optparse_from_long(longopts, optstring); 331 | result = optparse(options, optstring); 332 | if (longindex != 0) { 333 | *longindex = -1; 334 | if (result != -1) { 335 | int i; 336 | for (i = 0; !optparse_longopts_end(longopts, i); i++) 337 | if (longopts[i].shortname == options->optopt) 338 | *longindex = i; 339 | } 340 | } 341 | return result; 342 | } 343 | 344 | OPTPARSE_API 345 | int 346 | optparse_long(struct optparse *options, 347 | const struct optparse_long *longopts, 348 | int *longindex) 349 | { 350 | int i; 351 | char *option = options->argv[options->optind]; 352 | if (option == 0) { 353 | return -1; 354 | } else if (optparse_is_dashdash(option)) { 355 | options->optind++; /* consume "--" */ 356 | return -1; 357 | } else if (optparse_is_shortopt(option)) { 358 | return optparse_long_fallback(options, longopts, longindex); 359 | } else if (!optparse_is_longopt(option)) { 360 | if (options->permute) { 361 | int index = options->optind++; 362 | int r = optparse_long(options, longopts, longindex); 363 | optparse_permute(options, index); 364 | options->optind--; 365 | return r; 366 | } else { 367 | return -1; 368 | } 369 | } 370 | 371 | /* Parse as long option. */ 372 | options->errmsg[0] = '\0'; 373 | options->optopt = 0; 374 | options->optarg = 0; 375 | option += 2; /* skip "--" */ 376 | options->optind++; 377 | for (i = 0; !optparse_longopts_end(longopts, i); i++) { 378 | const char *name = longopts[i].longname; 379 | if (optparse_longopts_match(name, option)) { 380 | char *arg; 381 | if (longindex) 382 | *longindex = i; 383 | options->optopt = longopts[i].shortname; 384 | arg = optparse_longopts_arg(option); 385 | if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) { 386 | return optparse_error(options, OPTPARSE_MSG_TOOMANY, name); 387 | } if (arg != 0) { 388 | options->optarg = arg; 389 | } else if (longopts[i].argtype == OPTPARSE_REQUIRED) { 390 | options->optarg = options->argv[options->optind]; 391 | if (options->optarg == 0) 392 | return optparse_error(options, OPTPARSE_MSG_MISSING, name); 393 | else 394 | options->optind++; 395 | } 396 | return options->optopt; 397 | } 398 | } 399 | return optparse_error(options, OPTPARSE_MSG_INVALID, option); 400 | } 401 | 402 | #endif /* OPTPARSE_IMPLEMENTATION */ 403 | #endif /* OPTPARSE_H */ 404 | -------------------------------------------------------------------------------- /src/include/pbd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef PBD_H 26 | #define PBD_H 27 | 28 | #include 29 | 30 | /* Current version. */ 31 | #define MAJOR_VERSION 0 32 | #define MINOR_VERSION 7 33 | #define RLSE_VERSION "" 34 | 35 | /* Arguments flags. */ 36 | #define FLG_SHOW_LINES 0x01 37 | #define FLG_ONLY_LOCALS 0x02 38 | #define FLG_ONLY_GLOBALS 0x04 39 | #define FLG_IGNR_LIST 0x08 40 | #define FLG_WATCH_LIST 0x10 41 | #define FLG_DUMP_ALL 0x20 42 | #define FLG_IGNR_EQSTAT 0x40 43 | #define FLG_SYNTAX_HIGHLIGHT 0x80 44 | #define FLG_STATIC_ANALYSIS 0x100 45 | #define FLG_SANALYSIS_SETSTD 0x200 46 | 47 | /* PBD default output file. */ 48 | extern FILE *pbd_output; 49 | 50 | /* Experimental features. 51 | * 52 | * If something goes wrong, the following experimental flags 53 | * can be disabled. 54 | */ 55 | /* none yet. */ 56 | 57 | /* Program arguments. */ 58 | struct args 59 | { 60 | uint16_t flags; 61 | int context; 62 | struct iw_list 63 | { 64 | char *list; 65 | struct hashtable *ht_list; 66 | } iw_list; 67 | char *executable; 68 | char *function; 69 | char *theme_file; 70 | char *output_file; 71 | char **argv; 72 | }; 73 | 74 | extern struct args args; 75 | extern void finish(void); 76 | extern void usage(int retcode, const char *prg_name); 77 | 78 | #endif /* PDB_H */ 79 | -------------------------------------------------------------------------------- /src/include/ptrace.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef PTRACE_H 26 | #define PTRACE_H 27 | 28 | #define _GNU_SOURCE 29 | #include 30 | #ifdef __GLIBC__ 31 | #include 32 | #endif 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | /* Child Exit Signal. */ 45 | #define PT_CHILD_EXIT 1 46 | 47 | extern int pt_spawnprocess(const char *file, char **argv); 48 | extern int pt_waitchild(void); 49 | extern int pt_continue(pid_t child); 50 | extern int pt_continue_single_step(pid_t child); 51 | extern uintptr_t pt_readregister_pc(pid_t child); 52 | extern void pt_setregister_pc(pid_t child, uintptr_t pc); 53 | extern uintptr_t pt_readregister_bp(pid_t child); 54 | extern uintptr_t pt_readreturn_address(pid_t child); 55 | extern char *pt_readmemory(pid_t child, uintptr_t addr, size_t len); 56 | extern void pt_writememory(pid_t child, uintptr_t addr, char *data, size_t len); 57 | extern long pt_readmemory_long(pid_t child, uintptr_t addr); 58 | extern void pt_writememory_long(pid_t child, uintptr_t addr, long data); 59 | extern uint64_t pt_readmemory64(pid_t child, uintptr_t addr); 60 | extern void pt_writememory64(pid_t child, uintptr_t addr, uint64_t data); 61 | 62 | #endif /* PTRACE_H */ 63 | -------------------------------------------------------------------------------- /src/include/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef UTIL_H 26 | #define UTIL_H 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "pbd.h" 33 | 34 | /** 35 | * Triggers a compile time error if the expression 36 | * evaluates to 0. 37 | */ 38 | #define COMPILE_TIME_ASSERT(expr) \ 39 | switch(0){case 0:case expr:;} 40 | 41 | /** 42 | * Aborts the execution with the appropriate context. 43 | */ 44 | #define QUIT(code, msg, ...) \ 45 | quit(code, __FILE__, __func__, __LINE__, msg, ##__VA_ARGS__); 46 | 47 | /** 48 | * Quits with a return code and a formatted string. 49 | * 50 | * @param code Error code to be returned. 51 | * @param file File name. 52 | * @param function Function name. 53 | * @param line Line number. 54 | * @param fmt Formatted string to be printed. 55 | */ 56 | static void quit(int code, const char *file, const char *function, 57 | int line, const char* fmt, ...) 58 | { 59 | va_list args; 60 | 61 | /* Indentify the context. */ 62 | fprintf(stderr, "[%s:%d] %s: ", file, line, function); 63 | 64 | /* Emmits error. */ 65 | va_start(args, fmt); 66 | vfprintf(stderr, fmt, args); 67 | va_end(args); 68 | 69 | /* Finish everything. */ 70 | finish(); 71 | 72 | exit(code); 73 | } 74 | 75 | #endif /* UTIL_H */ 76 | -------------------------------------------------------------------------------- /src/include/variable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef VARIABLE_H 26 | #define VARIABLE_H 27 | 28 | #include "array.h" 29 | #include "dwarf_helper.h" 30 | #include "breakpoint.h" 31 | #include "function.h" 32 | #include 33 | 34 | /* Offset memcmp pointer. */ 35 | extern int64_t (*offmemcmp)( 36 | void *src, void *dest, size_t block_size, size_t length); 37 | 38 | /* External declarations. */ 39 | extern int64_t offmemcmp_generic(void *v1, void *v2, 40 | size_t block_size, size_t n); 41 | 42 | extern void var_dump(struct array *vars); 43 | 44 | extern char *var_format_value(char *buffer, union var_value *v, 45 | int encoding, size_t byte_size); 46 | 47 | extern int var_new_context(struct function *prev_ctx, 48 | struct function **curr_ctx, struct array *ctx_list); 49 | 50 | extern int var_deallocate_context(struct array *vars, 51 | struct array *context, int depth); 52 | 53 | extern int var_read(union var_value *value, struct dw_variable *v, 54 | pid_t child); 55 | 56 | extern void var_initialize(struct array *vars, pid_t child); 57 | 58 | extern void var_check_changes(struct breakpoint *bp, struct array *vars, 59 | pid_t child, int depth); 60 | 61 | extern void var_array_free(struct array *vars); 62 | 63 | #endif /* VARIABLE_H */ 64 | -------------------------------------------------------------------------------- /src/line.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "line.h" 26 | #include "array.h" 27 | #include "highlight.h" 28 | #include "dwarf_helper.h" 29 | #include "pbd.h" 30 | #include 31 | #include 32 | #include 33 | 34 | /* Compile unit source code. */ 35 | struct array *source_lines = NULL; 36 | struct array *source_lines_highlighted = NULL; 37 | 38 | /* 39 | * Buffer 40 | * 41 | * This buffer holds the value from before and 42 | * after the value is changed for a single 43 | * TBASE_TYPE, TENUM and TPOINTER. 44 | */ 45 | static char before[BS]; 46 | static char after[BS]; 47 | 48 | /* Current function pointer. */ 49 | void (*line_output)( 50 | int depth, unsigned line_no, 51 | struct dw_variable *v, union var_value *v_before, 52 | union var_value *v_after, int *array_idxs) = line_default_printer; 53 | 54 | /* Base file name. */ 55 | char *base_file_name = NULL; 56 | 57 | /** 58 | * @brief Compares two lines accordingly with the 59 | * line number and returns if there is a difference. 60 | * 61 | * @param a First line. 62 | * @param b Second line. 63 | * 64 | * @return Returns a negative, integer or zero if the first 65 | * line is less than, equal to, or greater than the second 66 | * line. 67 | */ 68 | int line_cmp(const void *a, const void *b) 69 | { 70 | struct dw_line *l1 = *(void **)a; 71 | struct dw_line *l2 = *(void **)b; 72 | return ((int)l1->line_no - (int)l2->line_no); 73 | } 74 | 75 | /** 76 | * @brief Check if the parameter @p c is a valid variable 77 | * and/or function name character. 78 | * 79 | * @param c Character to be checked. 80 | * 81 | * @return Returns 1 if valid or 0 otherwise. 82 | */ 83 | inline static int line_is_valid_variable_name(char c) 84 | { 85 | return (isalpha(c) || isdigit(c) || c == '_'); 86 | } 87 | 88 | /** 89 | * @brief For a given line @p line, gets the index of the variable 90 | * (if one) inside the string or no one if found, gets the index 91 | * of the first printable character. 92 | * 93 | * @param line Line to be checked. 94 | * @param var_name Target variable name. 95 | * 96 | * @return Returns the index of the variable (if applicable) or 97 | * the index of the first printable character found. 98 | */ 99 | static int line_first_printable_or_var 100 | ( 101 | const char * const line, 102 | const char * const var_name) 103 | { 104 | const char *p; /* Substring variable. */ 105 | const char *e; /* End pointer. */ 106 | int i; /* Loop index. */ 107 | size_t len; /* Variable name length. */ 108 | 109 | len = strlen(var_name); 110 | p = line; 111 | e = line + strlen(line); 112 | 113 | /* Searches for the variable name. */ 114 | while ((p = strstr(p, var_name)) != NULL) 115 | { 116 | if ( (size_t)(e - p + 1) >= len 117 | && line_is_valid_variable_name( p[len] ) ) 118 | { 119 | p += len; 120 | continue; 121 | } 122 | else 123 | break; 124 | } 125 | 126 | /* If found. */ 127 | if (p != NULL) 128 | return (p - line); 129 | 130 | 131 | /* Not found, gets the index of first printable char. */ 132 | for (i = 0; line[i] != '\0' && isspace(line[i]); i++); 133 | return (i); 134 | } 135 | 136 | /** 137 | * @brief Reindent a given line @p line by replacing all tabs 138 | * for spaces, this is needed for the proper alignment in 139 | * line_detailed_printer() routine. 140 | * 141 | * @param line Line to be reindented. 142 | * 143 | * @return Returns a new allocated line (user should free with 144 | * free()) with all tabs replaced with spaces. 145 | * 146 | */ 147 | static char *line_reindent(const char *line) 148 | { 149 | char *reindented_line; /* Target reindented line. */ 150 | int n_tabs; /* Amount of tabs. */ 151 | int n_spaces; /* Amount of spaces. */ 152 | int i; /* Loop index. */ 153 | 154 | /* Amount of tabs and spaces. */ 155 | n_tabs = 0; 156 | n_spaces = 0; 157 | for (i = 0; line[i] != '\0'; i++) 158 | { 159 | if (line[i] == '\t') 160 | n_tabs++; 161 | else if (line[i] == ' ') 162 | n_spaces++; 163 | else 164 | break; 165 | } 166 | 167 | /* Total space amount. */ 168 | n_spaces = n_spaces + (n_tabs * FUNCTION_INDENT_LEVEL); 169 | reindented_line = calloc(sizeof(char), (n_spaces + strlen(line) - i + 1)); 170 | 171 | /* Assemble the final line. */ 172 | memset(reindented_line, ' ', n_spaces); 173 | strcat(reindented_line, line + i); 174 | 175 | return (reindented_line); 176 | } 177 | 178 | /** 179 | * @brief Given a complete source file name @p filename, 180 | * read the entire file and saves into an array. 181 | * 182 | * @param filename File to be read. 183 | * @param highlight Enable (or not) syntax highlight. 184 | * 185 | * @return Returns 0 if success and a negative number 186 | * otherwise. 187 | */ 188 | int line_read_source(const char *filename, int highlight, char *theme_file) 189 | { 190 | FILE *fp; /* File pointer. */ 191 | char *line; /* Current line. */ 192 | char *tmp; /* Tmp line. */ 193 | size_t len; /* Allocated size. */ 194 | ssize_t read; /* Bytes read. */ 195 | char *reindented; /* Reindent line. */ 196 | 197 | line = NULL; 198 | len = 0; 199 | 200 | /* Try to read the source. */ 201 | fp = fopen(filename, "r"); 202 | if (fp == NULL) 203 | return (-1); 204 | 205 | /* Initialize array. */ 206 | array_init(&source_lines); 207 | 208 | /* If syntax highlight on. 209 | * 210 | * Note that using syntax highlight ends up using memory memory, 211 | * since we still need the original source code in order to 212 | * calculate the 'cursor' positioning, in the 'line_detailed_printer'. 213 | */ 214 | if (highlight) 215 | { 216 | /* Initialize syntax highlighting. */ 217 | if (highlight_init(theme_file) < 0) 218 | { 219 | fclose(fp); 220 | array_finish(&source_lines); 221 | source_lines = NULL; 222 | return (-1); 223 | } 224 | 225 | array_init(&source_lines_highlighted); 226 | 227 | /* Read the entire file. */ 228 | while ((read = getline(&line, &len, fp)) != -1) 229 | { 230 | array_add(&source_lines, 231 | (reindented = line_reindent(line))); 232 | 233 | array_add(&source_lines_highlighted, 234 | highlight_line(reindented, NULL)); 235 | } 236 | 237 | highlight_finish(); 238 | } 239 | else 240 | { 241 | /* Read the entire file. */ 242 | while ((read = getline(&line, &len, fp)) != -1) 243 | array_add(&source_lines, line_reindent(line)); 244 | } 245 | 246 | /* Set base name. */ 247 | tmp = basename((char *)filename); 248 | base_file_name = malloc(sizeof(char) * (strlen(tmp) + 1)); 249 | strcpy(base_file_name, tmp); 250 | 251 | free(line); 252 | fclose(fp); 253 | return (0); 254 | } 255 | 256 | /** 257 | * @brief Free all the allocated resources for line usage, 258 | * including the array lines and base_file_name. 259 | */ 260 | void line_free_source(int highlight) 261 | { 262 | size_t size; /* Amout of lines. */ 263 | 264 | /* If there is something to be removed. */ 265 | if (source_lines == NULL) 266 | return; 267 | 268 | size = array_size(&source_lines); 269 | 270 | if (highlight) 271 | { 272 | for (size_t i = 0; i < size; i++) 273 | { 274 | free( array_remove(&source_lines, 0, NULL) ); 275 | highlight_free( 276 | array_remove(&source_lines_highlighted, 0, NULL) ); 277 | } 278 | array_finish(&source_lines_highlighted); 279 | } 280 | else 281 | { 282 | for (size_t i = 0; i < size; i++) 283 | free( array_remove(&source_lines, 0, NULL) ); 284 | } 285 | 286 | array_finish(&source_lines); 287 | 288 | /* Free the base name. */ 289 | free(base_file_name); 290 | } 291 | 292 | /** 293 | * @brief Dummy function that does nothing... 294 | * useful for debugging purposes only. 295 | */ 296 | void line_null_printer(int depth, unsigned line_no, 297 | struct dw_variable *v, union var_value *v_before, 298 | union var_value *v_after, int *array_idxs) 299 | { 300 | /* Avoid unused args. */ 301 | ((void)depth); 302 | ((void)line_no); 303 | ((void)v); 304 | ((void)v_before); 305 | ((void)v_after); 306 | ((void)array_idxs); 307 | } 308 | 309 | /** 310 | * @brief Prints the line number changed, the variable name, its 311 | * context and before/after values. 312 | * 313 | * @param depth Current function depth. 314 | * @param line_no Current line number. 315 | * @param v Variable analized. 316 | * @param v_before Value before being changed. 317 | * @param v_after Value after being changed. 318 | * @param array_idxs Computed index, only applicable 319 | * if variable is an array, otherwise, 320 | * this value can safely be NULL. 321 | */ 322 | void line_default_printer(int depth, unsigned line_no, 323 | struct dw_variable *v, union var_value *v_before, 324 | union var_value *v_after, int *array_idxs) 325 | { 326 | /* If base type. */ 327 | if (v->type.var_type & (TBASE_TYPE|TENUM|TPOINTER)) 328 | { 329 | fn_printf(depth, 0, 330 | "[Line: %d] [%s] (%s) %s!, before: %s, after: %s\n", 331 | line_no, 332 | (v->scope == VGLOBAL) ? "global" : "local", 333 | v->name, 334 | (!v->initialized ? "initialized" : "has changed"), 335 | var_format_value(before, v_before, v->type.encoding, v->byte_size), 336 | var_format_value(after, v_after, v->type.encoding, v->byte_size) 337 | ); 338 | } 339 | 340 | /* If array. */ 341 | else if (v->type.var_type == TARRAY) 342 | { 343 | /* Print indexes and values. */ 344 | fn_printf(depth, 0, 345 | "[Line: %d] [%s] (%s", 346 | line_no, 347 | (v->scope == VGLOBAL) ? "global" : "local", 348 | v->name 349 | ); 350 | 351 | for (int j = 0; j < v->type.array.dimensions; j++) 352 | fprintf(pbd_output, "[%d]", array_idxs[j]); 353 | 354 | fprintf(pbd_output, ") has changed!, before: %s, after: %s\n", 355 | var_format_value(before, v_before, v->type.encoding, 356 | v->type.array.size_per_element), 357 | 358 | var_format_value(after, v_after, v->type.encoding, 359 | v->type.array.size_per_element) 360 | ); 361 | } 362 | } 363 | 364 | /** 365 | * @brief Prints the file name, line number changed, the line, 366 | * variable name and before/after values. 367 | * 368 | * @param depth Current function depth. 369 | * @param line_no Current line number. 370 | * @param v Variable analized. 371 | * @param v_before Value before being changed. 372 | * @param v_after Value after being changed. 373 | * @param array_idxs Computed index, only applicable 374 | * if variable is an array, otherwise, 375 | * this value can safely be NULL. 376 | */ 377 | void line_detailed_printer(int depth, unsigned line_no, 378 | struct dw_variable *v, union var_value *v_before, 379 | union var_value *v_after, int *array_idxs) 380 | { 381 | struct array *line_ptr; 382 | char *line; 383 | size_t size; 384 | 385 | line_ptr = source_lines; 386 | line = array_get(&line_ptr, line_no - 1, NULL); 387 | size = array_size(&line_ptr); 388 | 389 | /* 390 | * The line alignment is pretty trickier, it takes: 391 | * - the base filename 392 | * - the amount of digits of line number 393 | * - the adjustment of the 'cursor "^"' based in the variable name (if one) 394 | * plus the cosmetic symbols: "[", ":", "]" and ":". 395 | * 396 | * Pay attention while changing this. 397 | * 398 | * TODO: Think of a better solution. 399 | */ 400 | int predicted_offset = 401 | strlen(base_file_name) + 402 | line_first_printable_or_var(line, v->name) + 403 | (log10(line_no) + 1) + 404 | 4; 405 | 406 | /* Read highlighted line if available. */ 407 | if (source_lines_highlighted != NULL) 408 | { 409 | line = array_get(&source_lines_highlighted, line_no - 1, NULL); 410 | line_ptr = source_lines_highlighted; 411 | } 412 | 413 | /* If base type. */ 414 | if (v->type.var_type & (TBASE_TYPE|TENUM|TPOINTER|TARRAY)) 415 | { 416 | int start; 417 | int end; 418 | 419 | /* Print lines before. */ 420 | if (args.context) 421 | { 422 | fprintf(pbd_output, 423 | "----------------------------------------------------------" 424 | "---------------------\n"); 425 | 426 | if (line_no - args.context > 0) 427 | start = line_no - args.context - 1; 428 | else 429 | start = 0; 430 | 431 | end = line_no - 1; 432 | 433 | while (start < end) 434 | { 435 | fn_printf(depth, 0, "[%s:%d]:%s", base_file_name, start+1, 436 | array_get(&line_ptr, start, NULL)); 437 | start++; 438 | } 439 | } 440 | 441 | /* Line changed. */ 442 | fn_printf(depth, 0, "[%s:%d]:%s", base_file_name, line_no, line); 443 | 444 | /* If not array, lets proceed normally. */ 445 | if (v->type.var_type != TARRAY) 446 | { 447 | fn_printf(depth, predicted_offset, 448 | "^----- (%s) before: %s, after: %s\n", 449 | v->name, 450 | var_format_value(before, v_before, v->type.encoding, v->byte_size), 451 | var_format_value(after, v_after, v->type.encoding, v->byte_size) 452 | ); 453 | } 454 | 455 | /* If array, we also need to print the indexes. */ 456 | else 457 | { 458 | fn_printf(depth, predicted_offset, 459 | "^----- (%s", 460 | v->name, 461 | var_format_value(before, v_before, v->type.encoding, v->byte_size), 462 | var_format_value(after, v_after, v->type.encoding, v->byte_size) 463 | ); 464 | 465 | for (int j = 0; j < v->type.array.dimensions; j++) 466 | fprintf(pbd_output, "[%d]", array_idxs[j]); 467 | 468 | fprintf(pbd_output, "), before: %s, after: %s\n", 469 | var_format_value(before, v_before, v->type.encoding, 470 | v->type.array.size_per_element), 471 | 472 | var_format_value(after, v_after, v->type.encoding, 473 | v->type.array.size_per_element) 474 | ); 475 | } 476 | 477 | /* Print lines after. */ 478 | if (args.context) 479 | { 480 | 481 | if (line_no + args.context <= size) 482 | end = line_no + args.context; 483 | else 484 | end = size - 1; 485 | 486 | start = line_no; 487 | while (start < end) 488 | { 489 | fn_printf(depth, 0, "[%s:%d]:%s", base_file_name, start+1, 490 | array_get(&line_ptr, start, NULL)); 491 | start++; 492 | } 493 | 494 | fprintf(pbd_output, 495 | "----------------------------------------------------------" 496 | "---------------------\n\n"); 497 | } 498 | fprintf(pbd_output, "\n"); 499 | } 500 | } 501 | -------------------------------------------------------------------------------- /src/man/man1/pbd.1: -------------------------------------------------------------------------------- 1 | .\" MIT License 2 | .\" 3 | .\" Copyright (c) 2019-2020 Davidson Francis 4 | .\" 5 | .\" Permission is hereby granted, free of charge, to any person obtaining a copy 6 | .\" of this software and associated documentation files (the "Software"), to deal 7 | .\" in the Software without restriction, including without limitation the rights 8 | .\" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | .\" copies of the Software, and to permit persons to whom the Software is 10 | .\" furnished to do so, subject to the following conditions: 11 | .\" 12 | .\" The above copyright notice and this permission notice shall be included in all 13 | .\" copies or substantial portions of the Software. 14 | .\" 15 | .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | .\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | .\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | .\" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | .\" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | .\" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | .\" SOFTWARE. 22 | .\" 23 | .TH "PBD" "1" "2020-06-30" "v0.7" "pbd man page" 24 | .SH NAME 25 | pbd \- Printf Based Debugger 26 | .SH SYNOPSIS 27 | \fBpbd\fR [\fIoptions\fR] executable function_name [\fIexecutable_options\fR] 28 | .SH DESCRIPTION 29 | .PP 30 | \fBPBD\fR is a C debugger for Linux systems, which aims to monitor all 31 | global and local variables (of a specific function) and nicely print the 32 | old/new values on the screen, as well as the place where the change has 33 | occurred. 34 | .PP 35 | Focusing on performance, PBD can debug large amounts of variables in a small 36 | amount of time, and when compared to classic debuggers such as GDB, it 37 | achieves a speedup of up to 12x depending on the compiler version, GDB and 38 | workloads used. 39 | .SH OPTIONS 40 | .IP "-h --help" 41 | Display this information 42 | .IP "-v --version" 43 | Display the PBD version and copyright. 44 | .IP "-s --show-lines" 45 | Prints the line and line number of the code that was changed. By default, 46 | only the line number is printed. 47 | .IP "-x --context " 48 | Shows lines before and after the code portion. This option is meant 49 | to be used in conjunction with -s. 50 | .IP "-l --only-locals" 51 | Monitors only local variables (default: global + local) 52 | .IP "-g --only-globals" 53 | Monitors only global variables (default: global + local) 54 | .IP "-i --ignore-list=var1,var2..." 55 | Given a comma separated sequence of variables names, PBD will ignore them, 56 | whether local or global. Options -i and -w are mutually exclusive. 57 | .IP "-w --watch-list=var1,var2..." 58 | Given a comma separated sequence of variables names, PBD will only watch them, 59 | whether local or global. Options -i and -w are mutually exclusive. 60 | .IP "-o --output " 61 | Sets an output file for PBD output. Useful to not mix PBD and executable 62 | outputs. 63 | .IP "--args" 64 | Delimits executable arguments from this point. All arguments onwards will be 65 | treated as executable program arguments. 66 | .PP 67 | \fIStatic Analysis options:\fR 68 | .PP 69 | PBD is able to do a previous static analysis in the C source code that 70 | belongs to the monitored function, and thus, greatly improving the 71 | debugging time. Note however, that this is an experimental feature. 72 | .IP "-S --static" 73 | Enables static analysis 74 | .PP 75 | Optional flags: 76 | .IP "-D sym[=val]" 77 | Defines 'sym' with value 'val'. 78 | .IP "-U " 79 | Defines 'sym' with value 'val'. 80 | .IP "-I " 81 | Add 'dir' to the include path. 82 | .IP "--std=" 83 | Defines the language standard, supported values 84 | are: c89, gnu89, c99, gnu99, c11 and gnu11. 85 | (Default: gnu11) 86 | .PP 87 | \fISyntax highlighting options:\fR 88 | .IP "-c --color" 89 | Enables syntax highlight. This option only takes effect if used together with 90 | --show-lines. Note that this option also requires a 256-color compatible 91 | terminal. 92 | .IP "-t --theme " 93 | Select a theme file for the highlighting. 94 | .PP 95 | The following options are for PBD internals: 96 | .IP "-d --dump-all" 97 | Dump all information gathered by the executable: name, size, type, encoding, 98 | location, its breakpoint list and etc... 99 | .PP 100 | \fI\'Unsafe\' options:\fR 101 | .PP 102 | The options below are meant to be used with caution, since 103 | they could lead to wrong output. 104 | .IP "--avoid-equal-statements" 105 | If enabled, PBD will ignore all line statements that are 'duplicated', i.e: 106 | belongs to the same liner number, regardless its address. Although this could 107 | lead to a great speed up, this option may produce wrong output in certain 108 | cases, like tracking loops and multiples changes in the same line. Note that, 109 | in this case, PBD *will* note the changes, but will identify a wrong line 110 | number, in most cases, a number greater than the expected. 111 | .SH NOTES 112 | .PP 113 | At the current release (v0.7) PBD have some points that need some hightlights: 114 | .PP 115 | Supported compilers (others might work): 116 | .RS 2 117 | .IP \(em 2 118 | GCC 119 | .IP \(em 2 120 | Clang 121 | .RE 122 | .PP 123 | Architecture, OS and Language support: 124 | .RS 2 125 | .IP \(em 2 126 | x86 and x86_64 at the moment 127 | .IP \(em 2 128 | Linux environments 129 | .IP \(em 2 130 | C language 131 | .RE 132 | .PP 133 | DWARF Analysis: 134 | .RS 2 135 | .IP \(em 2 136 | PBD only supports DWARF-2 standard, which means that the user \fB*must*\fR 137 | build the target executable with \fI\-gdwarf\-2\fR flag. 138 | .IP \(em 2 139 | PBD do not support PIE executables, so all the target executables 140 | \fB*should\fR be built with \fI\-no\-pie\fR flag, if the compiler 141 | build PIE executables by default. 142 | .RE 143 | .PP 144 | C Features: 145 | .PP 146 | Currently PBD lacks the following features from C language, and thus, they will 147 | not be analyzed if present: 148 | .RS 2 149 | .IP \(em 2 150 | Structures 151 | .IP \(em 2 152 | Unions 153 | .RE 154 | .SH BUGS 155 | .PP 156 | No known bugs. 157 | .SH AUTHOR 158 | .PP 159 | Written by Davidson Francis (davidsondfgl@gmail.com), see 160 | \fIcontributors\fR page in github.com/Theldus/PBD for more details. 161 | -------------------------------------------------------------------------------- /src/ptrace.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "ptrace.h" 26 | #include "util.h" 27 | #include 28 | 29 | /** 30 | * Architecture independent ptrace helper functions. 31 | */ 32 | 33 | /** 34 | * @brief Creates a new process and executes a file 35 | * pointed to by @p file. 36 | * 37 | * @param file File to be executed. 38 | * 39 | * @return In case of success, returns the child PID, 40 | * otherwise, a negative number. 41 | */ 42 | int pt_spawnprocess(const char *file, char **argv) 43 | { 44 | pid_t child; /* Child Process. */ 45 | 46 | child = fork(); 47 | if (child == 0) 48 | { 49 | if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) 50 | { 51 | QUIT(EXIT_FAILURE, "a problem has ocurred while spawning the child!\n" 52 | "Reason: %s\n\n%s", strerror(errno), 53 | "Please check if your process have attach permissions. This can\n" 54 | "be set in the file /proc/sys/kernel/yama/ptrace_scope.\n" 55 | "This is known to happen inside unprivileged Docker containers\n" 56 | ); 57 | } 58 | execv(file, (char *const *)argv); 59 | } 60 | 61 | return (child); 62 | } 63 | 64 | /** 65 | * @brief Executes until the child process has been 66 | * stopped (breakpoint, signal...). 67 | * 68 | * @return Returns PT_CHILD_EXIT if the child process 69 | * was terminated, otherwise, returns 0. 70 | */ 71 | int pt_waitchild(void) 72 | { 73 | int status; /* Status Code. */ 74 | wait(&status); 75 | 76 | if (WIFEXITED(status) || WIFSIGNALED(status)) 77 | return (PT_CHILD_EXIT); 78 | 79 | return (0); 80 | } 81 | 82 | /** 83 | * @brief Continues the child execution. 84 | * 85 | * @param child Child to be resumed. 86 | */ 87 | int pt_continue(pid_t child) 88 | { 89 | ptrace(PTRACE_CONT, child, NULL, NULL); 90 | return (0); 91 | } 92 | 93 | /** 94 | * @brief Continues the child execution in single 95 | * step mode. 96 | * 97 | * @param child Child to be 'singlestepped'. 98 | */ 99 | int pt_continue_single_step(pid_t child) 100 | { 101 | ptrace(PTRACE_SINGLESTEP, child, NULL, NULL); 102 | return (0); 103 | } 104 | 105 | /** 106 | * @brief Reads sizeof(long) bytes from a given process 107 | * @p child at address @p addr. 108 | * 109 | * @param child Child process. 110 | * @param addr Address to be read. 111 | * 112 | * @return Returns a long containing a value for the specified 113 | * address. 114 | * 115 | * @note The rationale behind 'long' is simple: while 116 | * handling breakpoints, PBD needs to read and write a 117 | * single byte of memory multiples times, and since the 118 | * minor amount of bytes ptrace() can read/write is long, 119 | * let us read/write in multiples of long. 120 | */ 121 | long pt_readmemory_long(pid_t child, uintptr_t addr) 122 | { 123 | return (ptrace(PTRACE_PEEKDATA, child, addr, NULL)); 124 | } 125 | 126 | /** 127 | * @brief Writes a 'long' value into a given process @p child 128 | * and address @p addr. 129 | * 130 | * @param child Child process. 131 | * @param addr Address to be written. 132 | * @param data Value to be written. 133 | * 134 | * @note See pt_readmemory_long() notes. 135 | */ 136 | void pt_writememory_long(pid_t child, uintptr_t addr, long data) 137 | { 138 | ptrace(PTRACE_POKEDATA, child, addr, data); 139 | } 140 | 141 | /** 142 | * @brief Reads a uint64_t value (usually 64-bit in x86_64) from 143 | * a given processs @p child and address @p addr. 144 | * 145 | * @param child Child process. 146 | * @param addr Address to be read. 147 | * 148 | * @return Returns a uint64_t containing a value for the specified 149 | * address. 150 | */ 151 | uint64_t pt_readmemory64(pid_t child, uintptr_t addr) 152 | { 153 | /* Endianness agnostic, I hope so. */ 154 | union 155 | { 156 | uint32_t v[2]; 157 | uint64_t value; 158 | } u; 159 | 160 | /* Expected in 64-bit systems. */ 161 | if (sizeof(long) == 8) 162 | return (ptrace(PTRACE_PEEKDATA, child, addr, NULL)); 163 | 164 | /* Expected in 32-bit systems. */ 165 | if (sizeof(long) == 4) 166 | { 167 | u.v[0] = ptrace(PTRACE_PEEKDATA, child, addr, NULL); 168 | u.v[1] = ptrace(PTRACE_PEEKDATA, child, addr + 4, NULL); 169 | return (u.value); 170 | } 171 | else 172 | QUIT(EXIT_FAILURE, "unexpected long size: %zu", sizeof(long)); 173 | } 174 | 175 | /** 176 | * @brief Writes a uint64_t value into a given processs @p child 177 | * and address @p addr. 178 | * 179 | * @param child Child process. 180 | * @param addr Address to be written. 181 | * @param data Value to be written. 182 | */ 183 | void pt_writememory64(pid_t child, uintptr_t addr, uint64_t data) 184 | { 185 | /* Endianness agnostic, I hope so. */ 186 | union 187 | { 188 | uint32_t v[2]; 189 | uint64_t value; 190 | } u; 191 | 192 | /* Expected in 64-bit systems. */ 193 | if (sizeof(long) == 8) 194 | ptrace(PTRACE_POKEDATA, child, addr, data); 195 | 196 | /* Expected in 32-bit systems. */ 197 | else if (sizeof(long) == 4) 198 | { 199 | u.value = data; 200 | ptrace(PTRACE_POKEDATA, child, addr , u.v[0]); 201 | ptrace(PTRACE_POKEDATA, child, addr + 4, u.v[1]); 202 | } 203 | else 204 | QUIT(EXIT_FAILURE, "unexpected long size: %zu", sizeof(long)); 205 | } 206 | 207 | /** 208 | * @brief Reads an arbitrary amount of bytes @p len of the given 209 | * process @p child in the address @p addr. 210 | * 211 | * @param child Child process. 212 | * @param addr Address to be read. 213 | * @param len How many bytes will be read. 214 | 215 | * @return Returns a pointer containing the the memory read. 216 | * 217 | * @note Its up to the caller function to free the returned 218 | * pointer. 219 | */ 220 | char *pt_readmemory(pid_t child, uintptr_t addr, size_t len) 221 | { 222 | char *data; /* Return pointer. */ 223 | 224 | /* Allocates enough room for the data to be read. */ 225 | if (posix_memalign((void *)&data, 32, sizeof(char) * len) != 0) 226 | return (NULL); 227 | 228 | /* 229 | * Note: process_vm_readv() is only supported by GNU libc with 230 | * versions >= 2.15, moreover, this function is Linux-specific 231 | * and supported only on kernels >= 3.12 (2012-ish). 232 | * 233 | * So if we're not using GLIBC or the version is inferior, lets 234 | * use the tradional (and way slower) ptrace approach. 235 | */ 236 | #if defined(__linux__) && defined(__GLIBC__) \ 237 | && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 15)) 238 | 239 | struct iovec local[1]; /* IO Vector Local. */ 240 | struct iovec remote[1]; /* IO Vector Remote. */ 241 | 242 | /* Prepare arguments for readv. */ 243 | local[0].iov_base = data; 244 | local[0].iov_len = len; 245 | remote[0].iov_base = (void *) addr; 246 | remote[0].iov_len = len; 247 | 248 | if (process_vm_readv(child, local, 1, remote, 1, 0) != (ssize_t)len) 249 | return (NULL); 250 | else 251 | return (data); 252 | 253 | /* Ptrace approach. */ 254 | #else 255 | char *laddr; /* Auxiliar pointer. */ 256 | int i; /* Address index. */ 257 | int j; /* Block counter. */ 258 | int long_size; /* Long size. */ 259 | 260 | long_size = sizeof(long); 261 | union u 262 | { 263 | long val; 264 | char chars[sizeof(long)]; 265 | } temp_data; 266 | 267 | i = 0; 268 | j = len / long_size; 269 | 270 | /* Assigns memory. */ 271 | laddr = data; 272 | 273 | /* While there are 'blocks' remaining, keep reading. */ 274 | while (i < j) 275 | { 276 | temp_data.val = ptrace(PTRACE_PEEKDATA, child, addr + i * sizeof(void *), NULL); 277 | memcpy(laddr, temp_data.chars, long_size); 278 | i++; 279 | laddr += long_size; 280 | } 281 | 282 | /* If few bytes remaining, read them. */ 283 | j = len % long_size; 284 | if (j != 0) 285 | { 286 | temp_data.val = ptrace(PTRACE_PEEKDATA, child, addr + i * sizeof(void *), NULL); 287 | memcpy(laddr, temp_data.chars, j); 288 | } 289 | 290 | return (data); 291 | #endif 292 | } 293 | 294 | /** 295 | * @brief Writes an arbitrary amount of bytes @p len into the given 296 | * process @p child in the address @p addr. 297 | * 298 | * @param child Child process. 299 | * @param addr Address to be written. 300 | * @param data Data to be write. 301 | * @param len How many bytes will be written. 302 | */ 303 | void pt_writememory(pid_t child, uintptr_t addr, char *data, size_t len) 304 | { 305 | int i; /* Address index. */ 306 | int j; /* Block counter. */ 307 | char *laddr; /* Auxiliar pointer. */ 308 | int long_size; /* Long size. */ 309 | 310 | long_size = sizeof(long); 311 | union u 312 | { 313 | long val; 314 | char chars[sizeof(long)]; 315 | } temp_data; 316 | 317 | i = 0; 318 | j = len / long_size; 319 | laddr = data; 320 | 321 | /* While there are 'blocks' remaining, keep writing. */ 322 | while (i < j) 323 | { 324 | memcpy(temp_data.chars, laddr, long_size); 325 | ptrace(PTRACE_POKEDATA, child, addr + i * sizeof(char *), temp_data.val); 326 | i++; 327 | laddr += long_size; 328 | } 329 | 330 | /* If few bytes remaining, write them. */ 331 | j = len % long_size; 332 | if (j != 0) 333 | { 334 | memcpy(temp_data.chars, laddr, j); 335 | ptrace(PTRACE_POKEDATA, child, addr + i * sizeof(char *), temp_data.val); 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /src/tests/Makefile: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2020 Davidson Francis 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC ?= gcc 24 | override CFLAGS += -Wall -Wextra 25 | override CFLAGS += -std=c99 26 | override CFLAGS += -fno-omit-frame-pointer -O0 -gdwarf-2 27 | 28 | # 29 | # PIE Support check 30 | # 31 | # In order to PBD works as expected, every source intended to be analyzed 32 | # _should_ be built with at least -O0 and -gdwarf-2, this is not optional, 33 | # nonetheless, it's also highly desirable that the frame pointer is not 34 | # ommited, thus: -fno-omit-frame-pointer. 35 | # 36 | # Last but not least, PBD doesn't support 'Position Independent Executables' 37 | # and all the addresses need to be known from before execution. Since some 38 | # compilers build 'PIE' executables (yeah, redundant ;-)) by default, we 39 | # also need to check this in execution time. 40 | # 41 | # --- 42 | # This solution is not the best but at least cover GCC and CLANG... 43 | # 44 | PIE_SUPPORT := $(shell echo "int main(){}" | $(CC) -x c -; file a.out \ 45 | | grep -Ec "shared|pie"; rm a.out &> /dev/null) 46 | 47 | ifeq ($(PIE_SUPPORT), 1) 48 | override CFLAGS += -no-pie 49 | endif 50 | 51 | # Source 52 | C_SRC = $(wildcard *.c) 53 | OBJ = $(C_SRC:.c=.o) 54 | 55 | # Pretty print 56 | Q := @ 57 | ifeq ($(V), 1) 58 | Q := 59 | endif 60 | 61 | %.o: %.c 62 | @echo " CC $@" 63 | $(Q)$(CC) $< $(CFLAGS) -c -o $@ 64 | 65 | all: test run_tests 66 | 67 | test: $(OBJ) 68 | @echo " LD $@" 69 | $(Q)$(CC) $^ $(CFLAGS) -o $@ 70 | 71 | run_tests: test 72 | @bash run-tests.sh 73 | 74 | clean: 75 | @echo " CLEAN" 76 | @rm -f $(OBJ) test 77 | -------------------------------------------------------------------------------- /src/tests/outputs/test_func1_expected: -------------------------------------------------------------------------------- 1 | PBD (Printf Based Debugger) v0.7 2 | --------------------------------------- 3 | Debugging function func1: 4 | 5 | [depth: 1] Entering function... 6 | [Line: 84] [local] (func1_local_a) initialized!, before: 0, after: 3 7 | [Line: 91] [global] (anim_vect[0]) has changed!, before: 0, after: 1 8 | [Line: 92] [global] (anim_vect[1]) has changed!, before: 0, after: 2 9 | [Line: 93] [global] (anim_vect[2]) has changed!, before: 0, after: 3 10 | [Line: 94] [global] (anim_vect[3]) has changed!, before: 0, after: 4 11 | [Line: 97] [global] (integer_pointer) has changed!, before: 0x0, after: 0xDEADBEEB 12 | [Line: 98] [global] (integer_pointer) has changed!, before: 0xDEADBEEB, after: 0xDEADBEEF 13 | [Line: 102] [global] (array1dim[0]) has changed!, before: 0, after: 1 14 | [Line: 102] [global] (array1dim[1]) has changed!, before: 0, after: 2 15 | [Line: 102] [global] (array1dim[2]) has changed!, before: 0, after: 3 16 | [Line: 102] [global] (array1dim[3]) has changed!, before: 0, after: 4 17 | [Line: 102] [global] (array1dim[4]) has changed!, before: 0, after: 5 18 | [Line: 102] [global] (array1dim[5]) has changed!, before: 0, after: 6 19 | [Line: 102] [global] (array1dim[6]) has changed!, before: 0, after: 7 20 | [Line: 102] [global] (array1dim[7]) has changed!, before: 0, after: 8 21 | [Line: 102] [global] (array1dim[8]) has changed!, before: 0, after: 9 22 | [Line: 102] [global] (array1dim[9]) has changed!, before: 0, after: 10 23 | [Line: 104] [global] (array1dim[9]) has changed!, before: 10, after: 19 24 | [Line: 107] [global] (array1dim[0]) has changed!, before: 1, after: 5 25 | [Line: 107] [global] (array1dim[1]) has changed!, before: 2, after: 5 26 | [Line: 107] [global] (array1dim[2]) has changed!, before: 3, after: 5 27 | [Line: 107] [global] (array1dim[3]) has changed!, before: 4, after: 5 28 | [Line: 107] [global] (array1dim[5]) has changed!, before: 6, after: 5 29 | [Line: 107] [global] (array1dim[6]) has changed!, before: 7, after: 5 30 | [Line: 107] [global] (array1dim[7]) has changed!, before: 8, after: 5 31 | [Line: 107] [global] (array1dim[8]) has changed!, before: 9, after: 5 32 | [Line: 107] [global] (array1dim[9]) has changed!, before: 19, after: 5 33 | [Line: 110] [global] (array10x10[5][7][6]) has changed!, before: 0, after: 1 34 | [Line: 112] [local] (func1_local_b) initialized!, before: 0, after: 8 35 | [Line: 115] [local] (func1_local_argument1) initialized!, before: 0, after: 1 36 | [Line: 121] [global] (gi64) has changed!, before: 0, after: 1 37 | [Line: 121] [local] (func1_local_b) has changed!, before: 8, after: 9 38 | [Line: 124] [local] (func1_local_d) initialized!, before: 0.000000, after: 2.030000 39 | [Line: 125] [local] (func1_local_c) initialized!, before: 0.000000, after: 2.140000 40 | [Line: 126] [local] (func1_local_c) has changed!, before: 2.140000, after: 3.140000 41 | [Line: 129] [local] (func1_local_e) initialized!, before: 0.000000, after: 1.123400 42 | [Line: 130] [local] (func1_local_e) has changed!, before: 1.123400, after: 2.123400 43 | [Line: 151] [local] (func1_local_d) has changed!, before: 2.030000, after: 0.000000 44 | [Line: 151] [local] (func1_local_d) has changed!, before: 0.000000, after: 5.000000 45 | [Line: 151] [local] (func1_local_d) has changed!, before: 5.000000, after: 10.000000 46 | [Line: 151] [local] (func1_local_d) has changed!, before: 10.000000, after: 15.000000 47 | [Line: 151] [local] (func1_local_d) has changed!, before: 15.000000, after: 20.000000 48 | [Line: 154] [global] (gi8) has changed!, before: 0, after: 127 49 | [Line: 155] [global] (gu8) has changed!, before: 0, after: 255 50 | [Line: 156] [global] (gi16) has changed!, before: 0, after: 32767 51 | [Line: 157] [global] (gu16) has changed!, before: 0, after: 65535 52 | [Line: 158] [global] (gi32) has changed!, before: 0, after: 2147483647 53 | [Line: 159] [global] (gu32) has changed!, before: 0, after: 4294967295 54 | [Line: 160] [global] (gi64) has changed!, before: 1, after: 9223372036854775807 55 | [Line: 161] [global] (gu64) has changed!, before: 0, after: 18446744073709551615 56 | [depth: 1] Returning to function... 57 | 58 | 59 | [depth: 1] Entering function... 60 | [Line: 97] [global] (integer_pointer) has changed!, before: 0xDEADBEEF, after: 0xDEADBEEB 61 | [Line: 98] [global] (integer_pointer) has changed!, before: 0xDEADBEEB, after: 0xDEADBEEF 62 | [Line: 102] [global] (array1dim[0]) has changed!, before: 5, after: 1 63 | [Line: 102] [global] (array1dim[1]) has changed!, before: 5, after: 2 64 | [Line: 102] [global] (array1dim[2]) has changed!, before: 5, after: 3 65 | [Line: 102] [global] (array1dim[3]) has changed!, before: 5, after: 4 66 | [Line: 102] [global] (array1dim[5]) has changed!, before: 5, after: 6 67 | [Line: 102] [global] (array1dim[6]) has changed!, before: 5, after: 7 68 | [Line: 102] [global] (array1dim[7]) has changed!, before: 5, after: 8 69 | [Line: 102] [global] (array1dim[8]) has changed!, before: 5, after: 9 70 | [Line: 102] [global] (array1dim[9]) has changed!, before: 5, after: 10 71 | [Line: 104] [global] (array1dim[9]) has changed!, before: 10, after: 19 72 | [Line: 107] [global] (array1dim[0]) has changed!, before: 1, after: 5 73 | [Line: 107] [global] (array1dim[1]) has changed!, before: 2, after: 5 74 | [Line: 107] [global] (array1dim[2]) has changed!, before: 3, after: 5 75 | [Line: 107] [global] (array1dim[3]) has changed!, before: 4, after: 5 76 | [Line: 107] [global] (array1dim[5]) has changed!, before: 6, after: 5 77 | [Line: 107] [global] (array1dim[6]) has changed!, before: 7, after: 5 78 | [Line: 107] [global] (array1dim[7]) has changed!, before: 8, after: 5 79 | [Line: 107] [global] (array1dim[8]) has changed!, before: 9, after: 5 80 | [Line: 107] [global] (array1dim[9]) has changed!, before: 19, after: 5 81 | [Line: 110] [global] (array10x10[5][7][6]) has changed!, before: 1, after: 2 82 | [Line: 112] [local] (func1_local_b) initialized!, before: 0, after: 8 83 | [Line: 115] [local] (func1_local_argument1) initialized!, before: 0, after: 2 84 | [Line: 121] [global] (gi64) has changed!, before: 9223372036854775807, after: -9223372036854775808 85 | [Line: 121] [local] (func1_local_b) has changed!, before: 8, after: 9 86 | [Line: 124] [local] (func1_local_d) initialized!, before: 0.000000, after: 2.030000 87 | [Line: 125] [local] (func1_local_c) initialized!, before: 0.000000, after: 2.140000 88 | [Line: 126] [local] (func1_local_c) has changed!, before: 2.140000, after: 3.140000 89 | [Line: 129] [local] (func1_local_e) initialized!, before: 0.000000, after: 1.123400 90 | [Line: 130] [local] (func1_local_e) has changed!, before: 1.123400, after: 2.123400 91 | [Line: 151] [local] (func1_local_d) has changed!, before: 2.030000, after: 0.000000 92 | [Line: 151] [local] (func1_local_d) has changed!, before: 0.000000, after: 5.000000 93 | [Line: 151] [local] (func1_local_d) has changed!, before: 5.000000, after: 10.000000 94 | [Line: 151] [local] (func1_local_d) has changed!, before: 10.000000, after: 15.000000 95 | [Line: 151] [local] (func1_local_d) has changed!, before: 15.000000, after: 20.000000 96 | [Line: 160] [global] (gi64) has changed!, before: -9223372036854775808, after: 9223372036854775807 97 | [depth: 1] Returning to function... 98 | 99 | -------------------------------------------------------------------------------- /src/tests/outputs/test_func3_expected_sa: -------------------------------------------------------------------------------- 1 | ===static=analysis=== [181] param (is_assign: 0) (ignored) 2 | ===static=analysis=== [186] var_a (is_assign: 0) (decl) (ignored) 3 | ===static=analysis=== [187] var_b (is_assign: 1) (decl) 4 | ===static=analysis=== [190] anim (is_assign: 0) (decl) (ignored) 5 | ===static=analysis=== [191] anim (is_assign: 1) 6 | ===static=analysis=== [194] ss (is_assign: 0) (decl) (ignored) 7 | ===static=analysis=== [195] ss (is_assign: 1) (ignored) 8 | ===static=analysis=== [203] var_a (is_assign: 1) 9 | ===static=analysis=== [204] gi64 (is_assign: 1) 10 | ===static=analysis=== [205] param (is_assign: 1) 11 | ===static=analysis=== [208] var_c (is_assign: 1) (decl) (ignored) 12 | ===static=analysis=== [209] var_c (is_assign: 1) (ignored) 13 | ===static=analysis=== [218] ptr_a (is_assign: 1) (decl) 14 | ===static=analysis=== [220] ptr_a (is_assign: 1) (decl) (ignored) 15 | ===static=analysis=== [220] var_a (is_assign: 1) 16 | ===static=analysis=== [224] ptr_a (is_assign: 1) 17 | ===static=analysis=== [225] ptr_a (is_assign: 1) 18 | ===static=analysis=== [226] ptr_a (is_assign: 1) 19 | ===static=analysis=== [227] ptr_a (is_assign: 1) 20 | ===static=analysis=== [227] var_a (is_assign: 1) 21 | ===static=analysis=== [231] var_a (is_assign: 1) 22 | ===static=analysis=== [233] vet_a (is_assign: 0) (decl) (ignored) 23 | ===static=analysis=== [235] vet_a (is_assign: 1) (ignored) 24 | ===static=analysis=== [235] var_a (is_assign: 0) (ignored) 25 | ===static=analysis=== [241] array_a (is_assign: 0) (decl) (ignored) 26 | ===static=analysis=== [242] array_a (is_assign: 1) 27 | ===static=analysis=== [245] i (is_assign: 0) (ignored) 28 | ===static=analysis=== [246] var_a (is_assign: 1) 29 | ===static=analysis=== [245] i (is_assign: 1) (ignored) 30 | ===static=analysis=== [248] i (is_assign: 0) (ignored) 31 | ===static=analysis=== [249] var_a (is_assign: 1) 32 | ===static=analysis=== [248] i (is_assign: 1) (ignored) 33 | ===static=analysis=== [251] var_a (is_assign: 0) (ignored) 34 | ===static=analysis=== [251] var_a (is_assign: 1) 35 | ===static=analysis=== [252] dummy (is_assign: 0) (decl) (ignored) 36 | ===static=analysis=== [252] dummy (is_assign: 1) (ignored) 37 | ===static=analysis=== [253] var_a (is_assign: 1) 38 | ===static=analysis=== [254] dummy (is_assign: 0) (decl) (ignored) 39 | ===static=analysis=== [254] dummy (is_assign: 1) (ignored) 40 | ===static=analysis=== [255] var_a (is_assign: 0) (ignored) 41 | ===static=analysis=== [256] dummy (is_assign: 0) (decl) (ignored) 42 | ===static=analysis=== [256] dummy (is_assign: 1) (ignored) 43 | ===static=analysis=== [255] var_a (is_assign: 1) 44 | ===static=analysis=== [255] var_a (is_assign: 0) (ignored) 45 | ===static=analysis=== [259] var_a (is_assign: 0) (ignored) 46 | ===static=analysis=== [260] dummy (is_assign: 0) (decl) (ignored) 47 | ===static=analysis=== [260] dummy (is_assign: 1) (ignored) 48 | ===static=analysis=== [261] var_a (is_assign: 1) 49 | ===static=analysis=== [262] dummy (is_assign: 0) (decl) (ignored) 50 | ===static=analysis=== [262] dummy (is_assign: 1) (ignored) 51 | ===static=analysis=== [263] var_a (is_assign: 0) (ignored) 52 | ===static=analysis=== [264] dummy (is_assign: 1) (decl) (ignored) 53 | ===static=analysis=== [265] var_a (is_assign: 0) (ignored) 54 | ===static=analysis=== [265] var_b (is_assign: 1) 55 | ===static=analysis=== [272] var_a (is_assign: 0) (ignored) 56 | ===static=analysis=== [271] var_b (is_assign: 1) 57 | ===static=analysis=== [278] var_a (is_assign: 0) (ignored) 58 | ===static=analysis=== [278] var_a (is_assign: 1) 59 | ===static=analysis=== [277] var_b (is_assign: 1) 60 | ===static=analysis=== [277] var_b (is_assign: 0) (ignored) 61 | ===static=analysis=== [281] (func call) 62 | ===static=analysis=== [284] var_b (is_assign: 1) 63 | ===static=analysis=== [287] var_a (is_assign: 1) 64 | ===static=analysis=== [290] (func call) 65 | ===static=analysis=== [294] var_a (is_assign: 0) (decl) (ignored) 66 | ===static=analysis=== [295] var_a (is_assign: 1) (ignored) 67 | ===static=analysis=== [296] var_a (is_assign: 1) (ignored) 68 | ===static=analysis=== [296] var_b (is_assign: 1) 69 | ===static=analysis=== [301] var_a (is_assign: 1) 70 | ===static=analysis=== [301] var_b (is_assign: 1) 71 | ===static=analysis=== [306] var_a (is_assign: 0) (ignored) 72 | ===static=analysis=== [308] var_b (is_assign: 0) (ignored) 73 | ===static=analysis=== [309] var_b (is_assign: 1) 74 | ===static=analysis=== [309] var_a (is_assign: 0) (ignored) 75 | ===static=analysis=== [312] gi64 (is_assign: 0) (ignored) 76 | ===static=analysis=== [312] var_b (is_assign: 1) 77 | ===static=analysis=== [313] gi64 (is_assign: 1) 78 | -------------------------------------------------------------------------------- /src/tests/pbd.supp: -------------------------------------------------------------------------------- 1 | # PBD's Valgrind Supression File 2 | # 3 | # Unfortunately, libsparse 'leaks'* some memory while while handling 4 | # include paths, more specifically in set_stream_include_path 5 | # (pre-process.c) and init_stream (tokenize.c). 6 | # 7 | # Below you can find an excerpt of the stack trace generated by 8 | # Valgrind (--leak-check=full --show-leak-kinds=all): 9 | # 10 | # 172 bytes in 11 blocks are still reachable in loss record 1 of 2 11 | # at 0x4C2CB1F: malloc (vg_replace_malloc.c:299) 12 | # by 0x40D6BB: set_stream_include_path (pre-process.c:891) 13 | # by 0x40D6BB: handle_include_path (pre-process.c:993) 14 | # 15 | # 16 | # 2,880 bytes in 1 blocks are still reachable in loss record 2 of 2 17 | # at 0x4C2ED3F: realloc (vg_replace_malloc.c:785) 18 | # by 0x4149D5: init_stream (tokenize.c:310) 19 | # by 0x414D45: tokenize (tokenize.c:1016) 20 | # by 0x40C6A3: try_include (pre-process.c:923) 21 | # by 0x41D58C: do_include_path (pre-process.c:935) 22 | # by 0x41D770: handle_include_path (pre-process.c:998) 23 | # 24 | # 25 | # * While some might argue that 'still reachable' are not _true_ 26 | # memory leaks, I don't like the idea of leaving memory to the OS, 27 | # and thus, PBD will enforce an strong memory check, considering 28 | # all kind of leaks as errors, unless otherwise stated (supression 29 | # files). 30 | 31 | { 32 | libsparse 33 | Memcheck:Leak 34 | match-leak-kinds: reachable 35 | ... 36 | fun:handle_include_path 37 | ... 38 | } 39 | -------------------------------------------------------------------------------- /src/tests/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # MIT License 5 | # 6 | # Copyright (c) 2019-2020 Davidson Francis 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in all 16 | # copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | # 26 | 27 | # Fancy colors =) 28 | RED='\033[0;31m' 29 | GREEN='\033[0;32m' 30 | NC='\033[0m' 31 | 32 | # PBD Folder 33 | PBD_FOLDER=$(readlink -f ../) 34 | 35 | echo -n "Tests (normal + static analysis)..." 36 | 37 | # Run iterative and recursive tests 38 | { 39 | "$PBD_FOLDER"/pbd test func1 > outputs/test_func1_out &&\ 40 | "$PBD_FOLDER"/pbd test func1 -S > outputs/test_func1_out_sa &&\ 41 | "$PBD_FOLDER"/pbd test factorial 42 | } &> /dev/null 43 | 44 | if [ $? -eq 0 ] 45 | then 46 | # Note: 47 | # 48 | # If all tests were able to run and finish without errors, its 'almost' 49 | # sure that the output will be ok too, nevertheless, we need to ensure. 50 | # 51 | # 52 | # 53 | if cmp -s "outputs/test_func1_expected" "outputs/test_func1_out" 54 | then 55 | if ! cmp -s "outputs/test_func1_expected" "outputs/test_func1_out_sa" 56 | then 57 | echo -e " [${RED}NOT PASSED${NC}] (static analysis differ from expected output)" 58 | exit 1 59 | fi 60 | 61 | echo -e " [${GREEN}PASSED${NC}]" 62 | 63 | if [ -x "$(command -v valgrind)" ] 64 | then 65 | echo -n "Valgrind tests..." 66 | 67 | { 68 | valgrind\ 69 | --leak-check=full\ 70 | --suppressions=pbd.supp\ 71 | --errors-for-leak-kinds=all\ 72 | --error-exitcode=1\ 73 | "$PBD_FOLDER"/pbd test func1 &&\ 74 | 75 | valgrind\ 76 | --leak-check=full\ 77 | --suppressions=pbd.supp\ 78 | --errors-for-leak-kinds=all\ 79 | --error-exitcode=1\ 80 | "$PBD_FOLDER"/pbd test func1 -S &&\ 81 | 82 | valgrind\ 83 | --leak-check=full\ 84 | --suppressions=pbd.supp\ 85 | --errors-for-leak-kinds=all\ 86 | --error-exitcode=1\ 87 | "$PBD_FOLDER"/pbd test factorial 88 | } &> /dev/null 89 | 90 | if [ $? -eq 0 ] 91 | then 92 | echo -e " [${GREEN}PASSED${NC}]" 93 | else 94 | echo -e " [${RED}NOT PASSED${NC}] (memory leaks)" 95 | exit 1 96 | fi 97 | fi 98 | else 99 | echo -e " [${RED}NOT PASSED${NC}] (normal analysis differ from expected output)" 100 | exit 1 101 | fi 102 | else 103 | echo -e " [${RED}NOT PASSED${NC}] (execution error)" 104 | exit 1 105 | fi 106 | 107 | # Run static analysis tests 108 | echo -n "Static parsing analysis tests..." 109 | 110 | "$PBD_FOLDER"/pbd test static_analysis_func3 -d -S -D STATIC_ANALYSIS |\ 111 | grep "===static=analysis===" > outputs/test_func3_out_sa 112 | 113 | if [ $? -eq 0 ] 114 | then 115 | if cmp -s "outputs/test_func3_expected_sa" "outputs/test_func3_out_sa" 116 | then 117 | echo -e " [${GREEN}PASSED${NC}]" 118 | fi 119 | else 120 | echo -e " [${RED}NOT PASSED${NC}]" 121 | exit 1 122 | fi 123 | -------------------------------------------------------------------------------- /src/tests/test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2020 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | /* 30 | * == Note == 31 | * I'm assuming that PBD will not be used in *exotic* 32 | * systems, so I will resume all the C types into the 33 | * following sizes below: 34 | */ 35 | int8_t gi8; 36 | uint8_t gu8; 37 | int16_t gi16; 38 | uint16_t gu16; 39 | int32_t gi32; 40 | uint32_t gu32; 41 | int64_t gi64; 42 | uint64_t gu64; 43 | 44 | /* Arrays Test. */ 45 | char array1dim[10] = {0}; 46 | int array10x10[10][10][10]; 47 | 48 | /* Pointer test. */ 49 | int *integer_pointer; 50 | 51 | /* Enums test, */ 52 | enum animals{cat = 1, dog = 1, elephant = 3, monkey = 4}; 53 | enum animals anim_vect[4]; 54 | 55 | /* Stub struct. */ 56 | struct stub_struct 57 | { 58 | int foo; 59 | int bar; 60 | }; 61 | 62 | /*===========================================================================* 63 | * Iterative analysis * 64 | *===========================================================================*/ 65 | 66 | /* 67 | * Changing vars outside the monitored function. 68 | */ 69 | void func2(int *func2_local_argument1) 70 | { 71 | /* Change a global var from a side function. */ 72 | gi64 += 1; 73 | 74 | /* Change a pointer. */ 75 | (*func2_local_argument1)++; 76 | } 77 | 78 | /* 79 | * General tests, with local vars, global, loops 80 | * and blocks. 81 | */ 82 | int func1(int func1_local_argument1) 83 | { 84 | int func1_local_a = 3; 85 | int func1_local_b; 86 | float func1_local_c; 87 | double func1_local_d; 88 | long double func1_local_e; 89 | 90 | /* Enums. */ 91 | anim_vect[0] = cat; 92 | anim_vect[1] = dog + anim_vect[0]; 93 | anim_vect[2] = elephant; 94 | anim_vect[3] = monkey; 95 | 96 | /* Pointer. */ 97 | integer_pointer = (int *) 0xdeadbeeb; 98 | integer_pointer++; 99 | 100 | /* Arrays single dimension test. */ 101 | for (size_t i = 0; i < 10; i++) 102 | array1dim[i] = i+1; 103 | 104 | array1dim[9] += 9; 105 | 106 | /* Fill test. */ 107 | memset(array1dim, 5, sizeof(array1dim)); 108 | 109 | /* Array multiple dimension test. */ 110 | array10x10[5][7][6]++; 111 | 112 | func1_local_b = 5 + func1_local_a; 113 | 114 | /* Change an argument/local var. */ 115 | func1_local_argument1++; 116 | 117 | /* 118 | * If a function changes a local or global 119 | * variable, this change will also be tracked. 120 | */ 121 | func2(&func1_local_b); 122 | 123 | /* Floating-point variables. */ 124 | func1_local_d = 2.03; 125 | func1_local_c = func1_local_d + 0.11; 126 | func1_local_c++; 127 | 128 | /* Long double test. */ 129 | func1_local_e = 1.1234; 130 | func1_local_e++; 131 | 132 | /* 133 | * Variables defined inside a block scope are 134 | * _purposely_ not tracked. 135 | */ 136 | { 137 | int func1_block_a; 138 | int func1_block_b; 139 | 140 | func1_block_a = 42; 141 | func1_block_b = func1_block_a + 1; 142 | func1_block_b++; 143 | } 144 | 145 | /* 146 | * Which is useful for loop indexes: 147 | * If the purpose _is_ to debug the loop index, 148 | * please make it at the function scope. 149 | */ 150 | for (int i = 0; i < 5; i++) 151 | func1_local_d = (i * 5); 152 | 153 | /* Set all global vars. */ 154 | gi8 = INT8_MAX; 155 | gu8 = -1; 156 | gi16 = INT16_MAX; 157 | gu16 = -1; 158 | gi32 = INT32_MAX; 159 | gu32 = -1; 160 | gi64 = INT64_MAX; 161 | gu64 = -1; 162 | 163 | return (0); 164 | } 165 | 166 | /*===========================================================================* 167 | * Static Analysis * 168 | *===========================================================================*/ 169 | 170 | /** 171 | * This *very* ugly routine tests multiples cases that the 172 | * static analysis parser should watch/ignore, ranging from 173 | * trivial use cases to more unusual. 174 | * 175 | * @note This function is not meant to be compiled and/or 176 | * executed, it just serves as a target function to the 177 | * static analyzer. 178 | */ 179 | void static_analysis_func3(int param) 180 | { 181 | ((void)param); 182 | #ifdef STATIC_ANALYSIS 183 | typedef int mytype_t; 184 | 185 | /* Mixed declarations and initializers. */ 186 | int var_a; 187 | mytype_t var_b = 3; 188 | 189 | /* Enumerations. */ 190 | enum animals anim; 191 | anim = cat; 192 | 193 | /* Structs, should not track. */ 194 | struct stub_struct ss; 195 | ss.foo = 1; 196 | 197 | /* 198 | * Scopes: 199 | * - Global 200 | * - Function scope 201 | * - Function outside scope. 202 | */ 203 | var_a = 0; /* Local scope, simplest assignment. */ 204 | gi64 = 0; /* Global scope. */ 205 | param = 0; /* Parameter is equals function scope. */ 206 | { 207 | /* Declaration and assignment outside scope. */ 208 | int var_c = 3; 209 | var_c++; 210 | } 211 | 212 | /* 213 | * Assignment inside declaration initializer. 214 | * var_a++ will be ignored, since ptr_a is enough to track 215 | * the line. If ptr_a is outside the scope, var_a would be 216 | * analyzed. 217 | */ 218 | char *ptr_a = (char *)var_a++; 219 | { 220 | char *ptr_a = (char *)var_a++; 221 | } 222 | 223 | /* Pointer change, content change and multiples assignments. */ 224 | ptr_a++; 225 | *ptr_a = 'F'; 226 | ptr_a[5] = 'O'; 227 | ptr_a[var_a = 3] = 'O'; 228 | 229 | /* Nested scopes/compound statements. */ 230 | { 231 | var_a++; 232 | { 233 | int vet_a[10]; 234 | { 235 | vet_a[var_a + 1] = 2; 236 | } 237 | } 238 | } 239 | 240 | /* Multiples dimensions array. */ 241 | char array_a[10][5][2]; 242 | array_a[2][3][4] = 'B'; 243 | 244 | /* For Loops. */ 245 | for (int i = 0; i < 10; i++) { /* With compound statement. */ 246 | var_a++; 247 | } 248 | for (int i = 0; i < 10; i++) 249 | var_a++; 250 | 251 | for (var_a = 0; var_a < 10;) /* Assign inside init. */ 252 | { int dummy; dummy++; } 253 | for (;var_a = 0;) /* inside condition. */ 254 | { int dummy; dummy++; } 255 | for (; var_a < 10; var_a = var_a + 1) /* inside loop expr. */ 256 | { int dummy; dummy++; } 257 | 258 | /* While. */ 259 | while (var_a < 10) /* Not assign. */ 260 | { int dummy; dummy++; } 261 | while (var_a = 5) /* Inside condition. */ 262 | { int dummy; dummy++; } 263 | while (var_a) { 264 | int dummy = 1; 265 | (var_a < 10) ? var_b++ : 0; /* Ternary operator. */ 266 | } 267 | 268 | /* Do..While. */ 269 | do 270 | { 271 | var_b += 1; 272 | } while (var_a < 10); 273 | 274 | /* Assignment inside condition. */ 275 | do 276 | { 277 | var_b = var_b + 1; 278 | } while (var_a < 10 && var_a++); 279 | 280 | /* Function call. */ 281 | func1(0xbeef); 282 | 283 | /* Switch/case. */ 284 | switch (var_b = 3) /* Inside var. */ 285 | { 286 | case 0: 287 | var_a = 3; 288 | break; 289 | case 1: 290 | func1(0xcafe); 291 | break; 292 | case 2: 293 | { 294 | int var_a; /* Local scope of var_a, should not watch. */ 295 | var_a = 10; 296 | var_a = (char)(var_b++); /* Assign with cast expr. */ 297 | } 298 | break; 299 | default: 300 | /* Comma expr. */ 301 | var_a = 3, var_b = 5; 302 | break; 303 | } 304 | 305 | /* If statements. */ 306 | if (var_a) 307 | { 308 | if (var_b) 309 | var_b = var_a + 1; 310 | else 311 | { 312 | if (gi64 > (var_b = 1)) 313 | gi64++; 314 | } 315 | } 316 | #endif 317 | } 318 | 319 | /*===========================================================================* 320 | * Recursive analysis * 321 | *===========================================================================*/ 322 | 323 | /** 324 | * Factorial function. 325 | * 326 | * @param Current factorial number. 327 | * 328 | * @return Returns the factorial of n. 329 | */ 330 | uint64_t factorial(uint64_t n) 331 | { 332 | uint64_t result; 333 | if(n == 0) 334 | { 335 | result = 1; 336 | return (result); 337 | } 338 | else 339 | { 340 | result = n; 341 | result = n * factorial(n - 1); 342 | return (result); 343 | } 344 | } 345 | 346 | /** 347 | * Entry point 348 | */ 349 | int main() 350 | { 351 | /* Multiples function calls. */ 352 | for (int i = 0; i < 2; i++) 353 | func1(i); 354 | 355 | factorial(10); 356 | 357 | return (0); 358 | } 359 | --------------------------------------------------------------------------------