WiP
[oweals/gnunet.git] / src / monkey / action_api.c
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 monkey/action_api.c
23  * @brief Monkey API for actions taken by Monkey while debugging
24  */
25
26 #include "platform.h"
27 #include "gnunet_common.h"
28 #include "gnunet_monkey_action.h"
29 #include "gnunet_monkey_edb.h"
30 #include "gnunet_container_lib.h"
31 #include <libesmtp.h>
32
33 extern void sendMail (const char *messageContents, const char *emailAddress);
34
35
36 static int async_c=0;
37 static struct Expression *expressionListHead = NULL;
38 static struct Expression *expressionListTail = NULL;
39
40 struct Expression {
41         struct Expression *next;
42         struct Expression *prev;
43         const char* expressionSyntax;
44         int lineNo;
45 };
46
47 static void cb_console(const char *str, void *data)
48 {
49  printf("CONSOLE> %s\n",str);
50 }
51
52
53 /* Note that unlike what's documented in gdb docs it isn't usable. */
54 static void cb_target(const char *str, void *data)
55 {
56  printf("TARGET> %s\n",str);
57 }
58
59
60 static void cb_log(const char *str, void *data)
61 {
62  printf("LOG> %s\n",str);
63 }
64
65
66 static void cb_to(const char *str, void *data)
67 {
68  printf(">> %s",str);
69 }
70
71
72 static void cb_from(const char *str, void *data)
73 {
74  printf("<< %s\n",str);
75 }
76
77
78 static void cb_async(mi_output *o, void *data)
79 {
80  printf("ASYNC\n");
81  async_c++;
82 }
83
84
85 static int wait_for_stop(mi_h *h, struct GNUNET_MONKEY_ACTION_Context *cntxt)
86 {
87         while (!mi_get_response(h))
88                 usleep(1000);
89         /* The end of the async. */
90         cntxt->gdb_stop_reason=mi_res_stop(h);
91         if (cntxt->gdb_stop_reason)
92         {
93                 if (cntxt->gdb_stop_reason->reason == sr_exited_normally)
94                         return GDB_STATE_EXIT_NORMALLY;
95
96                 cntxt->gdb_frames = gmi_stack_info_frame(h);
97                 if (NULL == cntxt->gdb_frames)
98                   GNUNET_break(0);
99
100                 return GDB_STATE_STOPPED;
101         }
102         return GDB_STATE_ERROR;
103 }
104
105
106 int GNUNET_MONKEY_ACTION_report_file(struct GNUNET_MONKEY_ACTION_Context* cntxt, const char* dumpFileName)
107 {
108         FILE* file = fopen(dumpFileName, "w");
109         GNUNET_assert(NULL != file);
110         fprintf(file,"%s", cntxt->debug_report);
111         fclose(file);
112         return GNUNET_OK;
113 }
114
115
116 int GNUNET_MONKEY_ACTION_report_email(struct GNUNET_MONKEY_ACTION_Context* cntxt)
117 {
118         if (cntxt->debug_mode == DEBUG_MODE_REPORT_READY)
119                 sendMail(cntxt->debug_report, cntxt->email_address);
120
121         return GNUNET_OK;
122 }
123
124
125
126 int GNUNET_MONKEY_ACTION_rerun_with_valgrind()
127 {
128         return GNUNET_OK;
129 }
130
131 static int iterateExpressions(void *cls, int numColumns, char **colValues, char **colNames)
132 {
133         struct Expression *expression;
134
135         if (NULL == colValues[0] || NULL == colValues[1])
136                 return 1; /* Error */
137
138         expression = GNUNET_malloc(sizeof(struct Expression));
139         expression->expressionSyntax = strdup(colValues[0]);
140         expression->lineNo = atoi(colValues[1]);
141
142
143         GNUNET_CONTAINER_DLL_insert(expressionListHead, expressionListTail, expression);
144
145         return 0; /* OK */
146 }
147
148
149 static int scopeEndCallback(void *cls, int numColumns, char **colValues, char ** colNames)
150 {
151         int *scopeEnd = (int*) cls;
152
153         *scopeEnd = atoi(colValues[0]);
154         if (*scopeEnd < 0)
155                 return 1; /* Error */
156         return 0;
157 }
158
159
160 static int analyzeSegmentationFault(struct GNUNET_MONKEY_ACTION_Context *cntxt)
161 {
162         struct Expression *faultyExpression;
163         struct Expression *tmp;
164         int expressionLength = 0;
165         mi_wp *watchPoint;
166
167         tmp = expressionListHead;
168         while (NULL != tmp) {
169                 if ((tmp->lineNo == cntxt->gdb_frames->line)
170                                 && (strlen(tmp->expressionSyntax) > expressionLength)) {
171                         expressionLength = strlen(tmp->expressionSyntax);
172                         faultyExpression = tmp;
173                 }
174                 tmp = tmp->next;
175         }
176
177         /* Set watch points on the faulty-expression's subexpressions */
178         tmp = expressionListHead;
179         while (NULL != tmp) {
180                 if (tmp != faultyExpression) {
181                         /* Only subexpressions are interesting */
182                          watchPoint = gmi_break_watch(cntxt->gdb_handle, wm_write, tmp->expressionSyntax);
183                          if (!watchPoint)
184                            {
185                             printf("Error in setting watchpoint\n");
186                             return 1;
187                            }
188                          printf("Watchpoint %d for expression: %s\n", watchPoint->number, watchPoint->exp);
189                          mi_free_wp(watchPoint);
190                 }
191                 tmp = tmp->next;
192         }
193         return GNUNET_OK;
194 }
195
196
197 int GNUNET_MONKEY_ACTION_inspect_expression_database(struct GNUNET_MONKEY_ACTION_Context *cntxt)
198 {
199         struct GNUNET_MONKEY_EDB_Context *edbCntxt;
200         int ret = GNUNET_OK;
201         int endScope;
202
203         edbCntxt = GNUNET_MONKEY_EDB_connect(cntxt->expression_database_path);
204         if (NULL == edbCntxt) {
205                 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Unable to connect to Expression Database file!\n");
206                 return GNUNET_NO;
207         }
208
209         ret = GNUNET_MONKEY_EDB_get_expression_scope_end(edbCntxt,
210                                            cntxt->gdb_frames->file, cntxt->gdb_frames->line,
211                                            &scopeEndCallback, &endScope);
212         if (endScope < 0)
213                 return GNUNET_NO;
214
215         ret = GNUNET_MONKEY_EDB_get_expressions (edbCntxt,
216                                            cntxt->gdb_frames->file, cntxt->gdb_frames->line,
217                                            endScope,
218                                            &iterateExpressions,
219                                            NULL);
220
221         if (strcasecmp(cntxt->gdb_stop_reason->signal_meaning, "Segmentation fault") == 0)
222                 analyzeSegmentationFault(cntxt);
223
224         GNUNET_MONKEY_EDB_disconnect(edbCntxt);
225         return ret;
226 }
227
228
229 int GNUNET_MONKEY_ACTION_rerun_with_gdb(struct GNUNET_MONKEY_ACTION_Context* cntxt)
230 {
231         cntxt->debug_mode = DEBUG_MODE_GDB;
232
233         /* This is like a file-handle for fopen.
234             Here we have all the state of gdb "connection". */
235         if (NULL != cntxt->gdb_binary_path)
236                 mi_set_gdb_exe(cntxt->gdb_binary_path);
237         int ret;
238
239          /* Connect to gdb child. */
240         cntxt->gdb_handle = mi_connect_local();
241          if (!cntxt->gdb_handle)
242            {
243             printf("Connect failed\n");
244             return GNUNET_NO;
245            }
246          printf("Connected to gdb!\n");
247
248          /* Set all callbacks. */
249          mi_set_console_cb(cntxt->gdb_handle,cb_console,NULL);
250          mi_set_target_cb(cntxt->gdb_handle,cb_target,NULL);
251          mi_set_log_cb(cntxt->gdb_handle,cb_log,NULL);
252          mi_set_async_cb(cntxt->gdb_handle,cb_async,NULL);
253          mi_set_to_gdb_cb(cntxt->gdb_handle,cb_to,NULL);
254          mi_set_from_gdb_cb(cntxt->gdb_handle,cb_from,NULL);
255
256          /* Set the name of the child and the command line arguments. */
257          if (!gmi_set_exec(cntxt->gdb_handle, cntxt->binary_name, NULL))
258            {
259             printf("Error setting exec y args\n");
260             mi_disconnect(cntxt->gdb_handle);
261             return GNUNET_NO;
262            }
263
264          /* Tell gdb to attach the child to a terminal. */
265          if (!gmi_target_terminal(cntxt->gdb_handle, ttyname(STDIN_FILENO)))
266            {
267             printf("Error selecting target terminal\n");
268             mi_disconnect(cntxt->gdb_handle);
269             return GNUNET_NO;
270            }
271
272          /* Run the program. */
273          if (!gmi_exec_run(cntxt->gdb_handle))
274            {
275             printf("Error in run!\n");
276             mi_disconnect(cntxt->gdb_handle);
277             return GNUNET_NO;
278            }
279          /* Here we should be stopped when the program crashes */
280          ret = wait_for_stop(cntxt->gdb_handle, cntxt);
281          if (ret != GDB_STATE_ERROR)
282             mi_disconnect(cntxt->gdb_handle);
283
284         return ret;
285 }
286
287
288 int GNUNET_MONKEY_ACTION_format_report(struct GNUNET_MONKEY_ACTION_Context* cntxt)
289 {
290         switch (cntxt->debug_mode) {
291         case DEBUG_MODE_GDB:
292                 GNUNET_asprintf(&(cntxt->debug_report),
293                         "Bug detected in file:%s\nfunction:%s\nline:%d\nreason:%s\nreceived signal:%s\n%s\n",
294                         cntxt->gdb_frames->file, cntxt->gdb_frames->func, cntxt->gdb_frames->line, mi_reason_enum_to_str(cntxt->gdb_stop_reason->reason), cntxt->gdb_stop_reason->signal_name, cntxt->gdb_stop_reason->signal_meaning);
295                 break;
296         case DEBUG_MODE_VALGRIND:
297                 break;
298         default:
299                 break;
300         }
301
302         cntxt->debug_mode = DEBUG_MODE_REPORT_READY;
303         return GNUNET_OK;
304 }
305
306
307 int GNUNET_MONKEY_ACTION_check_bug_redundancy()
308 {
309         return GNUNET_OK;
310 }