1 /*
2     csdebug.c:
3 
4     Copyright (C) 2013 Andres Cabrera
5 
6     This file is part of Csound.
7 
8     The Csound Library is free software; you can redistribute it
9     and/or modify it under the terms of the GNU Lesser General Public
10     License as published by the Free Software Foundation; either
11     version 2.1 of the License, or (at your option) any later version.
12 
13     Csound is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU Lesser General Public License for more details.
17 
18     You should have received a copy of the GNU Lesser General Public
19     License along with Csound; if not, write to the Free Software
20     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21     02110-1301 USA
22 */
23 
24 #include <assert.h>
25 
26 #include "csdebug.h"
27 
28 debug_instr_t *csoundDebugGetCurrentInstrInstance(CSOUND *csound);
29 debug_opcode_t *csoundDebugGetCurrentOpcodeList(CSOUND *csound);
30 void csoundDebugFreeOpcodeList(CSOUND *csound, debug_opcode_t *opcode_list);
31 
csoundDebuggerBreakpointReached(CSOUND * csound)32 void csoundDebuggerBreakpointReached(CSOUND *csound)
33 {
34     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
35     debug_bkpt_info_t bkpt_info;
36     bkpt_info.breakpointInstr = csoundDebugGetCurrentInstrInstance(csound);
37     bkpt_info.instrListHead = csoundDebugGetInstrInstances(csound);
38     bkpt_info.currentOpcode = csoundDebugGetCurrentOpcodeList(csound);
39     bkpt_info.instrVarList = csoundDebugGetVariables(csound,
40                                                      bkpt_info.breakpointInstr);
41     if (data->bkpt_cb) {
42       data->bkpt_cb(csound, &bkpt_info, data->cb_data);
43     } else {
44       csoundMessage(csound, Str("Breakpoint callback not set. Breakpoint Reached."));
45     }
46     // TODO: These free operations could be moved to a low priority context
47     csoundDebugFreeInstrInstances(csound, bkpt_info.breakpointInstr);
48     csoundDebugFreeInstrInstances(csound, bkpt_info.instrListHead);
49     if (bkpt_info.currentOpcode) {
50         csoundDebugFreeOpcodeList(csound, bkpt_info.currentOpcode);
51     }
52     csoundDebugFreeVariables(csound, bkpt_info.instrVarList);
53 }
54 
csoundDebuggerInit(CSOUND * csound)55 PUBLIC void csoundDebuggerInit(CSOUND *csound)
56 {
57     csdebug_data_t *data =
58       (csdebug_data_t *) csound->Malloc(csound, sizeof(csdebug_data_t));
59     data->bkpt_anchor = (bkpt_node_t *) csound->Malloc(csound, sizeof(bkpt_node_t));
60     data->bkpt_anchor->line = -1;
61     data->bkpt_anchor->next = NULL;
62     data->debug_instr_ptr = NULL;
63     data->debug_opcode_ptr = NULL;
64     data->bkpt_cb = NULL;
65     data->status = CSDEBUG_STATUS_RUNNING;
66     data->bkpt_buffer = csoundCreateCircularBuffer(csound,
67                                                    64, sizeof(bkpt_node_t **));
68     data->cmd_buffer = csoundCreateCircularBuffer(csound,
69                                                   64, sizeof(debug_command_t));
70     csound->csdebug_data = data;
71     csound->kperf = kperf_debug;
72 }
73 
csoundDebuggerClean(CSOUND * csound)74 PUBLIC void csoundDebuggerClean(CSOUND *csound)
75 {
76     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
77     assert(data);
78     bkpt_node_t *node = data->bkpt_anchor;
79     csoundDestroyCircularBuffer(csound, data->bkpt_buffer);
80     csoundDestroyCircularBuffer(csound, data->cmd_buffer);
81     while (node) {
82         bkpt_node_t *oldnode = node;
83         node = node->next;
84         csound->Free(csound, oldnode);
85     }
86     csound->Free(csound, data);
87     csound->csdebug_data = NULL;
88     csound->kperf = kperf_nodebug;
89 }
90 
csoundDebugStart(CSOUND * csound)91 PUBLIC void csoundDebugStart(CSOUND *csound)
92 {
93     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
94     data->status = CSDEBUG_STATUS_RUNNING;
95 }
96 
csoundSetBreakpoint(CSOUND * csound,int line,int instr,int skip)97 PUBLIC void csoundSetBreakpoint(CSOUND *csound, int line, int instr, int skip)
98 {
99     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
100     if (!data) {
101       csound->Warning(csound,
102                       Str("csoundSetBreakpoint: cannot set breakpoint. "
103                           "Debugger is not initialised."));
104       return;
105     }
106     if (line <= 0) {
107       csound->Warning(csound, Str("csoundSetBreakpoint: line > 0 for breakpoint."));
108       return;
109     }
110     bkpt_node_t *newpoint =
111       (bkpt_node_t *) csound->Malloc(csound, sizeof(bkpt_node_t));
112     newpoint->line = line;
113     newpoint->instr = instr;
114     newpoint->skip = skip;
115     newpoint->count = skip;
116     newpoint->mode = CSDEBUG_BKPT_LINE;
117     csoundWriteCircularBuffer(csound, data->bkpt_buffer, &newpoint, 1);
118 }
119 
csoundRemoveBreakpoint(CSOUND * csound,int line,int instr)120 PUBLIC void csoundRemoveBreakpoint(CSOUND *csound, int line, int instr)
121 {
122     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
123     if (!data) {
124       csound->Warning(csound,
125                       Str("csoundRemoveBreakpoint: cannot remove breakpoint. "
126                           "Debugger is not initialised."));
127       return;
128     }
129     if (line < 0) {
130       csound->Warning(csound, Str ("Negative line for breakpoint invalid."));
131     }
132     bkpt_node_t *newpoint =
133       (bkpt_node_t *) csound->Malloc(csound, sizeof(bkpt_node_t));
134     newpoint->line = line;
135     newpoint->instr = instr;
136     newpoint->mode = CSDEBUG_BKPT_DELETE;
137     csoundWriteCircularBuffer(csound, data->bkpt_buffer, &newpoint, 1);
138 }
139 
csoundSetInstrumentBreakpoint(CSOUND * csound,MYFLT instr,int skip)140 PUBLIC void csoundSetInstrumentBreakpoint(CSOUND *csound, MYFLT instr, int skip)
141 {
142     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
143     if (!data) {
144       csound->Warning(csound,
145                       Str("csoundRemoveBreakpoint: cannot remove breakpoint. "
146                           "Debugger is not initialised."));
147       return;
148     }
149     assert(data);
150     bkpt_node_t *newpoint =
151       (bkpt_node_t *) csound->Malloc(csound, sizeof(bkpt_node_t));
152     newpoint->line = -1;
153     newpoint->instr = instr;
154     newpoint->skip = skip;
155     newpoint->count = skip;
156     newpoint->mode = CSDEBUG_BKPT_INSTR;
157     csoundWriteCircularBuffer(csound, data->bkpt_buffer, &newpoint, 1);
158 }
159 
csoundRemoveInstrumentBreakpoint(CSOUND * csound,MYFLT instr)160 PUBLIC void csoundRemoveInstrumentBreakpoint(CSOUND *csound, MYFLT instr)
161 {
162     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
163     assert(data);
164     bkpt_node_t *newpoint =
165       (bkpt_node_t *) csound->Malloc(csound, sizeof(bkpt_node_t));
166     newpoint->line = -1;
167     newpoint->instr = instr;
168     newpoint->mode = CSDEBUG_BKPT_DELETE;
169     csoundWriteCircularBuffer(csound, data->bkpt_buffer, &newpoint, 1);
170 }
171 
csoundClearBreakpoints(CSOUND * csound)172 PUBLIC void csoundClearBreakpoints(CSOUND *csound)
173 {
174     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
175     assert(data);
176     bkpt_node_t *newpoint =
177       (bkpt_node_t *) csound->Malloc(csound, sizeof(bkpt_node_t));
178     newpoint->line = -1;
179     newpoint->instr = -1;
180     newpoint->mode = CSDEBUG_BKPT_CLEAR_ALL;
181     csoundWriteCircularBuffer(csound, data->bkpt_buffer, &newpoint, 1);
182 }
183 
csoundSetBreakpointCallback(CSOUND * csound,breakpoint_cb_t bkpt_cb,void * userdata)184 PUBLIC void csoundSetBreakpointCallback(CSOUND *csound,
185                                        breakpoint_cb_t bkpt_cb, void *userdata)
186 {
187 
188     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
189     assert(data);
190     data->bkpt_cb = bkpt_cb;
191     data->cb_data = userdata;
192 }
193 
csoundDebugStepOver(CSOUND * csound)194 PUBLIC void csoundDebugStepOver(CSOUND *csound)
195 {
196     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
197     assert(data);
198     debug_command_t command = CSDEBUG_CMD_STEPOVER;
199     csoundWriteCircularBuffer(csound, data->cmd_buffer, &command, 1);
200 }
201 
csoundDebugStepInto(CSOUND * csound)202 PUBLIC void csoundDebugStepInto(CSOUND *csound)
203 {
204     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
205     assert(data);
206     debug_command_t command = CSDEBUG_CMD_STEPINTO;
207     csoundWriteCircularBuffer(csound, data->cmd_buffer, &command, 1);
208 }
209 
csoundDebugNext(CSOUND * csound)210 PUBLIC void csoundDebugNext(CSOUND *csound)
211 {
212     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
213     assert(data);
214     debug_command_t command = CSDEBUG_CMD_NEXT;
215     csoundWriteCircularBuffer(csound, data->cmd_buffer, &command, 1);
216 }
217 
csoundDebugContinue(CSOUND * csound)218 PUBLIC void csoundDebugContinue(CSOUND *csound)
219 {
220     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
221     assert(data);
222     debug_command_t command = CSDEBUG_CMD_CONTINUE;
223     csoundWriteCircularBuffer(csound, data->cmd_buffer, &command, 1);
224 }
225 
csoundDebugStop(CSOUND * csound)226 PUBLIC void csoundDebugStop(CSOUND *csound)
227 {
228     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
229     assert(data);
230     debug_command_t command = CSDEBUG_CMD_STOP;
231     csoundWriteCircularBuffer(csound, data->cmd_buffer, &command, 1);
232 }
233 
csoundDebugGetInstrInstances(CSOUND * csound)234 PUBLIC debug_instr_t *csoundDebugGetInstrInstances(CSOUND *csound)
235 {
236     debug_instr_t *instrhead = NULL;
237     debug_instr_t *debug_instr = NULL;
238     INSDS *insds = csound->actanchor.nxtact;
239 
240     while (insds) {
241         if (!instrhead) {
242             instrhead = csound->Malloc(csound, sizeof(debug_instr_t));
243             debug_instr = instrhead;
244         } else {
245             debug_instr->next = csound->Malloc(csound, sizeof(debug_instr_t));
246             debug_instr = debug_instr->next;
247         }
248         debug_instr->lclbas = insds->lclbas;
249         debug_instr->varPoolHead = insds->instr->varPool->head;
250         debug_instr->instrptr = (void *) insds;
251         debug_instr->p1 = insds->p1.value;
252         debug_instr->p2 = insds->p2.value;
253         debug_instr->p3 = insds->p3.value;
254         debug_instr->kcounter = insds->kcounter;
255         debug_instr->next = NULL;
256         insds = insds->nxtact;
257     }
258     return instrhead;
259 }
260 
csoundDebugGetCurrentInstrInstance(CSOUND * csound)261 debug_instr_t *csoundDebugGetCurrentInstrInstance(CSOUND *csound)
262 {
263     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
264     assert(data);
265     if (!data->debug_instr_ptr) {
266         return NULL;
267     }
268     debug_instr_t *debug_instr = csound->Malloc(csound, sizeof(debug_instr_t));
269     INSDS *insds = (INSDS *)data->debug_instr_ptr;
270     debug_instr->lclbas = insds->lclbas;
271     debug_instr->varPoolHead = insds->instr->varPool->head;
272     debug_instr->instrptr = data->debug_instr_ptr;
273     debug_instr->p1 = insds->p1.value;
274     debug_instr->p2 = insds->p2.value;
275     debug_instr->p3 = insds->p3.value;
276     debug_instr->kcounter = insds->kcounter;
277     debug_instr->next = NULL;
278     OPDS* opstart = (OPDS*) data->debug_instr_ptr;
279     if (opstart->nxtp) {
280       debug_instr->line = opstart->nxtp->optext->t.linenum;
281     } else {
282       debug_instr->line = 0;
283     }
284     return debug_instr;
285 }
286 
287 
csoundDebugGetCurrentOpcodeList(CSOUND * csound)288 debug_opcode_t *csoundDebugGetCurrentOpcodeList(CSOUND *csound)
289 {
290     csdebug_data_t *data = (csdebug_data_t *) csound->csdebug_data;
291     assert(data);
292     if (!data->debug_instr_ptr) {
293         return NULL;
294     }
295     OPDS *op = (OPDS *)data->debug_opcode_ptr;
296 
297     if (!op) {
298         return NULL;
299     }
300     debug_opcode_t *opcode_list = csound->Malloc(csound, sizeof(debug_opcode_t));
301     strNcpy(opcode_list->opname, op->optext->t.opcod, 16);
302     //opcode_list->opname[15] = '\0';
303     opcode_list->line = op->optext->t.linenum;
304     return opcode_list;
305 }
306 
csoundDebugFreeOpcodeList(CSOUND * csound,debug_opcode_t * opcode_list)307 void csoundDebugFreeOpcodeList(CSOUND *csound, debug_opcode_t *opcode_list)
308 {
309     csound->Free(csound, opcode_list);
310 }
311 
csoundDebugFreeInstrInstances(CSOUND * csound,debug_instr_t * instr)312 PUBLIC void csoundDebugFreeInstrInstances(CSOUND *csound, debug_instr_t *instr)
313 {
314     while (instr) {
315         debug_instr_t *oldinstr = instr;
316         instr = instr->next;
317         csound->Free(csound, oldinstr);
318     }
319 }
320 
csoundDebugGetVariables(CSOUND * csound,debug_instr_t * instr)321 PUBLIC debug_variable_t *csoundDebugGetVariables(CSOUND *csound,
322                                                  debug_instr_t *instr)
323 {
324     debug_variable_t *head = NULL;
325     debug_variable_t *debug_var = head;
326     CS_VARIABLE * var = instr->varPoolHead;
327     while (var) {
328         void * varmem = NULL;
329         if (!head) {
330             head = csound->Malloc(csound, sizeof(debug_variable_t));
331             debug_var = head;
332         } else {
333             debug_var->next = csound->Malloc(csound, sizeof(debug_variable_t));
334             debug_var = debug_var->next;
335         }
336         debug_var->next = NULL;
337         debug_var->name = var->varName;
338         debug_var->typeName = var->varType->varTypeName;
339         if (strcmp(debug_var->typeName, "i") == 0
340                 || strcmp(debug_var->typeName, "k") == 0
341                 || strcmp(debug_var->typeName, "a") == 0
342                 || strcmp(debug_var->typeName, "r") == 0
343                 ) {
344             varmem = instr->lclbas + var->memBlockIndex;
345         } else if (strcmp(debug_var->typeName, "S") == 0) {
346             STRINGDAT *strdata = (STRINGDAT *) (instr->lclbas + var->memBlockIndex);
347             varmem = &strdata->data[0];
348         } else {
349             csound->Message(csound, "csoundDebugGetVarData() unknown data type.\n");
350         }
351         debug_var->data = varmem;
352         var = var->next;
353     }
354     return head;
355 }
356 
357 
csoundDebugFreeVariables(CSOUND * csound,debug_variable_t * varHead)358 PUBLIC void csoundDebugFreeVariables(CSOUND *csound, debug_variable_t *varHead)
359 {
360     while (varHead) {
361         debug_variable_t *oldvar = varHead;
362         varHead = varHead->next;
363         csound->Free(csound, oldvar);
364     }
365 }
366 
367