-
Notifications
You must be signed in to change notification settings - Fork 277
Memory analyzer to take memory snapshots [blocks: #2649, #4438] #4261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4c2f327
6178da3
46ca388
767a385
d07dc8d
4c3967e
c4e560e
6bdd7bf
72809e5
c70540c
856aa5c
9f88304
98816c2
ac4cd65
af27d1f
2fddd15
554a166
6565c4b
9703af1
35abeee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
[CPROVER Manual TOC](../../) | ||
|
||
## Memory Analyzer | ||
|
||
The memory analyzer is a front-end for running and querying GDB in order to | ||
obtain a state of the input program. The GDB is not needed to be executed | ||
directly but is rather used as a back-end for the memory analysis. A common | ||
application would be to obtain a snapshot of a program under analysis at a | ||
particular state of execution. Such a snapshot could be useful on its own: to | ||
query about values of particular variables. Furthermore, since that snapshot is | ||
a state of a valid concrete execution, it can also be used for subsequent | ||
analyses. | ||
|
||
## Usage | ||
|
||
We assume that the user wants to inspect a binary executable compiled with | ||
debugging symbols and a symbol table information understandable by CBMC, e.g. | ||
(having `goto-gcc` on the `PATH`): | ||
|
||
```sh | ||
$ goto-gcc -g input_program.c -o input_program_exe | ||
``` | ||
|
||
Calling `goto-gcc` instead of simply compiling with `gcc` produces an ELF binary | ||
with a goto section that contains the goto model (goto program + symbol table) | ||
[goto-cc-variants](../goto-cc/variants/). | ||
|
||
The memory analyzer supports two workflows to initiate the GDB with user code: | ||
either to run the code from a core-file or up to a break-point. If the user | ||
already has a core-file, they can specify it with the option `--core-file cf`. | ||
If the user knows the point of their program from where they want to run the | ||
analysis, they can specify it with the option `--breakpoint bp`. Only one of | ||
core-file/break-point option can be used. | ||
|
||
The tool also expects a comma-separated list of symbols to be analysed | ||
`--symbols s1, s2, ..`. Given its dependence on GDB, `memory-analyzer` is a | ||
Unix-only tool. The tool calls `gdb` to obtain the snapshot which is why the | ||
`-g` option is necessary for the program symbols to be visible. | ||
|
||
Take for example the following program: | ||
|
||
```C | ||
// main.c | ||
void checkpoint() {} | ||
|
||
int array[] = {1, 2, 3}; | ||
|
||
int main() | ||
{ | ||
array[1] = 4; | ||
|
||
checkpoint(); | ||
|
||
return 0; | ||
} | ||
``` | ||
|
||
Say we are interested in the evaluation of `array` at the call-site of | ||
`checkpoint`. We compile the program with | ||
|
||
```sh | ||
$ goto-gcc -g main.c -o main_exe | ||
``` | ||
|
||
And then we call `memory-analyzer` with: | ||
|
||
```sh | ||
$ memory-analyzer --breakpoint checkpoint --symbols array main_exe | ||
``` | ||
|
||
to obtain as output the human readable list of values for each requested symbol: | ||
|
||
``` | ||
{ | ||
array = { 1, 4, 3 }; | ||
} | ||
``` | ||
|
||
The above option is useful for the user and their preliminary analysis but does | ||
not contain enough information for further computer-based analyses. To that end, | ||
memory analyzer has an option to request the result to be a snapshot of the | ||
whole symbol table `--symtab-snapshot`. Finally, to obtain an output in JSON | ||
format, e.g. for further analyses by `goto-harness` pass the additional option | ||
`--json-ui`. | ||
|
||
```sh | ||
$ memory-analyzer --symtab-snapshot --json-ui \ | ||
--breakpoint checkpoint --symbols array main_exe > snapshot.json | ||
``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the last two paragraphs: how to interpret the output generated (whether JSON or plain text)? I think this needs to be explained here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Explained. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,23 @@ DIRS = cbmc \ | |
symtab2gb \ | ||
# Empty last line | ||
|
||
ifeq ($(OS),Windows_NT) | ||
detected_OS := Windows | ||
else | ||
detected_OS := $(shell sh -c 'uname 2>/dev/null || echo Unknown') | ||
endif | ||
|
||
ifeq ($(detected_OS),Linux) | ||
ifneq ($(WITH_MEMORY_ANALYZER),0) | ||
# only set if it wasn't explicitly unset | ||
WITH_MEMORY_ANALYZER=1 | ||
endif | ||
endif | ||
|
||
ifeq ($(WITH_MEMORY_ANALYZER),1) | ||
DIRS += memory-analyzer | ||
endif | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need to make this conditional? Can't we just make this dependent on the OS/ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tautschnig We could enable the flag by default on Linux if you'd prefer, but the memory analyzer (and its tests!) don't actually work on some platforms (including some linux configurations), so I think just having a single flag saying that you want it is already the "simple" version. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is it that keeps is from working across all Linux configurations? I'm worried that anything that is optional will die at some point, and even when it doesn't people might not give it a whole lot of testing in their local builds. So, if we can, we should just enable it by default. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's slightly exotic, but some Linux systems have Again, I don't mind turning it on by default on Linux as it's going to work most of the time, but I'd also like to keep a user controlled option to turn it off if they don't want it, or forcing it to turn on even if our platform detection logic thinks it wouldn't work. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, so maybe turning it on by default is the best option. And, if possible, I'd prefer to build whenever possible, and only restrict testing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So always build on linux, and test there by default. Unchange on other platforms. Something like this: danpoe@d22cb3c? |
||
|
||
# Run all test directories in sequence | ||
.PHONY: test | ||
test: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
add_test_pl_tests( | ||
"../chain.sh \ | ||
$<TARGET_FILE:memory-analyzer> $<TARGET_FILE_DIR:goto-cc>/goto-gcc") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
default: clean tests.log | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we also have a CMakeLists.txt, please? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added. |
||
|
||
GOTO_GCC_EXE=../../../src/goto-cc/goto-gcc | ||
MEMORY_ANALYZER_EXE=../../../src/memory-analyzer/memory-analyzer | ||
|
||
clean: | ||
find -name '*.exe' -execdir $(RM) '{}' \; | ||
find -name '*.out' -execdir $(RM) '{}' \; | ||
$(RM) tests.log | ||
|
||
test: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The regression test directory also needs to be added to the parent directory's Makefile and CMakeLists.txt. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
@../test.pl -e -p -c "../chain.sh $(MEMORY_ANALYZER_EXE) $(GOTO_GCC_EXE)" | ||
|
||
tests.log: ../test.pl | ||
@../test.pl -e -p -c "../chain.sh $(MEMORY_ANALYZER_EXE) $(GOTO_GCC_EXE)" | ||
|
||
show: | ||
@for dir in *; do \ | ||
if [ -d "$$dir" ]; then \ | ||
vim -o "$$dir/*.c" "$$dir/*.out"; \ | ||
fi; \ | ||
done; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
//Copyright 2018 Author: Malte Mues <[email protected]> | ||
|
||
/// \file | ||
/// This file test array support in the memory-analyzer. | ||
/// A more detailed description of the test idea is in example3.h. | ||
/// setup() prepares the data structure. | ||
/// manipulate_data() is the hook used for the test, | ||
/// where gdb reaches the breakpoint. | ||
/// main() is just the required boilerplate around this methods, | ||
/// to make the compiled result executable. | ||
|
||
#include "arrays.h" | ||
|
||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
void setup() | ||
{ | ||
test_struct = malloc(sizeof(struct a_typet)); | ||
for(int i = 0; i < 10; i++) | ||
{ | ||
test_struct->config[i] = i + 10; | ||
} | ||
for(int i = 0; i < 10; i++) | ||
{ | ||
test_struct->values[i] = 0xf3; | ||
} | ||
for(int i = 10; i < 20; i++) | ||
{ | ||
test_struct->values[i] = 0x3f; | ||
} | ||
for(int i = 20; i < 30; i++) | ||
{ | ||
test_struct->values[i] = 0x01; | ||
} | ||
for(int i = 30; i < 40; i++) | ||
{ | ||
test_struct->values[i] = 0x01; | ||
} | ||
for(int i = 40; i < 50; i++) | ||
{ | ||
test_struct->values[i] = 0xff; | ||
} | ||
for(int i = 50; i < 60; i++) | ||
{ | ||
test_struct->values[i] = 0x00; | ||
} | ||
for(int i = 60; i < 70; i++) | ||
{ | ||
test_struct->values[i] = 0xab; | ||
} | ||
messaget option1 = {.text = "accept"}; | ||
for(int i = 0; i < 4; i++) | ||
{ | ||
char *value = malloc(sizeof(char *)); | ||
sprintf(value, "unique%i", i); | ||
entryt your_options = { | ||
.id = 1, .options[0] = option1, .options[1].text = value}; | ||
your_options.id = i + 12; | ||
test_struct->childs[i].id = your_options.id; | ||
test_struct->childs[i].options[0] = your_options.options[0]; | ||
test_struct->childs[i].options[1].text = your_options.options[1].text; | ||
} | ||
test_struct->initialized = true; | ||
} | ||
|
||
int manipulate_data() | ||
{ | ||
for(int i = 0; i < 4; i++) | ||
{ | ||
free(test_struct->childs[i].options[1].text); | ||
test_struct->childs[i].options[1].text = "decline"; | ||
} | ||
} | ||
|
||
int main() | ||
{ | ||
setup(); | ||
manipulate_data(); | ||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, so GDB never needs to be run directly? The above text made it sound like that would have been necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.