]> git.tdb.fi Git - gldbg.git/blob - source/gldbg.cpp
Enable bidirectional communication between gldbg and glwrap.so
[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 <cstdlib>
9 #include <fcntl.h>
10 #include <signal.h>
11 #include <sys/poll.h>
12 #include <sys/socket.h>
13 #include <readline/readline.h>
14 #include <msp/core/except.h>
15 #include <msp/fs/dir.h>
16 #include <msp/io/print.h>
17 #include <msp/strings/lexicalcast.h>
18 #include "functions.h"
19 #include "gldbg.h"
20 #include "gldecoder.h"
21 #include "packet.h"
22 #include "tool.h"
23
24 using namespace std;
25 using namespace Msp;
26
27 Application::RegApp<GlDbg> GlDbg::reg;
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 {
36         FS::Path libdir = FS::get_sys_lib_dir(argv[0], "gldbg");
37         process.setenv("LD_PRELOAD", (libdir/"glwrap.so").str().c_str());
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(list<Tool *>::iterator i=tools.begin(); i!=tools.end(); ++i)
47                 delete *i;
48 }
49
50 int GlDbg::main()
51 {
52         catch_signal(SIGINT);
53         catch_signal(SIGCHLD);
54         set_loop_mode(TICK_BUSY);
55
56         IO::print("GLdbg 0.0\n");
57         IO::print("Copyright © 2009-2010 Mikkosoft Productions\n");
58         IO::print("Type \"help\" for a list of commands\n");
59
60         Application::main();
61
62         return 0;
63 }
64
65 void GlDbg::launch()
66 {
67         if(process.get_state()!=Process::INACTIVE)
68                 throw InvalidState("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", lexical_cast(fds[1]));
78         process.setenv("GLWRAP_CTRL_FD", lexical_cast(fds[1]));
79         process.launch();
80         close(fds[1]);
81 }
82
83 void GlDbg::set_breakpoint(unsigned short func, char flag)
84 {
85         GlPacket *pkt = packet_begin(FUNC_GLDBREAK);
86         packet_write_short(pkt, func);
87         packet_write_char(pkt, flag);
88         packet_write_char(pkt, 0);
89         packet_send(pkt, sock_fd);
90 }
91
92 void GlDbg::clear_breakpoint(unsigned short func, char flag)
93 {
94         GlPacket *pkt = packet_begin(FUNC_GLDBREAK);
95         packet_write_short(pkt, func);
96         packet_write_char(pkt, 0);
97         packet_write_char(pkt, flag);
98         packet_send(pkt, sock_fd);
99 }
100
101 void GlDbg::quit(bool force)
102 {
103         if(!force && process.get_state()!=Process::INACTIVE)
104                 throw InvalidState("Program is still running");
105         exit(0);
106 }
107
108 void GlDbg::tick()
109 {
110         if(got_sigchld)
111         {
112                 got_sigchld = false;
113                 int ret = process.check();
114                 if(ret==0x100)
115                         IO::print("Target process exited normally\n");
116                 else if((ret>>8)==1)
117                         IO::print("Target process exited with status %d\n", ret&0xFF);
118                 else if((ret>>8)==2)
119                         IO::print("Target process terminated with signal %d\n", ret&0xFF);
120                 else if((ret>>8)==3)
121                 {
122                         IO::print("Target process stopped by signal %d\n", ret&0xFF);
123                         flushing = true;
124                 }
125         }
126
127         Process::State pstate = process.get_state();
128         if((pstate!=Process::INACTIVE && pstate!=Process::STOPPED) || flushing)
129                 read_stream();
130         else
131         {
132                 char *line = readline("gldbg> ");
133                 if(line)
134                 {
135                         try
136                         {
137                                 cmd_interp.execute(line);
138                         }
139                         catch(const Exception &e)
140                         {
141                                 IO::print("%s\n", e.what());
142                         }
143                         free(line);
144                 }
145                 else if(pstate==Process::INACTIVE)
146                         exit(0);
147         }
148 }
149
150 void GlDbg::read_stream()
151 {
152         pollfd pfd = { sock_fd, POLLIN, 0 };
153         int ret = poll(&pfd, 1, (flushing ? 0 : -1));
154         if(ret>0)
155         {
156                 char rbuf[1024];
157                 ret = read(sock_fd, rbuf, 1024);
158                 if(ret>0)
159                 {
160                         buffer.append(rbuf, ret);
161                         while(buffer.size()>buf_offset)
162                         {
163                                 const char *data = buffer.data()+buf_offset;
164                                 unsigned len = buffer.size()-buf_offset;
165                                 GlPacket *pkt = packet_receive_str(data, &len);
166                                 if(!pkt)
167                                         break;
168
169                                 unsigned short func;
170                                 packet_read_short(pkt, (short *)&func);
171                                 if(func==FUNC_GLDBREAK)
172                                 {
173                                         packet_read_short(pkt, (short *)&func);
174                                         IO::print("Breakpoint: %s\n", get_function_name(func));
175                                 }
176
177                                 for(list<Tool *>::iterator i=tools.begin(); i!=tools.end(); ++i)
178                                         (*i)->decode(data, len);
179                                 buf_offset += len;
180                         }
181                         if(buf_offset>8192)
182                         {
183                                 buffer.erase(0, buf_offset);
184                                 buf_offset = 0;
185                         }
186                 }
187         }
188         else if(flushing)
189                 flushing = false;
190 }
191
192 void GlDbg::sighandler(int sig)
193 {
194         if(sig==SIGCHLD)
195                 got_sigchld = true;
196 }