bugstalker 0.2.6

BugStalker is a modern and lightweight debugger for rust applications.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
# BugStalker

> Modern debugger for Linux x86-64. Written in Rust for Rust programs.

![debugger-demo](doc/demo.gif)

---

# Table of Contents

- [BugStalker]#bugstalker
    * [Supported rustc versions]#supported-rustc-versions
    * [Features]#features
    * [Installation]#installation
    * [Start debugger session]#start-debugger-session
    * [Help]#help
    * [Start and restart]#start-and-restart
    * [Stopping and continuing]#stopping-and-continuing
        * [Breakpoints]#breakpoints
        * [Watchpoints]#watchpoints
        * [Steps]#steps
        * [Signals]#signals
    * [Examining the stack]#examining-the-stack
    * [Examining source files]#examining-source-files
    * [Examining data]#examining-data
    * [Other commands]#other-commands
    * [Tui interface]#tui-interface
        * [Configuration]#configuration
    * [Oracles]#oracles
- [Contributing]#contributing

---

## Supported rustc versions

- 1.75
- 1.76
- 1.77
- 1.78
- 1.79
- 1.80
- 1.81
- 1.82
- 1.83
- 1.84

---

## Features

* written in rust for rust language with simplicity as a priority goal
* [breakpoints, steps, signals, watchpoints]#stopping-and-continuing
* multithreaded application support
* [data query expressions]#examining-data
* support for a rust type system (collections, smart pointers, thread locals and
  many others), not only for printing but also for interaction
* two ui types: console and [tui]#tui-interface, switch available at any
  moment
* [oracle]#oracles as an extension mechanism
* builtin [tokio oracle]#oracles -
  like [tokio_console]https://github.com/tokio-rs/console but there is no need
  to make changes to the source codes
* and much more!

---

## Installation

First check if the necessary dependencies
(`pkg-config` and `libunwind-dev`) are installed:

For example, ubuntu/debian:

```shell
apt install pkg-config libunwind-dev
```

For example, fedora:

```shell
dnf install pkg-config libunwind-devel
```

Now install debugger:

```shell
cargo install bugstalker
```

That's all, `bs` command is available now!

<details>
  <summary>Problem with libunwind?</summary>
If you have any issues with `libunwind`, you can try to install `bs` with
native unwinder 
(currently, I don't recommend this method because libunwind is better :))

```shell
cargo install bugstalker --no-default-features
```

</details>

### Distro Packages

<details>
  <summary>Packaging status</summary>

[![Packaging status](https://repology.org/badge/vertical-allrepos/bugstalker.svg)](https://repology.org/project/bugstalker/versions)

</details>

#### Arch Linux

```shell
pacman -S bugstalker
```

#### Nix package manager

There's flake which you can use to start using it.
Just [enable flakes](https://wiki.nixos.org/wiki/Flakes#Enable_flakes_temporarily)
then you're able to use it with:

```
nix run github:godzie44/BugStalker
```

`bugstalker` also provides a package which you can include to your NixOS config.
For example:

<details>

```nix
{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    bugstalker.url = "github:godzie44/BugStalker";
  };

  outpus = {nixpkgs, bugstalker, ...}: {
    nixosConfigurations.your_hostname = nixpkgs.lib.nixosSystem {
      modules = [
        ({...}: {
          environment.systemPackages = [
            # assuming your system runs on a x86-64 cpu
            bugstalker.packages."x86_64-linux".default
          ];
        })
      ];
    };
  };
}
```

</details>

##### Home-Manager

There's a home-manager module which adds `programs.bugstalker` to your home-manager config.
You can add it by doing the following:

<details>

```nix
{
  description = "NixOS configuration";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    home-manager.url = "github:nix-community/home-manager";
    home-manager.inputs.nixpkgs.follows = "nixpkgs";
    bugstalker.url = "github:godzie44/BugStalker";
  };

  outputs = inputs@{ nixpkgs, home-manager, bugstalker, ... }: {
    nixosConfigurations = {
      hostname = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules = [
          ./configuration.nix
          home-manager.nixosModules.home-manager
          {
            home-manager.sharedModules = [
              bugstalker.homeManagerModules.default
              ({...}: {
                programs.bugstalker = {
                  enable = true;
                  # the content of `keymap.toml`
                  keymap = {
                    common = {
                      up = ["k"];
                    }
                  };
                };
              })
            ];
          }
        ];
      };
    };
  };
}
```

</details>

---

## Start debugger session

To start with program from binary file use:

```shell
bs my_cool_program
```

Or with arguments:

```shell
bs my_cool_program -- --arg1 val1 --arg2 val2
```

Or attach to program by its pid:

```shell
bs -p 123
```

## Help

Print `help` for view all available commands.

## Start and restart

[demo](https://github.com/godzie44/BugStalker/blob/master/doc/demo_run.gif)

- `run` - start or restart a program (alias: `r`)

## Stopping and continuing

The Debugger stops your program when breakpoints are hit,
or after watchpoint are hit,
or after steps commands,
or when the OS signal is coming.
BugStalker always stops the whole program, meaning that all threads are stopped.
Thread witch initiated a stop become a current selected thread.

### Continue execution

[demo](https://github.com/godzie44/BugStalker/blob/master/doc/demo_cont.gif)

- `continue` - resume a stopped program

### Breakpoints

[demo](https://github.com/godzie44/BugStalker/blob/master/doc/demo_brkpt.gif)

- `break {file}:{line}` - set breakpoint at line (alias: `b {file}:{line}`)
- `break {function name}` - set breakpoint at start of the function (
  alias: `b {function_name}`)
- `break {instruction address}` - set breakpoint at instruction (
  alias: `b {instruction address}`)
- `break remove {number}` - remove breakpoint by its number (
  alias: `b r {number}`)
- `break remove {file}:{line}` - remove breakpoint at line (
  alias: `b r {file}:{line}`)
- `break remove {function name}` - remove breakpoint at start of the function (
  alias: `b r {function name}`)
- `break info` - print all breakpoints

### Watchpoints

[demo](https://github.com/godzie44/BugStalker/blob/master/doc/demo_watch.gif)

Watchpoint is a "data breakpoint".
This means that the program stops when the variable (or expression, or just raw memory region)
observed by watchpoint is changed.
Currently, watchpoints feature based on `x86-64` hardware breakpoints.
Therefore, there are two limitations:

- only 4 watchpoints are possible at one time
- watchpoint "observe" memory region of 1/2/4/8 bytes size

You can set watchpoint at variables (global or locals), or at expression based on variables.
Watchpoints for local variables will be removed automatically, when variable out of scope.
If watchpoint observes a global variable, then it will live as long as the debugger is running.

Lets look at examples:

- `watch my_var` - stop when variable value is rewriting (alias: `w my_var`)
- `watch +rw my_var` - stop when variable value is reading or rewriting
- `watch my_vector[0]` - stop when first vector element is rewriting
- `watch (~my_vector).len` - stop when vector length is changed
- `watch 0x100:4` - stop when writing to memory region [0x100:0x103]

### Steps

[demo](https://github.com/godzie44/BugStalker/blob/master/doc/demo_step.gif)

- `stepi` - step a single instruction
- `step` - step a program until it reaches a different source line (
  alias: `stepinto`)
- `next` - step a program, stepping over subroutine (function) calls (
  alias: `stepover`)
- `finish` - execute a program until selected stack frame returns (
  alias: `stepout`)

### Signals

[demo](https://github.com/godzie44/BugStalker/blob/master/doc/demo_signal.gif)

`BugStalker` will catch signals sent from OS to debugee program and stop execution.
For example, try to send SIGINT (ctrl+c) to the debugee program to stop it.

### Change current selected thread

[demo](https://github.com/godzie44/BugStalker/blob/master/doc/demo_thread.gif)

- `thread info` - print list with information about threads
- `thread current` - prints current selected thread
- `thread switch {number}` - switch selected thread

## Examining the stack

When your program has stopped,
the first thing you need to know is where it stopped and how it got there.

Each time your program performs a function call,
the information about where in your program the call was made from is saved in a
block of data
called a stack frame.
The frame also contains the arguments of the call and the local variables of the
function
that was called.
All the stack frames are allocated in a region of memory called the call stack.

### Stack frames

The call stack is divided up into contiguous pieces called stack frames.
Each frame is the data associated with one call to one function.
The frame contains the arguments given to the function,
the function's local variables,
and the address at which the function is executed.

### Backtrace

[demo](https://github.com/godzie44/BugStalker/blob/master/doc/demo_bt.gif)

- `backtrace` - print backtrace of current stopped thread (alias: `bt`).
  Backtrace contains information about thread
  (number, pid, address of instruction where thread stopped)
  and all frames starting with the currently executing frame (frame zero),
  followed by its caller (frame one), and on up the stack.
- `backtrace all` - print backtraces of all active threads (alias: `bt all`).

### Select a frame

[demo](https://github.com/godzie44/BugStalker/blob/master/doc/demo_frame.gif)

Most commands
for examining the stack and other data in your program works
on whichever stack frame is selected at the moment.

- `frame info` - print information about current selected frame.
- `frame switch {num}` - change current selected frame.

## Examining source files

[demo](https://github.com/godzie44/BugStalker/blob/master/doc/demo_source.gif)

BugStalker can print parts of your program's source code.
When your program stops,
the debugger spontaneously prints the line where it stopped.
There is `source` commands for print more.

- `source fn` - print current selected function
- `source {num}` - print lines range [current_line-num; current_line+num]
- `source asm` - print assembly representation of current selected function

## Examining data

[demo](https://github.com/godzie44/BugStalker/blob/master/doc/demo_data.gif)

Of course, you need a way to examine data of your program.

- `var {expression}|locals` command for print local and global variables
- `arg {expression}|all` command for print a function arguments

These commands accept expressions as input or have a special mode
(`var locals` print all local variables, `args all` print all arguments).

### Expression

BugStalker has a special syntax for explore program data.
You can dereference references, get structure fields,
slice arrays or get elements from vectors by its index (and much more!).

Operator available in expressions:

- select variable by its name (ex. `var a`)
- dereference pointers/references/smart pointers (ex. `var *ref_to_a`)
- take a structure field (ex. `var some_struct.some_field`)
- take an element by index or key from arrays, slices, vectors, hashmaps (
  ex. `var arr[1]` or even `var hm[{a: 1, b: 2}]`)
- slice arrays, vectors, slices (ex. `var some_vector[1..3]`
  or `var some_vector[1..]`)
- cast constant address to a pointer of a concrete type (
  ex. `var (*mut SomeType)0x123AABCD`)
- take address (ex. `var &some_struct.some_field`)
- show canonic representation (for example, show vector header instead of vector data `var ~myvec`)
- parentheses for control an operator execution ordering

Write expressions is simple, and you can do it right now!
Some examples:

- `var *some_variable` - dereference and print value of `some_variable`
- `var hm[{a: 1, b: *}]` - print value from hashmap corresponding to the key.
  Literal `{a: 1, b: *}` matches to any structure with field `a` equals to 1 and field `b` equals to any value
- `var some_array[0][2..5]` - print three elements, starts from index 2 from
  zero element of `some_array`
- `var *some_array[0]` - print dereferenced value of `some_array[0]`
- `var &some_array[0]` - print address of `some_array[0]`
- `var (~some_vec).len` - print len field from vector header
- `var (*some_array)[0]` - print a zero element of `*some_array`
- `var *(*(var1.field1)).field2[1][2]` - print dereferenced value of element at
  index 2 in
  element at index 1 at field `field2` in dereferenced value of field `field1`
  at variable var1 🤡

## Other commands

Of course, the debugger provides many more commands:

- `symbol {name or regex}` - print symbol kind and address
- `memory read {addr}` - read debugged program memory (alias: `mem read`)
- `memory write {addr} {value}` - write into debugged program memory (
  alias: `mem write`)
- `register read {reg_name}` - print value of register by name (x86_64 register
  name in lowercase) (alias: `reg read`)
- `register write {reg_name} {value}` - set new value to register by name (
  alias: `reg write`)
- `register info` - print list of registers with it values (alias: `reg info`)
- `sharedlib info` - show list of shared libraries
- `quit` - exit the BugStalker (alias: `q`)

## Tui interface

[demo](https://github.com/godzie44/BugStalker/blob/master/doc/demo_tui.gif)

One of the most funny BugStalker features is switching between old school
terminal interface and pretty tui at any moment.

- `tui` - switch too terminal ui (in tui use `Esc` for switch back)

### Configuration

There is a `keymap.toml` file with tui keybindings configuration.
You can find the default configuration files
at https://github.com/godzie44/BugStalker/tree/master/src/ui/tui/config/preset/keymap.toml.

To override any of the defaults, begin by creating the corresponding file (from the file linked above) to:
`~/.config/bs/keymap.toml`.
You can change keybindings configuration file by exporting the `KEYMAP_FILE` environment variable.

## Oracles

[demo console](https://github.com/godzie44/BugStalker/blob/master/doc/demo_oracle.gif)

[demo tui](https://github.com/godzie44/BugStalker/blob/master/doc/demo_oracle_tui.gif)

Oracle is a module that expands the capabilities of the debugger.
Oracles can monitor the internal state of a program
to display interesting information.
For example, tokio oracle is able
to provide information about tokio runtime during program debugging without the
need
to change the source code.
You must run the debugger with enabled oracle, for example, for tokio oracle:

```bash
bs --oracle tokio ...
```

Then use `oracle` command for view oracle information:

- `oracle {oracle name} {subcommands}` - run oracle (ex. `oracle tokio`)

Oracles also available in tui.
Currently, there is only one builtin oracle - tokio oracle.

## Contributing

Feel free to suggest changes, ask a question or implement a new feature.
Any contributions are very welcome.

[How to contribute](https://github.com/godzie44/BugStalker/blob/master/CONTRIBUTING.md).