aboutsummaryrefslogtreecommitdiff
path: root/pathologist/src/pathologist/pathologist.c
diff options
context:
space:
mode:
Diffstat (limited to 'pathologist/src/pathologist/pathologist.c')
-rw-r--r--pathologist/src/pathologist/pathologist.c357
1 files changed, 357 insertions, 0 deletions
diff --git a/pathologist/src/pathologist/pathologist.c b/pathologist/src/pathologist/pathologist.c
new file mode 100644
index 0000000..305c3ce
--- /dev/null
+++ b/pathologist/src/pathologist/pathologist.c
@@ -0,0 +1,357 @@
1/*
2 This file is part of GNUnet.
3 (C) 2010, 2011 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file pathologist/pathologist.c
23 * @brief Monkey Pathologist: automated debugging tool
24 */
25
26#include <stdio.h>
27#include <string.h>
28#include <unistd.h>
29#include <sys/stat.h>
30#include "monkey_common.h"
31#include "action.h"
32#include "monkey_getopt_lib.h"
33
34static char *dumpFileName;
35static char *binaryName;
36static char *command;
37static char *binaryArgs;
38static char *emailAddress;
39static char *edbFilePath;
40static char *gdbBinaryPath;
41static char *valgrindBinaryPath;
42static char *inspectExpression;
43static char *inspectFunction;
44static int scopeDepth;
45static int reverseExecutionAllowed = MONKEY_NO;
46static int ret = 0;
47
48static int checkFileExistence(const char *filePath) {
49 struct stat statBuff;
50 if (0 == stat(filePath, &statBuff)) {
51 return MONKEY_YES;
52 }
53 return MONKEY_NO;
54}
55
56
57static int checkELF(const char *binaryPath) {
58 int ch;
59 const char ELF[5] = {0x7f, 'E', 'L', 'F', '\0'}; // ELF magic number
60 char inputArr[5];
61 FILE *filePtr;
62 filePtr = fopen(binaryPath, "r");
63 int i;
64
65 for (i = 0; i < 4; i++) {
66 ch = fgetc(filePtr);
67 if (EOF == ch) {
68 return MONKEY_NO;
69 }
70 inputArr[i] = ch;
71 }
72 inputArr[4] = '\0';
73 if (strcmp(ELF, inputArr) == 0) {
74 return MONKEY_YES;
75 }
76 return MONKEY_NO;
77}
78/**
79 * Main function that will launch Pathologist's action api.
80*/
81static void run()
82{
83 int result;
84 struct MONKEY_ACTION_Context *cntxt;
85
86 /* Check files existence */
87 if (MONKEY_NO == checkFileExistence(binaryName)) {
88 fprintf(stderr, "Error: Target binary file does not exist.\n");
89 ret = 1;
90 return;
91 }
92
93 if (MONKEY_NO == checkFileExistence(edbFilePath)) {
94 fprintf(stderr, "Error: Expression database file does not exist.\n");
95 ret = 1;
96 return;
97 }
98
99 if (NULL != gdbBinaryPath &&
100 MONKEY_NO == checkFileExistence(gdbBinaryPath))
101 {
102 fprintf(stderr, "Error: gdb binary file does not exist.\n");
103 ret = 1;
104 return;
105
106 }
107
108 if (NULL != valgrindBinaryPath &&
109 MONKEY_NO == checkFileExistence(valgrindBinaryPath))
110 {
111 fprintf(stderr, "Error: valgrind binary file does not exist.\n");
112 ret = 1;
113 return;
114 }
115
116 /* Check if the target program is and ELF binary */
117 if (MONKEY_NO == checkELF(binaryName)) {
118 FILE* targetProgramPipe;
119 char buffer[128];
120 ret = 1;
121
122 fprintf(stderr, "Target program is not an ELF. Pathologist will not debug the program.\n");
123 fprintf(stderr, "Pathologist will attempt to run the program normally.\n");
124
125 targetProgramPipe = popen(command, "r");
126 if (NULL == targetProgramPipe) {
127 fprintf(stderr, "Target is not a binary.\n");
128 return;
129 }
130 while(!feof(targetProgramPipe)) {
131 if (NULL != fgets(buffer, 128, targetProgramPipe)) {
132 fputs(buffer, stderr);
133 }
134 }
135 pclose(targetProgramPipe);
136 return;
137 }
138
139 if (NULL == edbFilePath)
140 {
141 /* lookup a file named test.db in the working directory */
142 struct stat buf;
143 if (0 != stat ("test.db", &buf))
144 {
145 fprintf( stderr,
146 "Expression Database file was not provided as and argument, "
147 "and there is no file named test.db in pathologist's working directory!\n");
148 MONKEY_break (0);
149 ret = 1;
150 return;
151 }
152 }
153
154 if (NULL == dumpFileName)
155 {
156 /* if no file name provided for the output report, the default report name will be binaryName_PIDpid */
157 const char *bn = strrchr(binaryName, '/');
158 if (NULL == bn) {
159 bn = binaryName;
160 } else {
161 bn++;
162 }
163 MONKEY_asprintf(&dumpFileName, "%s_PID%d", bn, getpid());
164 }
165
166 /* Initialize context for the Action API */
167 cntxt = MONKEY_malloc (sizeof (struct MONKEY_ACTION_Context));
168 cntxt->email_address = emailAddress;
169 cntxt->binary_name = binaryName;
170 cntxt->binaryArgs = binaryArgs;
171 cntxt->expression_database_path = edbFilePath;
172 cntxt->gdb_binary_path = gdbBinaryPath;
173 cntxt->valgrind_binary_path = valgrindBinaryPath;
174 cntxt->inspect_expression = inspectExpression;
175 cntxt->inspect_function = inspectFunction;
176 cntxt->scope_depth = scopeDepth;
177 cntxt->bug_detected = NO_BUG_DETECTED;
178 cntxt->has_null = MONKEY_NO;
179 cntxt->xml_report_node = NULL;
180 cntxt->run_reverse = reverseExecutionAllowed;
181 cntxt->gdb_connected = MONKEY_NO;
182
183 result = MONKEY_ACTION_rerun_with_gdb (cntxt);
184 int retVal;
185 switch (result)
186 {
187 case MONKEY_NO:
188 case GDB_STATE_ERROR:
189 fprintf (stderr, "Error in using gdb. Pathologist will exit now!\n");
190 ret = 1;
191 break;
192 case GDB_STATE_EXIT_NORMALLY:
193 fprintf (stderr, "Debug with gdb, program exited normally!\n");
194 break;
195 case GDB_STATE_STOPPED:
196 //categorize bug
197 if ((strcasecmp(cntxt->gdb_stop_reason->signal_meaning, "Segmentation fault") == 0)
198 || (strcasecmp(cntxt->gdb_stop_reason->signal_meaning, "Signal 0") == 0))
199 cntxt->bug_detected = MONKEY_ACTION_is_Nullpointer(cntxt) ? BUG_NULL_POINTER : BUG_BAD_MEM_ACCESS;
200 else if (strcasecmp(cntxt->gdb_stop_reason->signal_meaning, "Aborted") == 0)
201 cntxt->bug_detected = BUG_ABORT;
202 else if (strcasecmp(cntxt->gdb_stop_reason->signal_meaning, "Arithmetic exception") == 0)
203 cntxt->bug_detected = BUG_ARITHMETIC;
204 else if (strcasecmp(cntxt->gdb_stop_reason->signal_name, "SIGBUS") == 0)
205 cntxt->bug_detected = BUG_SIG_BUS;
206
207 // get relevant expressions
208 retVal = MONKEY_ACTION_inspect_expression_database (cntxt);
209
210 if (MONKEY_NO == retVal)
211 {
212 fprintf( stderr,
213 "Error in using the expression database. Pathologist will exit now!\n");
214 ret = 1;
215 break;
216 }
217 else if (BUG_BAD_MEM_ACCESS == cntxt->bug_detected
218 || BUG_SIG_BUS == cntxt->bug_detected)
219 {
220 /* launch valgrind */
221 retVal = MONKEY_ACTION_rerun_with_valgrind (cntxt);
222 if (MONKEY_NO == retVal)
223 fprintf( stderr, "Error using Valgrind! Pathologist will continue debugging!\n");
224 }
225
226 /* Start backtracking if enabled by user */
227 if (cntxt->run_reverse) {
228 retVal = MONKEY_ACTION_start_reverse_execution(cntxt);
229 if (MONKEY_NO == retVal)
230 fprintf(stderr, "Error during Backtracking! Pathologist will continue debugging with backtracking.\n");
231 }
232
233
234 // REPORTING
235 if (MONKEY_OK != MONKEY_ACTION_format_report_xml (cntxt))
236 {
237 fprintf( stderr,
238 "Error in generating debug report!\n");
239 ret = 1;
240 }
241 if (emailAddress != NULL)
242 {
243 if (MONKEY_OK != MONKEY_ACTION_report_email (cntxt, dumpFileName))
244 {
245 fprintf( stderr, "Error sending email!\n");
246 ret = 1;
247 }
248 }
249 if (dumpFileName != NULL)
250 {
251 if (MONKEY_OK !=
252 MONKEY_ACTION_report_file (cntxt, dumpFileName, MONKEY_YES))
253 {
254 fprintf( stderr,
255 "Error in saving debug file!\n");
256 ret = 1;
257 }
258 }
259 break;
260 default:
261 break;
262 }
263
264MONKEY_ACTION_delete_context (cntxt);
265}
266
267
268int freePathologistArgs()
269{
270 if (NULL != dumpFileName)
271 MONKEY_free(dumpFileName);
272 if (NULL != emailAddress)
273 MONKEY_free(emailAddress);
274 if (NULL != edbFilePath)
275 MONKEY_free(edbFilePath);
276 if (NULL != gdbBinaryPath)
277 MONKEY_free(gdbBinaryPath);
278 if (NULL != valgrindBinaryPath)
279 MONKEY_free(valgrindBinaryPath);
280 if (NULL != inspectExpression)
281 MONKEY_free(inspectExpression);
282 if (NULL != inspectFunction)
283 MONKEY_free(inspectFunction);
284
285 return MONKEY_OK;
286}
287
288
289
290const char * buildBinaryArgs(int argc, char **argv) {
291 char * res = MONKEY_strdup("");
292 char * tmp;
293 int i;
294 for (i = 0; i < argc; i++) {
295 MONKEY_asprintf(&tmp, "%s %s", res, argv[i]);
296 MONKEY_free(res);
297 res = tmp;
298 }
299
300 return res;
301}
302
303int
304main (int argc, char *argv[])
305{
306 static const struct MONKEY_GETOPT_CommandLineOption options[] = {
307 {'d', "database", "FILENAME", "the expression database to use, generated by seaspider",
308 1, &MONKEY_GETOPT_set_string, &edbFilePath},
309 {'f', "function", "FUNCTIONNAME", "the name of the function, in which pathologist will set a breakpoint",
310 1, &MONKEY_GETOPT_set_string, &inspectFunction},
311 {'g', "gdb", "FILENAME", "path to gdb binary to use. If not passed, pathologist will set it to /usr/bin/gdb",
312 1, &MONKEY_GETOPT_set_string, &gdbBinaryPath},
313 {'h', "help", NULL, "shows this help",
314 0, &MONKEY_GETOPT_format_help_, "Monkey Pathologist: Automatically debug a service"},
315 {'i', "inspect", "EXPRESSIONNAME", "expression to inspect in the function specified after argument f",
316 1, &MONKEY_GETOPT_set_string, &inspectExpression},
317 {'l', "valgrind", "FILENAME", "path to valgrind binary. If not passed, pathologist will set it to /usr/bin/valgrind",
318 1, &MONKEY_GETOPT_set_string, &valgrindBinaryPath},
319 {'m', "mail", "MAILADDRESS", "if specified, pathologist will send the bugreport to the given address",
320 1, &MONKEY_GETOPT_set_string, &emailAddress},
321 {'o', "report", "FILENAME", "saves the xml formated report to the given file",
322 1, &MONKEY_GETOPT_set_string, &dumpFileName},
323 {'p', "depth", "UINT", "scope depth. How many scopes should pathologist lookup expression values outer than the scope in which the problem occurs. Default is 0 which means, pathologist is restricted to the scope of the expression in which the debugged program stopped" ,
324 1, &MONKEY_GETOPT_set_uint, &scopeDepth},
325 {'r', "reverse", NULL, "enables reverse execution",
326 0, &MONKEY_GETOPT_set_one, &reverseExecutionAllowed},
327 {'v', "version", NULL, "print the version number",
328 0, &MONKEY_GETOPT_print_version_, "0.1 alpha"},
329 MONKEY_GETOPT_OPTION_END
330 };
331
332 if (-1 == (ret = MONKEY_GETOPT_run ("pathologist -d DATABASEFILE [options] [--] BINARY", options, (unsigned int) argc, argv)))
333 return -1;
334
335 // Check for required arguments
336 if (argc <= ret)
337 printf("Error: missing argument: path to the binary file.\n");
338 if (NULL == edbFilePath)
339 printf("Error: missing argument: path to expression database file.\n");
340 if (argc <= ret || NULL == edbFilePath)
341 exit(EXIT_FAILURE);
342
343 /* All remaining args are considered program arguments */
344 binaryName = argv[ret];
345 command = (char *)buildBinaryArgs(argc - ret, &argv[ret]) + 1;
346 binaryArgs = (char *)buildBinaryArgs(argc - ret - 1, &argv[ret + 1]);
347
348 printf("\n%s\n%s\n%s\n\n", binaryName, command, binaryArgs);
349
350 /* Run Pathologist */
351 run();
352
353 /* clean up */
354 freePathologistArgs();
355
356 return ret;
357}