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