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