]> git.tdb.fi Git - gldbg.git/blob - source/gldbg.cpp
Retain user-set breakpoints and send them to the process on startup
[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         packet_send(pkt, sock_fd);
102 }
103
104 void GlDbg::hold()
105 {
106         GlPacket *pkt = packet_begin(FUNC_GLDHOLD);
107         send(pkt);
108 }
109
110 void GlDbg::send_breakpoint(unsigned short func, unsigned char set_flags, unsigned char clear_flags)
111 {
112         GlPacket *pkt = packet_begin(FUNC_GLDBREAK);
113         packet_write_short(pkt, func);
114         packet_write_char(pkt, set_flags);
115         packet_write_char(pkt, clear_flags);
116         send(pkt);
117 }
118
119 void GlDbg::set_breakpoint(unsigned short func, unsigned char flag, Tool *owner)
120 {
121         Breakpoint *bp = (func ? get_breakpoint(func, flag) : 0);
122         if(bp)
123                 bp->add_owner(owner);
124         else
125         {
126                 if(func)
127                 {
128                         breakpoints.push_back(Breakpoint(func, flag));
129                         breakpoints.back().add_owner(owner);
130                 }
131
132                 if(process.get_state()>=Process::RUNNING)
133                         send_breakpoint(func, flag, 0);
134         }
135 }
136
137 void GlDbg::clear_breakpoint(unsigned short func, unsigned char flag, Tool *owner)
138 {
139         Breakpoint *bp = get_breakpoint(func, flag);
140         if(bp)
141         {
142                 bp->remove_owner(owner);
143                 if(bp->owners.empty())
144                 {
145                         if(current_break==bp)
146                                 current_break = 0;
147
148                         // XXX Inefficient.  Should get_breakpoint() return an iterator?
149                         for(BreakList::iterator i=breakpoints.begin(); i!=breakpoints.end(); ++i)
150                                 if(i->function==func && i->flag==flag)
151                                 {
152                                         breakpoints.erase(i);
153                                         break;
154                                 }
155
156                         if(process.get_state()>=Process::RUNNING)
157                                 send_breakpoint(func, 0, flag);
158                 }
159         }
160 }
161
162 void GlDbg::resume_from_break(Tool *tool)
163 {
164         if(!current_break)
165                 return;
166
167         ToolList::iterator i = find(break_holders.begin(), break_holders.end(), tool);
168         if(i!=break_holders.end())
169                 break_holders.erase(i);
170
171         if(break_holders.empty())
172                 process.resume();
173 }
174
175 void GlDbg::quit(bool force)
176 {
177         if(!force && process.get_state()!=Process::INACTIVE)
178                 throw runtime_error("Program is still running");
179         exit(0);
180 }
181
182 void GlDbg::tick()
183 {
184         if(got_sigchld)
185         {
186                 got_sigchld = false;
187                 stop_reason = process.check();
188                 if((stop_reason>>8)==3)
189                         flushing = true;
190         }
191
192         Process::State pstate = process.get_state();
193         if((pstate!=Process::INACTIVE && pstate!=Process::STOPPED) || flushing)
194                 read_stream();
195         else
196         {
197                 if(stop_reason)
198                 {
199                         if(stop_reason==0x100)
200                                 printf("Target process exited normally\n");
201                         else if((stop_reason>>8)==1)
202                                 printf("Target process exited with status %d\n", stop_reason&0xFF);
203                         else if((stop_reason>>8)==2)
204                                 printf("Target process terminated with signal %d\n", stop_reason&0xFF);
205                         else if((stop_reason>>8)==3)
206                                 printf("Target process stopped by signal %d\n", stop_reason&0xFF);
207
208                         stop_reason = 0;
209                 }
210
211                 char *line = readline("gldbg> ");
212                 if(line)
213                 {
214                         try
215                         {
216                                 cmd_interp.execute(line);
217                         }
218                         catch(const exception &e)
219                         {
220                                 printf("%s\n", e.what());
221                         }
222                         free(line);
223                 }
224                 else if(pstate==Process::INACTIVE)
225                         exit(0);
226         }
227 }
228
229 void GlDbg::read_stream()
230 {
231         pollfd pfd = { sock_fd, POLLIN, 0 };
232         int ret = poll(&pfd, 1, (flushing ? 0 : -1));
233         if(ret>0)
234         {
235                 char rbuf[1024];
236                 ret = read(sock_fd, rbuf, 1024);
237                 if(ret>0)
238                 {
239                         buffer.append(rbuf, ret);
240                         while(buffer.size()>buf_offset)
241                         {
242                                 const char *data = buffer.data()+buf_offset;
243                                 unsigned len = buffer.size()-buf_offset;
244                                 GlPacket *pkt = packet_receive_str(data, &len);
245                                 if(!pkt)
246                                         break;
247
248                                 unsigned short func;
249                                 packet_read_short(pkt, (short *)&func);
250                                 if(func==FUNC_GLDBREAK)
251                                 {
252                                         packet_read_short(pkt, (short *)&func);
253                                         unsigned char flag;
254                                         packet_read_char(pkt, (char *)&flag);
255
256                                         current_break = get_breakpoint(func, flag);
257                                         bool announce = !current_break;
258                                         if(current_break)
259                                         {
260                                                 break_holders = current_break->owners;
261                                                 announce = current_break->has_owner(0);
262                                         }
263
264                                         if(announce)
265                                                 printf("Breakpoint: %s\n", get_function_name(func));
266                                 }
267
268                                 for(ToolList::iterator i=tools.begin(); i!=tools.end(); ++i)
269                                         (*i)->decode(data, len);
270                                 buf_offset += len;
271                         }
272                         if(buf_offset>8192)
273                         {
274                                 buffer.erase(0, buf_offset);
275                                 buf_offset = 0;
276                         }
277                 }
278         }
279         else if(flushing)
280         {
281                 flushing = false;
282
283                 if(stop_reason)
284                 {
285                         for(ToolList::iterator i=tools.begin(); i!=tools.end(); ++i)
286                                 (*i)->process_stopped(stop_reason);
287
288                         if(process.get_state()==Process::RUNNING)
289                                 current_break = 0;
290                 }
291         }
292 }
293
294 GlDbg::Breakpoint *GlDbg::get_breakpoint(unsigned short func, unsigned char flag)
295 {
296         for(BreakList::iterator i=breakpoints.begin(); i!=breakpoints.end(); ++i)
297                 if(i->function==func && i->flag==flag)
298                         return &*i;
299
300         return 0;
301 }
302
303 void GlDbg::sighandler(int sig)
304 {
305         if(sig==SIGCHLD)
306                 instance->got_sigchld = true;
307 }
308
309
310 GlDbg::Breakpoint::Breakpoint(unsigned short f, unsigned char l):
311         function(f),
312         flag(l)
313 { }
314
315 void GlDbg::Breakpoint::add_owner(Tool *t)
316 {
317         ToolList::iterator i = find(owners.begin(), owners.end(), t);
318         if(i==owners.end())
319                 owners.push_back(t);
320 }
321
322 bool GlDbg::Breakpoint::has_owner(Tool *t) const
323 {
324         ToolList::const_iterator i = find(owners.begin(), owners.end(), t);
325         return i!=owners.end();
326 }
327
328 void GlDbg::Breakpoint::remove_owner(Tool *t)
329 {
330         ToolList::iterator i = find(owners.begin(), owners.end(), t);
331         if(i!=owners.end())
332                 owners.erase(i);
333 }