After we establish a connection, we need to resend any offline breakpoints
[macgdbp.git] / Source / DebuggerConnection.m
1 /*
2 * MacGDBp
3 * Copyright (c) 2007 - 2008, Blue Static <http://www.bluestatic.org>
4 *
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17 #import "DebuggerConnection.h"
18 #import "AppDelegate.h"
19
20 @interface DebuggerConnection (Private)
21 - (NSString *)createCommand:(NSString *)cmd;
22 - (NSXMLDocument *)processData:(NSString *)data;
23 @end
24
25 @implementation DebuggerConnection
26
27 @synthesize socket, windowController;
28
29 /**
30 * Creates a new DebuggerConnection and initializes the socket from the given connection
31 * paramters.
32 */
33 - (id)initWithWindowController:(DebuggerWindowController *)wc port:(int)aPort session:(NSString *)aSession;
34 {
35 if (self = [super init])
36 {
37 port = aPort;
38 session = aSession;
39 connected = NO;
40
41 windowController = wc;
42
43 // now that we have our host information, open the socket
44 socket = [[SocketWrapper alloc] initWithConnection:self];
45 [socket setDelegate:self];
46 [socket connect];
47
48 [[BreakpointManager sharedManager] setConnection:self];
49 }
50 return self;
51 }
52
53 /**
54 * Gets the port number
55 */
56 - (int)port
57 {
58 return port;
59 }
60
61 /**
62 * Gets the session name
63 */
64 - (NSString *)session
65 {
66 return session;
67 }
68
69 /**
70 * Returns the name of the remote host
71 */
72 - (NSString *)remoteHost
73 {
74 if (!connected)
75 {
76 return @"(DISCONNECTED)";
77 }
78 return [socket remoteHost];
79 }
80
81 /**
82 * Returns whether or not we have an active connection
83 */
84 - (BOOL)isConnected
85 {
86 return connected;
87 }
88
89 /**
90 * Called by SocketWrapper after the connection is successful. This immediately calls
91 * -[SocketWrapper receive] to clear the way for communication, though the information
92 * could be useful server information that we don't use right now.
93 */
94 - (void)socketDidAccept:(id)obj
95 {
96 connected = YES;
97 [socket receive];
98 [self refreshStatus];
99
100 // register any breakpoints that exist offline
101 for (Breakpoint *bp in [[BreakpointManager sharedManager] breakpoints])
102 {
103 [self addBreakpoint:bp];
104 }
105 }
106
107 /**
108 * Receives errors from the SocketWrapper and updates the display
109 */
110 - (void)errorEncountered:(NSString *)error
111 {
112 [windowController setError:error];
113 }
114
115 /**
116 * Reestablishes communication with the remote debugger so that a new connection doesn't have to be
117 * created every time you want to debug a page
118 */
119 - (void)reconnect
120 {
121 [socket close];
122 [windowController setStatus:@"Connecting"];
123 [windowController resetDisplays];
124 [socket connect];
125 }
126
127 /**
128 * Tells the debugger to continue running the script
129 */
130 - (void)run
131 {
132 [socket send:[self createCommand:@"run"]];
133 [socket receive];
134 [self refreshStatus];
135 }
136
137 /**
138 * Method that runs tells the debugger to give us its status and will update the status text on the window
139 */
140 - (void)refreshStatus
141 {
142 [socket send:[self createCommand:@"status"]];
143
144 NSXMLDocument *doc = [self processData:[socket receive]];
145 NSString *status = [[[doc rootElement] attributeForName:@"status"] stringValue];
146 [windowController setStatus:[status capitalizedString]];
147
148 if ([status isEqualToString:@"break"])
149 {
150 [self updateStackTraceAndRegisters];
151 }
152 else if ([status isEqualToString:@"stopped"])
153 {
154 connected = NO;
155 [socket close];
156 [windowController setStatus:[status capitalizedString]];
157 }
158 }
159
160 /**
161 * Tells the debugger to step into the current command.
162 */
163 - (void)stepIn
164 {
165 [socket send:[self createCommand:@"step_into"]];
166 [socket receive];
167 [self refreshStatus];
168 }
169
170 /**
171 * Tells the debugger to step out of the current context
172 */
173 - (void)stepOut
174 {
175 [socket send:[self createCommand:@"step_out"]];
176 [socket receive];
177 [self refreshStatus];
178 }
179
180 /**
181 * Tells the debugger to step over the current function
182 */
183 - (void)stepOver
184 {
185 [socket send:[self createCommand:@"step_over"]];
186 [socket receive];
187 [self refreshStatus];
188 }
189
190 /**
191 * This function queries the debug server for the current stacktrace and all the registers on
192 * level one. If a user then tries to expand past level one... TOOD: HOLY CRAP WHAT DO WE DO PAST LEVEL 1?
193 */
194 - (void)updateStackTraceAndRegisters
195 {
196 // do the stack
197 [socket send:[self createCommand:@"stack_get"]];
198 NSXMLDocument *doc = [self processData:[socket receive]];
199 NSArray *children = [[doc rootElement] children];
200 NSMutableArray *stack = [NSMutableArray array];
201 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
202 for (int i = 0; i < [children count]; i++)
203 {
204 NSArray *attrs = [[children objectAtIndex:i] attributes];
205 for (int j = 0; j < [attrs count]; j++)
206 {
207 [dict setValue:[[attrs objectAtIndex:j] stringValue] forKey:[[attrs objectAtIndex:j] name]];
208 }
209 [stack addObject:dict];
210 dict = [NSMutableDictionary dictionary];
211 }
212 [windowController setStack:stack];
213
214 // do the registers
215 [socket send:[self createCommand:@"context_get"]];
216 [windowController setRegister:[self processData:[socket receive]]];
217 }
218
219 /**
220 * Tells the debugger engine to get a specifc property. This also takes in the NSXMLElement
221 * that requested it so that the child can be attached.
222 */
223 - (NSArray *)getProperty:(NSString *)property
224 {
225 [socket send:[self createCommand:[NSString stringWithFormat:@"property_get -n \"%@\"", property]]];
226
227 NSXMLDocument *doc = [self processData:[socket receive]];
228
229 /*
230 <response>
231 <property> <!-- this is the one we requested -->
232 <property ... /> <!-- these are what we want -->
233 </property>
234 </repsonse>
235 */
236
237 // we now have to detach all the children so we can insert them into another document
238 NSXMLElement *parent = (NSXMLElement *)[[doc rootElement] childAtIndex:0];
239 NSArray *children = [parent children];
240 [parent setChildren:nil];
241 return children;
242 }
243
244 #pragma mark Breakpoints
245
246 /**
247 * Send an add breakpoint command
248 */
249 - (void)addBreakpoint:(Breakpoint *)bp
250 {
251 if (!connected)
252 {
253 return;
254 }
255
256 NSString *cmd = [self createCommand:[NSString stringWithFormat:@"breakpoint_set -t line -f %@ -n %i", [bp file], [bp line]]];
257 [socket send:cmd];
258 NSXMLDocument *info = [self processData:[socket receive]];
259 [bp setDebuggerId:[[[[info rootElement] attributeForName:@"id"] stringValue] intValue]];
260 }
261
262 /**
263 * Removes a breakpoint
264 */
265 - (void)removeBreakpoint:(Breakpoint *)bp
266 {
267 if (!connected)
268 {
269 return;
270 }
271
272 [socket send:[self createCommand:[NSString stringWithFormat:@"breakpoint_remove -d %i", [bp debuggerId]]]];
273 [socket receive];
274 }
275
276 #pragma mark Private
277
278 /**
279 * Helper method to create a string command with the -i <session> automatically tacked on
280 */
281 - (NSString *)createCommand:(NSString *)cmd
282 {
283 return [NSString stringWithFormat:@"%@ -i %@", cmd, session];
284 }
285
286 /**
287 * Helper function to parse the NSData into an NSXMLDocument
288 */
289 - (NSXMLDocument *)processData:(NSString *)data
290 {
291 NSError *parseError = nil;
292 NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:data options:0 error:&parseError];
293 if (parseError)
294 {
295 NSLog(@"Could not parse XML? --- %@", parseError);
296 NSLog(@"Error UserInfo: %@", [parseError userInfo]);
297 NSLog(@"This is the XML Document: %@", data);
298 return nil;
299 }
300
301 // check and see if there's an error
302 NSArray *error = [[doc rootElement] elementsForName:@"error"];
303 if ([error count] > 0)
304 {
305 [windowController setError:[[[[error objectAtIndex:0] children] objectAtIndex:0] stringValue]];
306 return nil;
307 }
308
309 return doc;
310 }
311
312 @end