One more instance of calling -[setStatus:] that needs to change
[macgdbp.git] / Source / GDBpConnection.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 "GDBpConnection.h"
18 #import "AppDelegate.h"
19
20 NSString *kErrorOccurredNotif = @"GDBpConnection_ErrorOccured_Notification";
21
22 @interface GDBpConnection()
23 @property(readwrite, copy) NSString *status;
24
25 - (NSString *)createCommand:(NSString *)cmd;
26 - (NSXMLDocument *)processData:(NSString *)data;
27 - (StackFrame *)createStackFrame;
28 - (void)updateStatus;
29 @end
30
31 @implementation GDBpConnection
32
33 @synthesize socket, windowController, status;
34
35 /**
36 * Creates a new DebuggerConnection and initializes the socket from the given connection
37 * paramters.
38 */
39 - (id)initWithWindowController:(DebuggerController *)wc port:(int)aPort session:(NSString *)aSession;
40 {
41 if (self = [super init])
42 {
43 port = aPort;
44 session = [aSession retain];
45 connected = NO;
46
47 windowController = [wc retain];
48
49 // now that we have our host information, open the socket
50 socket = [[SocketWrapper alloc] initWithConnection:self];
51 [socket setDelegate:self];
52 [socket connect];
53
54 [[BreakpointManager sharedManager] setConnection:self];
55 }
56 return self;
57 }
58
59 /**
60 * Deallocates the object
61 */
62 - (void)dealloc
63 {
64 [socket release];
65 [session release];
66 [windowController release];
67 [super dealloc];
68 }
69
70 /**
71 * Gets the port number
72 */
73 - (int)port
74 {
75 return port;
76 }
77
78 /**
79 * Gets the session name
80 */
81 - (NSString *)session
82 {
83 return session;
84 }
85
86 /**
87 * Returns the name of the remote host
88 */
89 - (NSString *)remoteHost
90 {
91 if (!connected)
92 {
93 return @"(DISCONNECTED)";
94 }
95 return [socket remoteHost];
96 }
97
98 /**
99 * Returns whether or not we have an active connection
100 */
101 - (BOOL)isConnected
102 {
103 return connected;
104 }
105
106 /**
107 * Called by SocketWrapper after the connection is successful. This immediately calls
108 * -[SocketWrapper receive] to clear the way for communication, though the information
109 * could be useful server information that we don't use right now.
110 */
111 - (void)socketDidAccept:(id)obj
112 {
113 connected = YES;
114 [socket receive];
115 [self refreshStatus];
116
117 // register any breakpoints that exist offline
118 for (Breakpoint *bp in [[BreakpointManager sharedManager] breakpoints])
119 {
120 [self addBreakpoint:bp];
121 }
122 }
123
124 /**
125 * Receives errors from the SocketWrapper and updates the display
126 */
127 - (void)errorEncountered:(NSString *)error
128 {
129 [[NSNotificationCenter defaultCenter]
130 postNotificationName:kErrorOccurredNotif
131 object:self
132 userInfo:[NSDictionary
133 dictionaryWithObject:error
134 forKey:@"NSString"
135 ]
136 ];
137 }
138
139 /**
140 * Reestablishes communication with the remote debugger so that a new connection doesn't have to be
141 * created every time you want to debug a page
142 */
143 - (void)reconnect
144 {
145 [socket close];
146 self.status = @"Connecting";
147 [windowController resetDisplays];
148 [socket connect];
149 }
150
151 /**
152 * Tells the debugger to continue running the script
153 */
154 - (void)run
155 {
156 [socket send:[self createCommand:@"run"]];
157 [socket receive];
158 [self refreshStatus];
159 }
160
161 /**
162 * Method that runs tells the debugger to give us its status and will update the status text on the window
163 */
164 - (void)refreshStatus
165 {
166 [self updateStatus];
167 if ([status isEqualToString:@"Break"])
168 {
169 [self updateStackTraceAndRegisters];
170 }
171 }
172
173 /**
174 * Tells the debugger to step into the current command.
175 */
176 - (StackFrame *)stepIn
177 {
178 [socket send:[self createCommand:@"step_into"]];
179 [socket receive];
180
181 StackFrame *frame = [self createStackFrame];
182 [self refreshStatus];
183
184 return frame;
185 }
186
187 /**
188 * Tells the debugger to step out of the current context
189 */
190 - (StackFrame *)stepOut
191 {
192 [socket send:[self createCommand:@"step_out"]];
193 [socket receive];
194
195 StackFrame *frame = [self createStackFrame];
196 [self refreshStatus];
197
198 return frame;
199 }
200
201 /**
202 * Tells the debugger to step over the current function
203 */
204 - (StackFrame *)stepOver
205 {
206 [socket send:[self createCommand:@"step_over"]];
207 [socket receive];
208
209 StackFrame *frame = [self createStackFrame];
210 [self refreshStatus];
211
212 return frame;
213 }
214
215 /**
216 * This function queries the debug server for the current stacktrace and all the registers on
217 * level one. If a user then tries to expand past level one... TOOD: HOLY CRAP WHAT DO WE DO PAST LEVEL 1?
218 */
219 - (void)updateStackTraceAndRegisters
220 {
221 // do the registers
222 [socket send:[self createCommand:@"context_get"]];
223 [windowController setRegister:[self processData:[socket receive]]];
224 }
225
226 /**
227 * Tells the debugger engine to get a specifc property. This also takes in the NSXMLElement
228 * that requested it so that the child can be attached.
229 */
230 - (NSArray *)getProperty:(NSString *)property
231 {
232 [socket send:[self createCommand:[NSString stringWithFormat:@"property_get -n \"%@\"", property]]];
233
234 NSXMLDocument *doc = [self processData:[socket receive]];
235
236 /*
237 <response>
238 <property> <!-- this is the one we requested -->
239 <property ... /> <!-- these are what we want -->
240 </property>
241 </repsonse>
242 */
243
244 // we now have to detach all the children so we can insert them into another document
245 NSXMLElement *parent = (NSXMLElement *)[[doc rootElement] childAtIndex:0];
246 NSArray *children = [parent children];
247 [parent setChildren:nil];
248 return children;
249 }
250
251 #pragma mark Breakpoints
252
253 /**
254 * Send an add breakpoint command
255 */
256 - (void)addBreakpoint:(Breakpoint *)bp
257 {
258 if (!connected)
259 {
260 return;
261 }
262
263 NSString *cmd = [self createCommand:[NSString stringWithFormat:@"breakpoint_set -t line -f %@ -n %i", [bp file], [bp line]]];
264 [socket send:cmd];
265 NSXMLDocument *info = [self processData:[socket receive]];
266 [bp setDebuggerId:[[[[info rootElement] attributeForName:@"id"] stringValue] intValue]];
267 }
268
269 /**
270 * Removes a breakpoint
271 */
272 - (void)removeBreakpoint:(Breakpoint *)bp
273 {
274 if (!connected)
275 {
276 return;
277 }
278
279 [socket send:[self createCommand:[NSString stringWithFormat:@"breakpoint_remove -d %i", [bp debuggerId]]]];
280 [socket receive];
281 }
282
283 #pragma mark Private
284
285 /**
286 * Helper method to create a string command with the -i <session> automatically tacked on
287 */
288 - (NSString *)createCommand:(NSString *)cmd
289 {
290 return [NSString stringWithFormat:@"%@ -i %@", cmd, session];
291 }
292
293 /**
294 * Helper function to parse the NSData into an NSXMLDocument
295 */
296 - (NSXMLDocument *)processData:(NSString *)data
297 {
298 NSError *parseError = nil;
299 NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:data options:0 error:&parseError];
300 if (parseError)
301 {
302 NSLog(@"Could not parse XML? --- %@", parseError);
303 NSLog(@"Error UserInfo: %@", [parseError userInfo]);
304 NSLog(@"This is the XML Document: %@", data);
305 return nil;
306 }
307
308 // check and see if there's an error
309 NSArray *error = [[doc rootElement] elementsForName:@"error"];
310 if ([error count] > 0)
311 {
312 [self errorEncountered:[[[[error objectAtIndex:0] children] objectAtIndex:0] stringValue]];
313 return nil;
314 }
315
316 return [doc autorelease];
317 }
318
319 /**
320 * Creates a StackFrame based on the current position in the debugger
321 */
322 - (StackFrame *)createStackFrame
323 {
324 [socket send:[self createCommand:@"stack_get -d 0"]];
325 NSXMLDocument *doc = [self processData:[socket receive]];
326
327 NSXMLElement *xmlframe = [[[doc rootElement] children] objectAtIndex:0];
328 StackFrame *frame = [[StackFrame alloc]
329 initWithIndex:0
330 withFilename:[[xmlframe attributeForName:@"filename"] stringValue]
331 withSource:nil
332 atLine:[[[xmlframe attributeForName:@"lineno"] stringValue] intValue]
333 inFunction:[[xmlframe attributeForName:@"where"] stringValue]
334 withContexts:nil
335 ];
336
337 return [frame autorelease];
338 }
339
340 /**
341 * Fetches the value of and sets the status instance variable
342 */
343 - (void)updateStatus
344 {
345 [socket send:[self createCommand:@"status"]];
346 NSXMLDocument *doc = [self processData:[socket receive]];
347 self.status = [[[[doc rootElement] attributeForName:@"status"] stringValue] capitalizedString];
348
349 if ([status isEqualToString:@"Stopped"] || [status isEqualToString:@"Stopping"])
350 {
351 connected = NO;
352 [socket close];
353 self.status = @"Stopped";
354 }
355 }
356
357 @end