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