Skip to content

Commit 09875c3

Browse files
authored
Merge pull request #4261 from danpoe/feature/memory-analyzer
Memory analyzer to take memory snapshots [blocks: #2649, #4438]
2 parents 2a7d0cd + 35abeee commit 09875c3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+2285
-140
lines changed

.travis.yml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ jobs:
108108
env:
109109
- COMPILER="ccache /usr/bin/g++-5"
110110
- EXTRA_CXXFLAGS="-D_GLIBCXX_DEBUG"
111+
- WITH_MEMORY_ANALYZER=1
111112

112113
# OS X using clang++
113114
- stage: Test different OS/CXX/Flags
@@ -118,7 +119,9 @@ jobs:
118119
before_install:
119120
- HOMEBREW_NO_AUTO_UPDATE=1 brew install ccache parallel gdb
120121
- export PATH=$PATH:/usr/local/opt/ccache/libexec
121-
env: COMPILER="ccache clang++"
122+
env:
123+
- COMPILER="ccache clang++"
124+
- WITH_MEMORY_ANALYZER=0
122125

123126
# Ubuntu Linux with glibc using g++-5, debug mode
124127
- stage: Test different OS/CXX/Flags
@@ -144,6 +147,7 @@ jobs:
144147
env:
145148
- COMPILER="ccache /usr/bin/g++-5"
146149
- EXTRA_CXXFLAGS="-DDEBUG"
150+
- WITH_MEMORY_ANALYZER=1
147151
script: echo "Not running any tests for a debug build."
148152

149153
# Ubuntu Linux with glibc using clang++-7, debug mode, disable USE_DSTRING
@@ -175,6 +179,7 @@ jobs:
175179
- COMPILER="ccache /usr/bin/clang++-7"
176180
- EXTRA_CXXFLAGS="-Qunused-arguments -fcolor-diagnostics -DDEBUG -DUSE_STD_STRING"
177181
- CCACHE_CPP2=yes
182+
- WITH_MEMORY_ANALYZER=1
178183
script: echo "Not running any tests for a debug build."
179184

180185
# cmake build using g++-7, enable NAMED_SUB_IS_FORWARD_LIST
@@ -200,7 +205,7 @@ jobs:
200205
install:
201206
- ccache -z
202207
- ccache --max-size=1G
203-
- cmake -H. -Bbuild '-DCMAKE_BUILD_TYPE=Release' '-DCMAKE_CXX_COMPILER=/usr/bin/g++-7' '-DCMAKE_CXX_FLAGS=-DNAMED_SUB_IS_FORWARD_LIST'
208+
- cmake -H. -Bbuild '-DCMAKE_BUILD_TYPE=Release' '-DCMAKE_CXX_COMPILER=/usr/bin/g++-7' '-DCMAKE_CXX_FLAGS=-DNAMED_SUB_IS_FORWARD_LIST' '-DWITH_MEMORY_ANALYZER=On'
204209
- git submodule update --init --recursive
205210
- cmake --build build -- -j4
206211
script: (cd build; bin/unit "[core][irept]")
@@ -228,7 +233,7 @@ jobs:
228233
install:
229234
- ccache -z
230235
- ccache --max-size=1G
231-
- cmake -H. -Bbuild '-DCMAKE_BUILD_TYPE=Release' '-DCMAKE_CXX_COMPILER=/usr/bin/g++-7' '-DCMAKE_USE_CUDD=true' -DCMAKE_CXX_FLAGS="-DBDD_GUARDS"
236+
- cmake -H. -Bbuild '-DCMAKE_BUILD_TYPE=Release' '-DCMAKE_CXX_COMPILER=/usr/bin/g++-7' '-DCMAKE_USE_CUDD=true' -DCMAKE_CXX_FLAGS="-DBDD_GUARDS" '-DWITH_MEMORY_ANALYZER=On'
232237
- git submodule update --init --recursive
233238
- cmake --build build -- -j4
234239
script: (cd build; ctest -V -L CORE -j2)
@@ -264,7 +269,7 @@ jobs:
264269
install:
265270
- ccache -z
266271
- ccache --max-size=1G
267-
- cmake -H. -Bbuild '-DCMAKE_BUILD_TYPE=Release' '-DCMAKE_CXX_COMPILER=/usr/bin/clang++-7' '-DCMAKE_CXX_FLAGS=-Qunused-arguments'
272+
- cmake -H. -Bbuild '-DCMAKE_BUILD_TYPE=Release' '-DCMAKE_CXX_COMPILER=/usr/bin/clang++-7' '-DCMAKE_CXX_FLAGS=-Qunused-arguments' '-DWITH_MEMORY_ANALYZER=On'
268273
- git submodule update --init --recursive
269274
- cmake --build build -- -j4
270275
script: (cd build; ctest -V -L CORE -j2)
@@ -321,6 +326,7 @@ jobs:
321326
env:
322327
- NAME="COVERITY_SCAN"
323328
- COMPILER="ccache g++"
329+
- WITH_MEMORY_ANALYZER=1
324330
script: echo "This is coverity build. No need for tests."
325331

326332
install:

CMakeLists.txt

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,37 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR
104104
endif()
105105
endif()
106106

107+
function(cprover_default_properties)
108+
set(CBMC_CXX_STANDARD 11)
109+
set(CBMC_CXX_STANDARD_REQUIRED true)
110+
set(CBMC_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY
111+
"Developer ID Application: Daniel Kroening")
112+
113+
set_target_properties(
114+
${ARGN}
115+
PROPERTIES
116+
CXX_STANDARD ${CBMC_CXX_STANDARD}
117+
CXX_STANDARD_REQUIRED ${CBMC_CXX_STANDARD_REQUIRED}
118+
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ${CBMC_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY})
119+
endfunction()
120+
121+
option(WITH_MEMORY_ANALYZER OFF
122+
"build the memory analyzer")
123+
124+
if(CMAKE_SYSTEM_NAME STREQUAL Linux)
125+
set(WITH_MEMORY_ANALYZER_DEFAULT ON)
126+
else()
127+
set(WITH_MEMORY_ANALYZER_DEFAULT OFF)
128+
endif()
129+
130+
option(WITH_MEMORY_ANALYZER ${WITH_MEMORY_ANALYZER_DEFAULT}
131+
"build the memory analyzer")
132+
107133
add_subdirectory(src)
108134
add_subdirectory(regression)
109135
add_subdirectory(unit)
110136

111-
set(CBMC_CXX_STANDARD 11)
112-
set(CBMC_CXX_STANDARD_REQUIRED true)
113-
set(CBMC_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY
114-
"Developer ID Application: Daniel Kroening")
115-
116-
set_target_properties(
137+
cprover_default_properties(
117138
analyses
118139
ansi-c
119140
assembler
@@ -145,13 +166,7 @@ set_target_properties(
145166
testing-utils
146167
unit
147168
util
148-
xml
149-
150-
PROPERTIES
151-
CXX_STANDARD ${CBMC_CXX_STANDARD}
152-
CXX_STANDARD_REQUIRED ${CBMC_CXX_STANDARD_REQUIRED}
153-
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ${CBMC_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY}
154-
)
169+
xml)
155170

156171
option(WITH_JBMC "Build the JBMC Java front-end" ON)
157172
if(WITH_JBMC)

doc/cprover-manual/index.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88

99
[A Short Tutorial](cbmc/tutorial/),
1010
[Loop Unwinding](cbmc/unwinding/),
11-
[Assertion Checking](cbmc/assertions/)
11+
[Assertion Checking](cbmc/assertions/),
12+
[Memory Analyzer](cbmc/memory-analyzer/),
13+
[Program Harness](cbmc/goto-harness/)
1214

1315
## 4. [Test Suite Generation](test-suite/)
1416

doc/cprover-manual/memory-analyzer.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
[CPROVER Manual TOC](../../)
2+
3+
## Memory Analyzer
4+
5+
The memory analyzer is a front-end for running and querying GDB in order to
6+
obtain a state of the input program. The GDB is not needed to be executed
7+
directly but is rather used as a back-end for the memory analysis. A common
8+
application would be to obtain a snapshot of a program under analysis at a
9+
particular state of execution. Such a snapshot could be useful on its own: to
10+
query about values of particular variables. Furthermore, since that snapshot is
11+
a state of a valid concrete execution, it can also be used for subsequent
12+
analyses.
13+
14+
## Usage
15+
16+
We assume that the user wants to inspect a binary executable compiled with
17+
debugging symbols and a symbol table information understandable by CBMC, e.g.
18+
(having `goto-gcc` on the `PATH`):
19+
20+
```sh
21+
$ goto-gcc -g input_program.c -o input_program_exe
22+
```
23+
24+
Calling `goto-gcc` instead of simply compiling with `gcc` produces an ELF binary
25+
with a goto section that contains the goto model (goto program + symbol table)
26+
[goto-cc-variants](../goto-cc/variants/).
27+
28+
The memory analyzer supports two workflows to initiate the GDB with user code:
29+
either to run the code from a core-file or up to a break-point. If the user
30+
already has a core-file, they can specify it with the option `--core-file cf`.
31+
If the user knows the point of their program from where they want to run the
32+
analysis, they can specify it with the option `--breakpoint bp`. Only one of
33+
core-file/break-point option can be used.
34+
35+
The tool also expects a comma-separated list of symbols to be analysed
36+
`--symbols s1, s2, ..`. Given its dependence on GDB, `memory-analyzer` is a
37+
Unix-only tool. The tool calls `gdb` to obtain the snapshot which is why the
38+
`-g` option is necessary for the program symbols to be visible.
39+
40+
Take for example the following program:
41+
42+
```C
43+
// main.c
44+
void checkpoint() {}
45+
46+
int array[] = {1, 2, 3};
47+
48+
int main()
49+
{
50+
array[1] = 4;
51+
52+
checkpoint();
53+
54+
return 0;
55+
}
56+
```
57+
58+
Say we are interested in the evaluation of `array` at the call-site of
59+
`checkpoint`. We compile the program with
60+
61+
```sh
62+
$ goto-gcc -g main.c -o main_exe
63+
```
64+
65+
And then we call `memory-analyzer` with:
66+
67+
```sh
68+
$ memory-analyzer --breakpoint checkpoint --symbols array main_exe
69+
```
70+
71+
to obtain as output the human readable list of values for each requested symbol:
72+
73+
```
74+
{
75+
array = { 1, 4, 3 };
76+
}
77+
```
78+
79+
The above option is useful for the user and their preliminary analysis but does
80+
not contain enough information for further computer-based analyses. To that end,
81+
memory analyzer has an option to request the result to be a snapshot of the
82+
whole symbol table `--symtab-snapshot`. Finally, to obtain an output in JSON
83+
format, e.g. for further analyses by `goto-harness` pass the additional option
84+
`--json-ui`.
85+
86+
```sh
87+
$ memory-analyzer --symtab-snapshot --json-ui \
88+
--breakpoint checkpoint --symbols array main_exe > snapshot.json
89+
```

jbmc/CMakeLists.txt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ add_custom_target(java-models-library ALL
88
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib/java-models-library
99
)
1010

11-
set_target_properties(
11+
cprover_default_properties(
1212
java_bytecode
1313
java-models-library
1414
jbmc
@@ -20,9 +20,4 @@ set_target_properties(
2020
java-testing-utils
2121
java-unit
2222
miniz
23-
24-
PROPERTIES
25-
CXX_STANDARD ${CBMC_CXX_STANDARD}
26-
CXX_STANDARD_REQUIRED ${CBMC_CXX_STANDARD_REQUIRED}
27-
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ${CBMC_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY}
2823
)

regression/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,7 @@ add_subdirectory(goto-harness)
5353
add_subdirectory(goto-cc-file-local)
5454
add_subdirectory(linking-goto-binaries)
5555
add_subdirectory(symtab2gb)
56+
57+
if(WITH_MEMORY_ANALYZER)
58+
add_subdirectory(memory-analyzer)
59+
endif()

regression/Makefile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ DIRS = cbmc \
3030
symtab2gb \
3131
# Empty last line
3232

33+
ifeq ($(OS),Windows_NT)
34+
detected_OS := Windows
35+
else
36+
detected_OS := $(shell sh -c 'uname 2>/dev/null || echo Unknown')
37+
endif
38+
39+
ifeq ($(detected_OS),Linux)
40+
ifneq ($(WITH_MEMORY_ANALYZER),0)
41+
# only set if it wasn't explicitly unset
42+
WITH_MEMORY_ANALYZER=1
43+
endif
44+
endif
45+
46+
ifeq ($(WITH_MEMORY_ANALYZER),1)
47+
DIRS += memory-analyzer
48+
endif
49+
3350
# Run all test directories in sequence
3451
.PHONY: test
3552
test:
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
add_test_pl_tests(
2+
"../chain.sh \
3+
$<TARGET_FILE:memory-analyzer> $<TARGET_FILE_DIR:goto-cc>/goto-gcc")

regression/memory-analyzer/Makefile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
default: clean tests.log
2+
3+
GOTO_GCC_EXE=../../../src/goto-cc/goto-gcc
4+
MEMORY_ANALYZER_EXE=../../../src/memory-analyzer/memory-analyzer
5+
6+
clean:
7+
find -name '*.exe' -execdir $(RM) '{}' \;
8+
find -name '*.out' -execdir $(RM) '{}' \;
9+
$(RM) tests.log
10+
11+
test:
12+
@../test.pl -e -p -c "../chain.sh $(MEMORY_ANALYZER_EXE) $(GOTO_GCC_EXE)"
13+
14+
tests.log: ../test.pl
15+
@../test.pl -e -p -c "../chain.sh $(MEMORY_ANALYZER_EXE) $(GOTO_GCC_EXE)"
16+
17+
show:
18+
@for dir in *; do \
19+
if [ -d "$$dir" ]; then \
20+
vim -o "$$dir/*.c" "$$dir/*.out"; \
21+
fi; \
22+
done;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//Copyright 2018 Author: Malte Mues <[email protected]>
2+
3+
/// \file
4+
/// This file test array support in the memory-analyzer.
5+
/// A more detailed description of the test idea is in example3.h.
6+
/// setup() prepares the data structure.
7+
/// manipulate_data() is the hook used for the test,
8+
/// where gdb reaches the breakpoint.
9+
/// main() is just the required boilerplate around this methods,
10+
/// to make the compiled result executable.
11+
12+
#include "arrays.h"
13+
14+
#include <stdio.h>
15+
#include <stdlib.h>
16+
#include <string.h>
17+
18+
void setup()
19+
{
20+
test_struct = malloc(sizeof(struct a_typet));
21+
for(int i = 0; i < 10; i++)
22+
{
23+
test_struct->config[i] = i + 10;
24+
}
25+
for(int i = 0; i < 10; i++)
26+
{
27+
test_struct->values[i] = 0xf3;
28+
}
29+
for(int i = 10; i < 20; i++)
30+
{
31+
test_struct->values[i] = 0x3f;
32+
}
33+
for(int i = 20; i < 30; i++)
34+
{
35+
test_struct->values[i] = 0x01;
36+
}
37+
for(int i = 30; i < 40; i++)
38+
{
39+
test_struct->values[i] = 0x01;
40+
}
41+
for(int i = 40; i < 50; i++)
42+
{
43+
test_struct->values[i] = 0xff;
44+
}
45+
for(int i = 50; i < 60; i++)
46+
{
47+
test_struct->values[i] = 0x00;
48+
}
49+
for(int i = 60; i < 70; i++)
50+
{
51+
test_struct->values[i] = 0xab;
52+
}
53+
messaget option1 = {.text = "accept"};
54+
for(int i = 0; i < 4; i++)
55+
{
56+
char *value = malloc(sizeof(char *));
57+
sprintf(value, "unique%i", i);
58+
entryt your_options = {
59+
.id = 1, .options[0] = option1, .options[1].text = value};
60+
your_options.id = i + 12;
61+
test_struct->childs[i].id = your_options.id;
62+
test_struct->childs[i].options[0] = your_options.options[0];
63+
test_struct->childs[i].options[1].text = your_options.options[1].text;
64+
}
65+
test_struct->initialized = true;
66+
}
67+
68+
int manipulate_data()
69+
{
70+
for(int i = 0; i < 4; i++)
71+
{
72+
free(test_struct->childs[i].options[1].text);
73+
test_struct->childs[i].options[1].text = "decline";
74+
}
75+
}
76+
77+
int main()
78+
{
79+
setup();
80+
manipulate_data();
81+
return 0;
82+
}

0 commit comments

Comments
 (0)