]> git.tdb.fi Git - gldbg.git/blob - source/gldbg.cpp
Track owners of breakpoints
[gldbg.git] / source / gldbg.cpp
1 /* $Id$
2
3 This file is part of gldbg
4 Copyright © 2009-2010  Mikko Rasa, Mikkosoft Productions
5 Distributed under the GPL
6 */
7
8 #include <algorithm>
9 #include <cstdlib>
10 #include <fcntl.h>
11 #include <signal.h>
12 #include <sys/poll.h>
13 #include <sys/socket.h>
14 #include <readline/readline.h>
15 #include <msp/core/except.h>
16 #include <msp/fs/dir.h>
17 #include <msp/io/print.h>
18 #include <msp/strings/lexicalcast.h>
19 #include "functions.h"
20 #include "gldbg.h"
21 #include "gldecoder.h"
22 #include "packet.h"
23 #include "tool.h"
24
25 using namespace std;
26 using namespace Msp;
27
28 Application::RegApp<GlDbg> GlDbg::reg;
29
30 GlDbg::GlDbg(int argc, char **argv):
31         cmd_interp(*this),
32         process(vector<string>(argv+1, argv+argc)),
33         buf_offset(0),
34         flushing(false),
35         got_sigchld(false),
36         stop_reason(0),
37         current_break(0)
38 {
39         FS::Path libdir = FS::get_sys_lib_dir(argv[0], "gldbg");
40         process.setenv("LD_PRELOAD", (libdir/"glwrap.so").str().c_str());
41
42         const list<Tool::Factory *> &factories = Tool::get_factories();
43         for(list<Tool::Factory *>::const_iterator i=factories.begin(); i!=factories.end(); ++i)
44                 tools.push_back((*i)->create(*this));
45 }
46
47 GlDbg::~GlDbg()
48 {
49         for(ToolList::iterator i=tools.begin(); i!=tools.end(); ++i)
50                 delete *i;
51 }
52
53 int GlDbg::main()
54 {
55         catch_signal(SIGINT);
56         catch_signal(SIGCHLD);
57         set_loop_mode(TICK_BUSY);
58
59         IO::print("GLdbg 0.0\n");
60         IO::print("Copyright © 2009-2010 Mikkosoft Productions\n");
61         IO::print("Type \"help\" for a list of commands\n");
62
63         Application::main();
64
65         return 0;
66 }
67
68 void GlDbg::launch()
69 {
70         if(process.get_state()!=Process::INACTIVE)
71                 throw InvalidState("Program is already running");
72
73         int fds[2];
74         socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
75         sock_fd = fds[0];
76
77         int flags = fcntl(sock_fd, F_GETFD);
78         fcntl(sock_fd, F_SETFD, flags|FD_CLOEXEC);
79
80         process.setenv("GLWRAP_FD", lexical_cast(fds[1]));
81         process.setenv("GLWRAP_CTRL_FD", lexical_cast(fds[1]));
82         process.launch();
83         close(fds[1]);
84
85         breakpoints.clear();
86         for(ToolList::iterator i=tools.begin(); i!=tools.end(); ++i)
87                 (*i)->process_started();
88 }
89
90 void GlDbg::send(GlPacket *pkt)
91 {
92         packet_send(pkt, sock_fd);
93 }
94
95 void GlDbg::hold()
96 {
97         GlPacket *pkt = packet_begin(FUNC_GLDHOLD);
98         send(pkt);
99 }
100
101 void GlDbg::set_breakpoint(unsigned short func, unsigned char flag, Tool *owner)
102 {
103         Breakpoint *bp = (func ? get_breakpoint(func, flag) : 0);
104         if(bp)
105                 bp->add_owner(owner);
106         else
107         {
108                 if(func)
109                 {
110                         breakpoints.push_back(Breakpoint(func, flag));
111                         breakpoints.back().add_owner(owner);
112                 }
113
114                 GlPacket *pkt = packet_begin(FUNC_GLDBREAK);
115                 packet_write_short(pkt, func);
116                 packet_write_char(pkt, flag);
117                 packet_write_char(pkt, 0);
118                 send(pkt);
119         }
120 }
121
122 void GlDbg::clear_breakpoint(unsigned short func, unsigned char flag, Tool *owner)
123 {
124         Breakpoint *bp = get_breakpoint(func, flag);
125         if(bp)
126         {
127                 bp->remove_owner(owner);
128                 if(bp->owners.empty())
129                 {
130                         if(current_break==bp)
131                                 current_break = 0;
132
133                         // XXX Inefficient.  Should get_breakpoint() return an iterator?
134                         for(BreakList::iterator i=breakpoints.begin(); i!=breakpoints.end(); ++i)
135                                 if(i->function==func && i->flag==flag)
136                                 {
137                                         breakpoints.erase(i);
138                                         break;
139                                 }
140
141                         GlPacket *pkt = packet_begin(FUNC_GLDBREAK);
142                         packet_write_short(pkt, func);
143                         packet_write_char(pkt, 0);
144                         packet_write_char(pkt, flag);
145                         send(pkt);
146                 }
147         }
148 }
149
150 void GlDbg::resume_from_break(Tool *tool)
151 {
152         if(!current_break)
153                 return;
154
155         ToolList::iterator i = find(break_holders.begin(), break_holders.end(), tool);
156         if(i!=break_holders.end())
157                 break_holders.erase(i);
158
159         if(break_holders.empty())
160                 process.resume();
161 }
162
163 void GlDbg::quit(bool force)
164 {
165         if(!force && process.get_state()!=Process::INACTIVE)
166                 throw InvalidState("Program is still running");
167         exit(0);
168 }
169
170 void GlDbg::tick()
171 {
172         if(got_sigchld)
173         {
174                 got_sigchld = false;
175                 stop_reason = process.check();
176                 if((stop_reason>>8)==3)
177                         flushing = true;
178         }
179
180         Process::State pstate = process.get_state();
181         if((pstate!=Process::INACTIVE && pstate!=Process::STOPPED) || flushing)
182                 read_stream();
183         else
184         {
185                 if(stop_reason)
186                 {
187                         if(stop_reason==0x100)
188                                 IO::print("Target process exited normally\n");
189                         else if((stop_reason>>8)==1)
190                                 IO::print("Target process exited with status %d\n", stop_reason&0xFF);
191                         else if((stop_reason>>8)==2)
192                                 IO::print("Target process terminated with signal %d\n", stop_reason&0xFF);
193                         else if((stop_reason>>8)==3)
194                                 IO::print("Target process stopped by signal %d\n", stop_reason&0xFF);
195
196                         stop_reason = 0;
197                 }
198
199                 char *line = readline("gldbg> ");
200                 if(line)
201                 {
202                         try
203                         {
204                                 cmd_interp.execute(line);
205                         }
206                         catch(const Exception &e)
207                         {
208                                 IO::print("%s\n", e.what());
209                         }
210                         free(line);
211                 }
212                 else if(pstate==Process::INACTIVE)
213                         exit(0);
214         }
215 }
216
217 void GlDbg::read_stream()
218 {
219         pollfd pfd = { sock_fd, POLLIN, 0 };
220         int ret = poll(&pfd, 1, (flushing ? 0 : -1));
221         if(ret>0)
222         {
223                 char rbuf[1024];
224                 ret = read(sock_fd, rbuf, 1024);
225                 if(ret>0)
226                 {
227                         buffer.append(rbuf, ret);
228                         while(buffer.size()>buf_offset)
229                         {
230                                 const char *data = buffer.data()+buf_offset;
231                                 unsigned len = buffer.size()-buf_offset;
232                                 GlPacket *pkt = packet_receive_str(data, &len);
233                                 if(!pkt)
234                                         break;
235
236                                 unsigned short func;
237                                 packet_read_short(pkt, (short *)&func);
238                                 if(func==FUNC_GLDBREAK)
239                                 {
240                                         packet_read_short(pkt, (short *)&func);
241                                         unsigned char flag;
242                                         packet_read_char(pkt, (char *)&flag);
243
244                                         current_break = get_breakpoint(func, flag);
245                                         bool announce = !current_break;
246                                         if(current_break)
247                                         {
248                                                 break_holders = current_break->owners;
249                                                 announce = current_break->has_owner(0);
250                                         }
251
252                                         if(announce)
253                                                 IO::print("Breakpoint: %s\n", get_function_name(func));
254                                 }
255
256                                 for(ToolList::iterator i=tools.begin(); i!=tools.end(); ++i)
257                                         (*i)->decode(data, len);
258                                 buf_offset += len;
259                         }
260                         if(buf_offset>8192)
261                         {
262                                 buffer.erase(0, buf_offset);
263                                 buf_offset = 0;
264                         }
265                 }
266         }
267         else if(flushing)
268         {
269                 flushing = false;
270
271                 if(stop_reason)
272                 {
273                         for(ToolList::iterator i=tools.begin(); i!=tools.end(); ++i)
274                                 (*i)->process_stopped(stop_reason);
275
276                         if(process.get_state()==Process::RUNNING)
277                                 current_break = 0;
278                 }
279         }
280 }
281
282 GlDbg::Breakpoint *GlDbg::get_breakpoint(unsigned short func, unsigned char flag)
283 {
284         for(BreakList::iterator i=breakpoints.begin(); i!=breakpoints.end(); ++i)
285                 if(i->function==func && i->flag==flag)
286                         return &*i;
287
288         return 0;
289 }
290
291 void GlDbg::sighandler(int sig)
292 {
293         if(sig==SIGCHLD)
294                 got_sigchld = true;
295 }
296
297
298 GlDbg::Breakpoint::Breakpoint(unsigned short f, unsigned char l):
299         function(f),
300         flag(l)
301 { }
302
303 void GlDbg::Breakpoint::add_owner(Tool *t)
304 {
305         ToolList::iterator i = find(owners.begin(), owners.end(), t);
306         if(i==owners.end())
307                 owners.push_back(t);
308 }
309
310 bool GlDbg::Breakpoint::has_owner(Tool *t) const
311 {
312         ToolList::const_iterator i = find(owners.begin(), owners.end(), t);
313         return i!=owners.end();
314 }
315
316 void GlDbg::Breakpoint::remove_owner(Tool *t)
317 {
318         ToolList::iterator i = find(owners.begin(), owners.end(), t);
319         if(i!=owners.end())
320                 owners.erase(i);
321 }