Quellcode durchsuchen

breakpoints done, server-initiated replies through comet calls

Simon Van Mierlo vor 8 Jahren
Ursprung
Commit
1959b02062

+ 42 - 1
kernel/mvk_server/classes/mvkcontroller.xml

@@ -106,6 +106,8 @@
                                     print("Added to input queue")
                                 else:
                                     print("Appending " + data["op"])
+                                    if "args" in data:
+                                        args = json.loads(data["args"])
                                     self.input_queue.append((source, data["op"], args, data["taskname"]))
                             except ValueError:
                                 print("Error when deserializing request: " + str(data))
@@ -151,10 +153,19 @@
                             <parameter expr="args"/>
                         </raise>
                     </transition>
+
+                    <transition cond="self.input_queue[0][1] == 'poll_messages'" target="../wait">
+                        <script>
+                            source, op, args, taskname = self.input_queue.pop(0)
+                        </script>
+                        <raise event="poll_messages" scope="narrow" target="self.task_statecharts[taskname]">
+                            <parameter expr="source"/>
+                            <parameter expr="args"/>
+                        </raise>
+                    </transition>
                     
                     <transition cond="self.input_queue[0][1] == 'pause'" target="../wait">
                         <script>
-                            print("Sending pause")
                             source, op, args, taskname = self.input_queue.pop(0)
                         </script>
                         <raise event="pause" scope="narrow" target="self.task_statecharts[taskname]">
@@ -242,6 +253,36 @@
                             <parameter expr="args"/>
                         </raise>
                     </transition>
+                    
+                    <transition cond="self.input_queue[0][1] == 'add_breakpoint'" target="../wait">
+                        <script>
+                            source, op, args, taskname = self.input_queue.pop(0)
+                        </script>
+                        <raise event="add_breakpoint" scope="narrow" target="self.task_statecharts[taskname]">
+                            <parameter expr="source"/>
+                            <parameter expr="args"/>
+                        </raise>
+                    </transition>
+                    
+                    <transition cond="self.input_queue[0][1] == 'del_breakpoint'" target="../wait">
+                        <script>
+                            source, op, args, taskname = self.input_queue.pop(0)
+                        </script>
+                        <raise event="del_breakpoint" scope="narrow" target="self.task_statecharts[taskname]">
+                            <parameter expr="source"/>
+                            <parameter expr="args"/>
+                        </raise>
+                    </transition>
+                    
+                    <transition cond="self.input_queue[0][1] == 'toggle_breakpoint'" target="../wait">
+                        <script>
+                            source, op, args, taskname = self.input_queue.pop(0)
+                        </script>
+                        <raise event="toggle_breakpoint" scope="narrow" target="self.task_statecharts[taskname]">
+                            <parameter expr="source"/>
+                            <parameter expr="args"/>
+                        </raise>
+                    </transition>
                 </state>
             </state>
 

+ 218 - 40
kernel/mvk_server/classes/task_statechart.xml

@@ -2,6 +2,7 @@
     <relationships>
         <association name="parent" class="MvKController" min="1" max="1"/>
     </relationships>
+    
     <constructor>
         <parameter name="taskname"/>
         <parameter name="mvk" />
@@ -27,21 +28,37 @@
             self.big_step_info = None
             self.line_step_info = None
             self.step_into_info = None
+
+            self.breakpoints = []
+            self.triggered_bp = -1
+            
+            self.queue = []
+            self.polling_source = None
             ]]>
         </body>
     </constructor>
     
+    <method name="enqueue">
+        <parameter name="parameters" />
+        <body>
+            <![CDATA[
+            self.queue.append(parameters)
+            ]]>
+        </body>
+    </method>
+    
     <method name="extract_line_info">
         <body>
             <![CDATA[
             if self.mvk.debug_info[self.taskname][-1] != 'None':
                 splitted = self.mvk.debug_info[self.taskname][-1].split(':')
                 self.curr_file = ":".join(splitted[0:-2])[1:]
-                self.curr_line = splitted[-2]
+                self.curr_line = int(splitted[-2])
                 splitted_chars = splitted[-1].split("-")
                 self.curr_begin_char = splitted_chars[0]
                 self.curr_end_char = splitted_chars[1]
-                print 'curr_line = %s %s %s %s' % (self.curr_file, self.curr_line, self.curr_begin_char, self.curr_end_char)
+                # print 'curr_line = %s %s %s %s' % (self.curr_file, self.curr_line, self.curr_begin_char, self.curr_end_char)
+                print (self.curr_file, self.curr_line)
             ]]>
         </body>
     </method>
@@ -49,6 +66,7 @@
     <method name="add_source_code">
         <parameter name="stack" />
         <body>
+            <![CDATA[
             ret_value = []
             for line in stack:
                 splitted = line.split(':')
@@ -61,6 +79,28 @@
                 except IOError:
                     ret_value.append((line, ""))
             return ret_value
+            ]]>
+        </body>
+    </method>
+    
+    <method name="breakpoint_triggers">
+        <body>
+            <![CDATA[
+            # Now that it includes breakpoints, results are to be interpretted as follows:
+            # -1 --> continue simulation
+            # other --> breakpoint triggered
+            for bp in self.breakpoints:
+                if not bp.enabled:
+                    continue
+                if self.curr_file == bp.file_name and self.curr_line == bp.line_number:
+                    self.triggered_bp = bp.id
+                    if bp.disable_on_trigger:
+                        bp.enabled = False
+                    return bp.id
+                else:
+                    continue
+            return -1
+            ]]>
         </body>
     </method>
     
@@ -191,6 +231,18 @@
                         <transition after="1.0" target="../try_execute"/>
                     </state>
                 </state>
+                
+                <state id="polling_listener" initial="listening">
+                    <state id="listening">
+                        <transition target="." event="poll_messages">
+                            <parameter name="source" />
+                            <parameter name="args"/>
+                            <script>
+                                self.polling_source = source
+                            </script>
+                        </transition>
+                    </state>
+                </state>
             
                 <history id="running_normal_history" type="deep" />
             </parallel>
@@ -198,16 +250,22 @@
             <transition target="../running_debug" event="attach_debugger">
                 <parameter name="source"/>
                 <parameter name="args"/>
+                <script>
+                    self.enqueue({'task': self.taskname, 'state': 'debugger_attached', 'result': 'success'})
+                </script>
                 <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
-                    <parameter expr="json.dumps({'task': self.taskname, 'state': 'debugger_attached', 'result': 'success'})"/>
+                    <parameter expr="json.dumps(True)"/>
                 </raise>
             </transition>
             
             <transition target="running/running_normal_history" event="detach_debugger">
                 <parameter name="source"/>
                 <parameter name="args"/>
+                <script>
+                    self.enqueue({'task': self.taskname, 'result': 'not allowed'})
+                </script>
                 <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
-                    <parameter expr="json.dumps({'task': self.taskname, 'result': 'not allowed'})"/>
+                    <parameter expr="json.dumps(True)"/>
                 </raise>
             </transition>
         </state>
@@ -344,36 +402,51 @@
                     <transition target="execution_state_history" event="pause">
                         <parameter name="source"/>
                         <parameter name="args"/>
+                        <script>
+                            self.enqueue({'task': self.taskname, 'result': 'not allowed'})
+                        </script>
                         <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
-                            <parameter expr="json.dumps({'task': self.taskname, 'result': 'not allowed'})"/>
+                            <parameter expr="json.dumps(True)"/>
                         </raise>
                     </transition>
                     <transition target="execution_state_history" event="resume">
                         <parameter name="source"/>
                         <parameter name="args"/>
+                        <script>
+                            self.enqueue({'task': self.taskname, 'result': 'not allowed'})
+                        </script>
                         <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
-                            <parameter expr="json.dumps({'task': self.taskname, 'result': 'not allowed'})"/>
+                            <parameter expr="json.dumps(True)"/>
                         </raise>
                     </transition>
                     <transition target="execution_state_history" event="big_step">
                         <parameter name="source"/>
                         <parameter name="args"/>
+                        <script>
+                            self.enqueue({'task': self.taskname, 'result': 'not allowed'})
+                        </script>
                         <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
-                            <parameter expr="json.dumps({'task': self.taskname, 'result': 'not allowed'})"/>
+                            <parameter expr="json.dumps(True)"/>
                         </raise>
                     </transition>
                     <transition target="execution_state_history" event="line_step">
                         <parameter name="source"/>
                         <parameter name="args"/>
+                        <script>
+                            self.enqueue({'task': self.taskname, 'result': 'not allowed'})
+                        </script>
                         <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
-                            <parameter expr="json.dumps({'task': self.taskname, 'result': 'not allowed'})"/>
+                            <parameter expr="json.dumps(True)"/>
                         </raise>
                     </transition>
                     <transition target="execution_state_history" event="step_into">
                         <parameter name="source"/>
                         <parameter name="args"/>
+                        <script>
+                            self.enqueue({'task': self.taskname, 'result': 'not allowed'})
+                        </script>
                         <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
-                            <parameter expr="json.dumps({'task': self.taskname, 'result': 'not allowed'})"/>
+                            <parameter expr="json.dumps(True)"/>
                         </raise>
                     </transition>
                     <history id="execution_state_history" type="deep" />
@@ -383,42 +456,45 @@
                             <transition event="pause" target="../../paused">
                                 <parameter name="source"/>
                                 <parameter name="args"/>
+                                <script>
+                                    self.enqueue({'task': self.taskname, 'result': 'success', 'state': 'paused', 'stack': self.add_source_code(self.mvk.debug_info[self.taskname])})
+                                </script>
                                 <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
-                                    <parameter expr="json.dumps({'task': self.taskname, 'result': 'success', 'state': 'paused', 'stack': self.add_source_code(self.mvk.debug_info[self.taskname])})"/>
+                                    <parameter expr="json.dumps(True)"/>
                                 </raise>
                             </transition>
+                            <transition cond="self.breakpoint_triggers() != -1" target="../../paused">
+                                <script>
+                                    print 'breakpoint %s triggers!' % self.triggered_bp
+                                    self.enqueue({'task': self.taskname, 'result': 'breakpoint triggered', 'state': 'paused', 'triggered_bp': self.triggered_bp, 'stack': self.add_source_code(self.mvk.debug_info[self.taskname])})
+                                </script>
+                            </transition>
                         </state>
                         <state id="big_step">
                             <transition event="big_step_done" target="../../paused">
-                                <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + self.big_step_source">
-                                    <parameter expr="json.dumps({'task': self.taskname, 'result': 'success', 'state': 'paused', 'stack': self.add_source_code(self.mvk.debug_info[self.taskname]), 'instruction': self.mvk.inst_v['value']})"/>
-                                </raise>
+                                <script>
+                                    self.enqueue({'task': self.taskname, 'result': 'success', 'state': 'paused', 'stack': self.add_source_code(self.mvk.debug_info[self.taskname]), 'instruction': self.mvk.inst_v['value']})
+                                </script>
                             </transition>
                         </state>
                         <state id="small_step">
                             <transition event="small_step_done" target="../../paused">
-                                <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + self.small_step_source">
-                                    <parameter expr="json.dumps({'task': self.taskname, 'result': 'success', 'state': 'paused', 'stack': self.add_source_code(self.mvk.debug_info[self.taskname]), 'instruction': self.mvk.inst_v['value'], 'phase': self.mvk.phase_v})"/>
-                                </raise>
+                                <script>
+                                    self.enqueue({'task': self.taskname, 'result': 'success', 'state': 'paused', 'stack': self.add_source_code(self.mvk.debug_info[self.taskname]), 'instruction': self.mvk.inst_v['value'], 'phase': self.mvk.phase_v})
+                                </script>
                             </transition>
                         </state>
                         <state id="step_into">
                             <transition event="step_into_done" target="../../paused">
-                                <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + self.step_into_info['source']">
-                                    <parameter expr="json.dumps({'task': self.taskname, 'result': 'success', 'state': 'paused', 'stack': self.add_source_code(self.mvk.debug_info[self.taskname])})"/>
-                                </raise>
                                 <script>
-                                    self.step_into_info = None
+                                    self.enqueue({'task': self.taskname, 'result': 'success', 'state': 'paused', 'stack': self.add_source_code(self.mvk.debug_info[self.taskname])})
                                 </script>
                             </transition>
                         </state>
                         <state id="line_step">
                             <transition event="line_step_done" target="../../paused">
-                                <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + self.line_step_info['source']">
-                                    <parameter expr="json.dumps({'task': self.taskname, 'result': 'success', 'state': 'paused', 'stack': self.add_source_code(self.mvk.debug_info[self.taskname])})"/>
-                                </raise>
                                 <script>
-                                    self.line_step_info = None
+                                    self.enqueue({'task': self.taskname, 'result': 'success', 'state': 'paused', 'stack': self.add_source_code(self.mvk.debug_info[self.taskname])})
                                 </script>
                             </transition>
                         </state>
@@ -427,43 +503,50 @@
                         <transition target="../running/continuous" event="resume">
                             <parameter name="source"/>
                             <parameter name="args"/>
+                            <script>
+                                self.enqueue({'task': self.taskname, 'result': 'success', 'state': 'running'})
+                            </script>
                             <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
-                                <parameter expr="json.dumps({'task': self.taskname, 'result': 'success', 'state': 'running'})"/>
+                                <parameter expr="json.dumps(True)"/>
                             </raise>
                         </transition>
                         <transition target="../running/big_step" event="big_step">
                             <parameter name="source"/>
                             <parameter name="args"/>
-                            <script>
-                                self.big_step_source = source
-                            </script>
+                            <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
+                                <parameter expr="json.dumps(True)"/>
+                            </raise>
                         </transition>
                         <transition target="../running/small_step" event="small_step">
                             <parameter name="source"/>
                             <parameter name="args"/>
-                            <script>
-                                self.small_step_source = source
-                            </script>
+                            <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
+                                <parameter expr="json.dumps(True)"/>
+                            </raise>
                         </transition>
                         <transition target="../running/step_into" event="step_into">
                             <parameter name="source"/>
                             <parameter name="args"/>
                             <script>
-                                self.step_into_info = {'source': source,
-                                                       'stack_len': len(self.mvk.debug_info[self.taskname]),
+                                self.step_into_info = {'stack_len': len(self.mvk.debug_info[self.taskname]),
                                                        'file': self.curr_file,
                                                        'line': self.curr_line}
                             </script>
+                            <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
+                                <parameter expr="json.dumps(True)"/>
+                            </raise>
                         </transition>
                         <transition target="../running/line_step" event="line_step">
                             <parameter name="source"/>
                             <parameter name="args"/>
                             <script>
-                                self.line_step_info = {'source': source,
-                                                       'stack_len': len(self.mvk.debug_info[self.taskname]),
+                                self.line_step_info = {'stack_len': len(self.mvk.debug_info[self.taskname]),
                                                        'file': self.curr_file,
                                                        'line': self.curr_line}
                             </script>
+                            <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
+                                <parameter expr="json.dumps(True)"/>
+                            </raise>
                         </transition>
                     </state>
                     <state id="stopped" />
@@ -493,16 +576,22 @@
                             <parameter name="returnvalue"/>
                             <parameter name="success"/>
                             <parameter name="request_id"/>
+                            <script>
+                                self.enqueue({'task': self.taskname, 'result': 'success', 'symbols': returnvalue})
+                            </script>
                             <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + self.symbol_source">
-                                <parameter expr="json.dumps({'task': self.taskname, 'result': 'success', 'symbols': returnvalue})"/>
+                                <parameter expr="json.dumps(True)"/>
                             </raise>
                         </transition>
                         <transition event="executed" cond="not success and self.outstanding_execution == request_id" target="../listening">
                             <parameter name="returnvalue"/>
                             <parameter name="success"/>
                             <parameter name="request_id"/>
+                            <script>
+                                self.enqueue({'task': self.taskname, 'result': 'failure'})
+                            </script>
                             <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + self.symbol_source">
-                                <parameter expr="json.dumps({'task': self.taskname, 'result': 'failure'})"/>
+                                <parameter expr="json.dumps(True)"/>
                             </raise>
                         </transition>
                     </state>
@@ -541,7 +630,7 @@
                     </state>
                 </state>
                 
-                <state id="get_output_queue">
+                <state id="get_output_queue" initial="waiting">
                     <state id="waiting">
                         <transition event="get_output" target=".">
                             <parameter name="source"/>
@@ -593,6 +682,89 @@
                         <transition after="1.0" target="../try_execute"/>
                     </state>
                 </state>
+                
+                <state id="breakpoint_manager" initial="listening">
+                    <state id="listening">
+                        <transition event="add_breakpoint" target=".">
+                            <parameter name="source"/>
+                            <parameter name="args"/>
+                            <script>
+                                if not 'file_name' in args or args['file_name'] is None:
+                                    args['file_name'] = self.curr_file
+                                if not 'line_number' in args or args['line_number'] is None:
+                                    args['line_number'] = self.curr_line
+                                self.breakpoints.append(Breakpoint(args['breakpoint_id'], args['file_name'], args['line_number'], args['enabled'], args['disable_on_trigger']))
+                                print 'adding breakpoint %s' % args
+                                self.breakpoint_source = source
+                                self.enqueue({'task': self.taskname, 'id': args['breakpoint_id'], 'result': 'breakpoint added'})
+                            </script>
+                            <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
+                                <parameter expr="json.dumps(True)"/>
+                            </raise>
+                        </transition>
+                        <transition event="del_breakpoint" target=".">
+                            <parameter name="source"/>
+                            <parameter name="args"/>
+                            <script>
+                                self.breakpoints = [breakpoint for breakpoint in self.breakpoints if breakpoint.id != args['del_breakpoint_id']]
+                                self.enqueue({'task': self.taskname, 'id': args['breakpoint_id'], 'result': 'breakpoint deleted'})
+                            </script>
+                            <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
+                                <parameter expr="json.dumps(True)"/>
+                            </raise>
+                        </transition>
+                        <transition event="toggle_breakpoint" target=".">
+                            <parameter name="source"/>
+                            <parameter name="args"/>
+                            <script>
+                                for breakpoint in self.breakpoints:
+                                    if breakpoint.id == args['breakpoint_id']:
+                                        breakpoint.enabled = args['enabled']
+                                        break
+                                self.enqueue({'task': self.taskname, 'id': args['breakpoint_id'], 'result': 'breakpoint toggled'})
+                            </script>
+                            <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
+                                <parameter expr="json.dumps(True)"/>
+                            </raise>
+                        </transition>
+                        <transition event="list_breakpoints" target=".">
+                            <parameter name="source"/>
+                            <parameter name="args"/>
+                            <script>
+                                self.enqueue({'task': self.taskname, 'result': 'breakpoints listed', 'breakpoints': [bp.id for bp in self.breakpoints]})
+                            </script>
+                            <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
+                                <parameter expr="json.dumps(True)"/>
+                            </raise>
+                        </transition>
+                    </state>
+                </state>
+                
+                <state id="polling_listener" initial="listening">
+                    <state id="listening">
+                        <transition target="." event="poll_messages">
+                            <parameter name="source" />
+                            <parameter name="args"/>
+                            <script>
+                                self.polling_source = source
+                            </script>
+                        </transition>
+                    </state>
+                </state>
+                
+                <state id="polling_sender" initial="sending">
+                    <state id="sending">
+                        <transition target="." cond="self.queue and (self.polling_source is not None)">
+                            <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + self.polling_source">
+                                <parameter expr="json.dumps(self.queue)"/>
+                            </raise>
+                            <script>
+                                self.polling_source = None
+                                self.queue = []
+                            </script>
+                        </transition>
+                    </state>
+                </state>
             
                 <history id="running_debug_history" type="deep" />
             </parallel>
@@ -600,16 +772,22 @@
             <transition target="../running_normal" event="detach_debugger">
                 <parameter name="source"/>
                 <parameter name="args"/>
+                <script>
+                    self.enqueue({'task': self.taskname, 'state': 'debugger_detached', 'result': 'success'})
+                </script>
                 <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
-                    <parameter expr="json.dumps({'task': self.taskname, 'state': 'debugger_detached', 'result': 'success'})"/>
+                    <parameter expr="json.dumps(True)"/>
                 </raise>
             </transition>
             
             <transition target="running/running_debug_history" event="attach_debugger">
                 <parameter name="source"/>
                 <parameter name="args"/>
+                <script>
+                    self.enqueue({'task': self.taskname, 'result': 'not allowed'})
+                </script>
                 <raise event="HTTP_input" scope="narrow" target="'parent/to_mvi/' + source">
-                    <parameter expr="json.dumps({'task': self.taskname, 'result': 'not allowed'})"/>
+                    <parameter expr="json.dumps(True)"/>
                 </raise>
             </transition>
         </state>

+ 8 - 0
kernel/mvk_server/server.xml

@@ -13,6 +13,14 @@
         from modelverse_kernel.main import ModelverseKernel
         sys.path.append("../../state")
         from modelverse_state.main import ModelverseState
+        
+        class Breakpoint:
+            def __init__(self, breakpoint_id, file_name, line_number, enabled, disable_on_trigger):
+                self.id = breakpoint_id
+                self.file_name = file_name
+                self.line_number = line_number
+                self.enabled = enabled
+                self.disable_on_trigger = disable_on_trigger
     </top>
 
     <inport name="socket_in"/>

+ 61 - 26
scripts/debug_prompt.py

@@ -19,41 +19,76 @@ def remote_print(string):
     print(string)
 
 local_print("Welcome to the debugging shell!")
-local_print("Please specify Modelverse location (default: 127.0.0.1:8001)")
-
-location = raw_input()
-if location == "":
-    address = "http://127.0.0.1:8001/"
-    
-local_print("Please specify task name (default: test)")
-
-taskname = raw_input()
-if location == "":
-    taskname = "test"
+try:
+    address = sys.argv[1]
+except IndexError:
+    address = "http://127.0.0.1:8001"
+try:
+    taskname = sys.argv[2]
+except IndexError:
+    import random
+    taskname = str(random.random())
 
 local_print("Switching context to Modelverse: all data is piped.")
-local_print("Available commands: 'attach_debugger', 'detach_debugger', 'pause', 'resume', 'line_step', 'big_step', 'small_step', 'step_into', 'read_symbols'")
+local_print("Available commands: 'attach_debugger', 'detach_debugger', 'pause', 'resume', 'line_step', 'big_step', 'small_step', 'step_into', 'read_symbols', 'add_breakpoint', 'del_breakpoint', 'toggle_breakpoint'")
 local_print("To quit: execute command 'quit'")
 
+def polling_func():
+    while 1:
+        try:
+            replies = json.loads(urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "poll_messages", "taskname": taskname})), timeout=60).read())
+        except:
+            continue
+        for reply in replies:
+            print "RES: %s" % reply['result']
+            if 'state' in reply:
+                print "STATE: %s" % reply['state']
+            if 'stack' in reply:
+                print "STACK:\n\t%s" % '\n\t'.join(['{} {: >50.50} .{}'.format(l[::-1], lnr[::-1], str(k)[::-1])[::-1] for (k, (lnr, l)) in list(enumerate(reply['stack']))])
+            if 'instruction' in reply:
+                print "INST: %s" % reply['instruction']
+            if 'phase' in reply:
+                print "PHASE: %s" % reply['phase']
+            if 'symbols' in reply:
+                print "SYMB: %s" % reply['symbols']
+            if 'triggered_bp' in reply:
+                print "BP: %s" % reply['triggered_bp']
+
+thrd = threading.Thread(target=polling_func)
+thrd.daemon = True
+thrd.start()
+            
 while 1:
     inp = raw_input().split(" ")
     action = inp[0]
+    params = {}
     if action == "quit":
         local_print("Received quit: breaking connection to Modelverse immediately!")
         break
-    elif action not in set(["attach_debugger", "detach_debugger", "pause", "resume", "line_step", "big_step", "small_step", "step_into", "read_symbols"]):
+    elif action not in set(["attach_debugger", "detach_debugger", "pause", "resume", "line_step", "big_step", "small_step", "step_into", "read_symbols", "add_breakpoint", "del_breakpoint", "toggle_breakpoint"]):
         local_print("Invalid action %s" % action)
         continue
-
-    reply = json.loads(urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": action, "taskname": taskname}))).read())
-    print "RES: %s" % reply['result']
-    if 'state' in reply:
-        print "STATE: %s" % reply['state']
-    if 'stack' in reply:
-        print "STACK:\n\t%s" % '\n\t'.join(['{} {: >50.50} .{}'.format(l[::-1], lnr[::-1], str(k)[::-1])[::-1] for (k, (lnr, l)) in list(enumerate(reply['stack']))])
-    if 'instruction' in reply:
-        print "INST: %s" % reply['instruction']
-    if 'phase' in reply:
-        print "PHASE: %s" % reply['phase']
-    if 'symbols' in reply:
-        print "SYMB: %s" % reply['symbols']
+        
+    if action == "add_breakpoint":
+        if len(inp) < 2:
+            print 'add_breakpoint requires at least 1 parameter!'
+            continue
+        inp += [None, None, True, True][(len(inp) - 2):]
+        params['breakpoint_id'] = inp[1]
+        params['file_name'] = inp[2] if inp[2] != "." else None
+        params['line_number'] = int(inp[3]) if inp[3] else None
+        params['enabled'] = bool(inp[4])
+        params['disable_on_trigger'] = bool(inp[1])
+    elif action == "del_breakpoint":
+        if len(inp) < 2:
+            print 'del_breakpoint requires 1 parameter!'
+            continue
+        params['breakpoint_id'] = inp[1]
+    elif action == "toggle_breakpoint":
+        if len(inp) < 3:
+            print 'del_breakpoint requires 2 parameters!'
+            continue
+        params['breakpoint_id'] = inp[1]
+        params['enabled'] = bool(inp[2])
+    
+    urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": action, "taskname": taskname, "args": json.dumps(params)}))).read()