# Yet another stack/session dumper utility
A "simple" library to help analyze issues post-mortem without a debugger attached.

## Usage
### Dumping the process state
You can use `yasdu.dump()` to dump the process state.
Be careful:
  - Dumps like this can contain sensitive data, like passwords, authentication tokens etc.
  **SHARE THEM WITH EXTREME CAUTION.**
  - `yasdu.dump` will not overwrite files and will create `filename.number` files.

```python
import yasdu
yasdu.dump('dump.json', 'Comment here')
# *** Created stack dump in 'dump.json' ***
# => 'dump.json'
# 'dump.json' is actual filename the dump was saved in

yasdu.dump('dump.json', 'Comment here')
# *** Created stack dump in 'dump.json.1' ***
# => 'dump.json.1'
# Since 'dump.json' already exists, the library picked 'dump.json.1'
```

### Hooking exceptions.
The `yasdu.hook()` function will hook `sys.excepthook` and if there's an uncaught
exception killing the program it will dump the process state into `yasdu_autodump_[ISODATETIME].json`
See [hook and crash example](./examples/hook_and_crash.py)

### Loading dumps

#### CLI tool
For the CLI IPython is recommended but not needed.
```
usage: yasdu [-h] -l FILE [-S] [-y]

optional arguments:
  -l FILE, --load FILE  Loads given file and starts a Python interpreter

  -S, --no-sources      Disables reading the sources from disk and showing them, useful if they have changed.
  -y                    Assume yes to all questions
  -h, --help            show this help message and exit
```

Example:
```shell script
$ python examples/hook_and_crash.py
*** Created stack dump in 'yasdu_autodump.json' ***
Traceback (most recent call last):
  File "examples/hook_and_crash.py", line 13, in <module>
    happily_convert_some_values()
  File "examples/hook_and_crash.py", line 8, in happily_convert_some_values
    v2 = int('deadbeef')  # oh noes
ValueError: invalid literal for int() with base 10: 'deadbeef'
```
Let's inspect the automatic dump.

```python
$ yasdu -l yasdu_autodump.json
Loading dump files can allow them to execute code. Do you still want to proceed? [y/N]y
Unpickled 9 objects...
Done loading data.


# exception traceback generated by the program ↓
This is an automatic dump it has been triggered by an exception:
Traceback (most recent call last):
  File "examples/hook_and_crash.py", line 13, in <module>
    happily_convert_some_values()
  File "examples/hook_and_crash.py", line 8, in happily_convert_some_values
    v2 = int('deadbeef')  # oh noes
ValueError: invalid literal for int() with base 10: 'deadbeef'

# stack data saved in the dump and sources loaded from disk
Available frames
  0. examples/hook_and_crash.py:13 happily_convert_some_values() <--  # we're here
  1. examples/hook_and_crash.py:8 v2 = int('deadbeef')  # oh noes
LoadedFrame object is available through the `_lframe` global variable

Using IPython for shell...
# [snip]

[hook_and_crash.py:13 (0)] In [1]: sources()
   import yasdu
   
   some_global_variable = 'testing123'
   
   
   def happily_convert_some_values():
       v1 = int('deadbeef', 16)
       v2 = int('deadbeef')  # oh noes
   
   
   if __name__ == '__main__':
       yasdu.hook()
->     happily_convert_some_values()

[hook_and_crash.py:13 (0)] In [2]: down()
Available frames
  0. examples/hook_and_crash.py:13 happily_convert_some_values()
  1. examples/hook_and_crash.py:8 v2 = int('deadbeef')  # oh noes <--

[hook_and_crash.py:13 (0)] In [3]: sources()
   import yasdu
   
   some_global_variable = 'testing123'
   
   
   def happily_convert_some_values():
       v1 = int('deadbeef', 16)
->     v2 = int('deadbeef')  # oh noes
   
   
   if __name__ == '__main__':
       yasdu.hook()
       happily_convert_some_values()

# variables are loaded from the dump
[hook_and_crash.py:13 (0)] In [4]: v1
Out[4]: 3735928559

[hook_and_crash.py:13 (0)] In [5]: some_global_variable
Out[5]: 'testing123'
```
