lldb-platform.cpp revision 263367
1//===-- lldb-platform.cpp ---------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "lldb/lldb-python.h"
11
12// C Includes
13#include <errno.h>
14#include <getopt.h>
15#include <signal.h>
16#include <stdint.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21// C++ Includes
22
23// Other libraries and framework includes
24#include "lldb/lldb-private-log.h"
25#include "lldb/Core/Error.h"
26#include "lldb/Core/ConnectionFileDescriptor.h"
27#include "lldb/Core/ConnectionMachPort.h"
28#include "lldb/Core/Debugger.h"
29#include "lldb/Core/StreamFile.h"
30#include "lldb/Host/OptionParser.h"
31#include "lldb/Interpreter/CommandInterpreter.h"
32#include "lldb/Interpreter/CommandReturnObject.h"
33#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h"
34#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
35using namespace lldb;
36using namespace lldb_private;
37
38//----------------------------------------------------------------------
39// option descriptors for getopt_long_only()
40//----------------------------------------------------------------------
41
42int g_debug = 0;
43int g_verbose = 0;
44int g_stay_alive = 0;
45
46static struct option g_long_options[] =
47{
48    { "debug",              no_argument,        &g_debug,           1   },
49    { "verbose",            no_argument,        &g_verbose,         1   },
50    { "stay-alive",         no_argument,        &g_stay_alive,      1   },
51    { "listen",             required_argument,  NULL,               'L' },
52    { "port-offset",        required_argument,  NULL,               'p' },
53    { "gdbserver-port",     required_argument,  NULL,               'P' },
54    { "min-gdbserver-port", required_argument,  NULL,               'm' },
55    { "max-gdbserver-port", required_argument,  NULL,               'M' },
56    { "lldb-command",       required_argument,  NULL,               'c' },
57    { NULL,                 0,                  NULL,               0   }
58};
59
60#if defined (__APPLE__)
61#define LOW_PORT    (IPPORT_RESERVED)
62#define HIGH_PORT   (IPPORT_HIFIRSTAUTO)
63#else
64#define LOW_PORT    (1024u)
65#define HIGH_PORT   (49151u)
66#endif
67
68
69//----------------------------------------------------------------------
70// Watch for signals
71//----------------------------------------------------------------------
72void
73signal_handler(int signo)
74{
75    switch (signo)
76    {
77    case SIGHUP:
78        // Use SIGINT first, if that does not work, use SIGHUP as a last resort.
79        // And we should not call exit() here because it results in the global destructors
80        // to be invoked and wreaking havoc on the threads still running.
81        Host::SystemLog(Host::eSystemLogWarning, "SIGHUP received, exiting lldb-platform...\n");
82        abort();
83        break;
84    }
85}
86
87static void
88display_usage (const char *progname)
89{
90    fprintf(stderr, "Usage:\n  %s [--log-file log-file-path] [--log-flags flags] --listen port\n", progname);
91    exit(0);
92}
93
94//----------------------------------------------------------------------
95// main
96//----------------------------------------------------------------------
97int
98main (int argc, char *argv[])
99{
100    const char *progname = argv[0];
101    signal (SIGPIPE, SIG_IGN);
102    signal (SIGHUP, signal_handler);
103    int long_option_index = 0;
104    Error error;
105    std::string listen_host_port;
106    int ch;
107    Debugger::Initialize(NULL);
108
109    lldb::DebuggerSP debugger_sp = Debugger::CreateInstance ();
110
111    debugger_sp->SetInputFileHandle(stdin, false);
112    debugger_sp->SetOutputFileHandle(stdout, false);
113    debugger_sp->SetErrorFileHandle(stderr, false);
114
115    GDBRemoteCommunicationServer::PortMap gdbserver_portmap;
116    int min_gdbserver_port = 0;
117    int max_gdbserver_port = 0;
118    uint16_t port_offset = 0;
119
120    std::vector<std::string> lldb_commands;
121    bool show_usage = false;
122    int option_error = 0;
123
124    std::string short_options(OptionParser::GetShortOptionString(g_long_options));
125
126#if __GLIBC__
127    optind = 0;
128#else
129    optreset = 1;
130    optind = 1;
131#endif
132
133    while ((ch = getopt_long_only(argc, argv, short_options.c_str(), g_long_options, &long_option_index)) != -1)
134    {
135        switch (ch)
136        {
137        case 0:   // Any optional that auto set themselves will return 0
138            break;
139
140        case 'L':
141            listen_host_port.append (optarg);
142            break;
143
144        case 'p':
145            {
146                char *end = NULL;
147                long tmp_port_offset = strtoul(optarg, &end, 0);
148                if (end && *end == '\0')
149                {
150                    if (LOW_PORT <= tmp_port_offset && tmp_port_offset <= HIGH_PORT)
151                    {
152                        port_offset = (uint16_t)tmp_port_offset;
153                    }
154                    else
155                    {
156                        fprintf (stderr, "error: port offset %li is not in the valid user port range of %u - %u\n", tmp_port_offset, LOW_PORT, HIGH_PORT);
157                        option_error = 5;
158                    }
159                }
160                else
161                {
162                    fprintf (stderr, "error: invalid port offset string %s\n", optarg);
163                    option_error = 4;
164                }
165            }
166            break;
167
168        case 'P':
169        case 'm':
170        case 'M':
171            {
172                char *end = NULL;
173                long portnum = strtoul(optarg, &end, 0);
174                if (end && *end == '\0')
175                {
176                    if (LOW_PORT <= portnum && portnum <= HIGH_PORT)
177                    {
178                        if (ch  == 'P')
179                            gdbserver_portmap[(uint16_t)portnum] = LLDB_INVALID_PROCESS_ID;
180                        else if (ch == 'm')
181                            min_gdbserver_port = portnum;
182                        else
183                            max_gdbserver_port = portnum;
184                    }
185                    else
186                    {
187                        fprintf (stderr, "error: port number %li is not in the valid user port range of %u - %u\n", portnum, LOW_PORT, HIGH_PORT);
188                        option_error = 1;
189                    }
190                }
191                else
192                {
193                    fprintf (stderr, "error: invalid port number string %s\n", optarg);
194                    option_error = 2;
195                }
196            }
197            break;
198
199        case 'c':
200            lldb_commands.push_back(optarg);
201            break;
202
203        case 'h':   /* fall-through is intentional */
204        case '?':
205            show_usage = true;
206            break;
207        }
208    }
209
210    // Make a port map for a port range that was specified.
211    if (min_gdbserver_port < max_gdbserver_port)
212    {
213        for (uint16_t port = min_gdbserver_port; port < max_gdbserver_port; ++port)
214            gdbserver_portmap[port] = LLDB_INVALID_PROCESS_ID;
215    }
216    else if (min_gdbserver_port != max_gdbserver_port)
217    {
218        fprintf (stderr, "error: --min-gdbserver-port (%u) is greater than --max-gdbserver-port (%u)\n", min_gdbserver_port, max_gdbserver_port);
219        option_error = 3;
220
221    }
222
223    // Print usage and exit if no listening port is specified.
224    if (listen_host_port.empty())
225        show_usage = true;
226
227    if (show_usage || option_error)
228    {
229        display_usage(progname);
230        exit(option_error);
231    }
232
233    // Skip any options we consumed with getopt_long_only
234    argc -= optind;
235    argv += optind;
236
237    // Execute any LLDB commands that we were asked to evaluate.
238    for (const auto &lldb_command : lldb_commands)
239    {
240        lldb_private::CommandReturnObject result;
241        printf("(lldb) %s\n", lldb_command.c_str());
242        debugger_sp->GetCommandInterpreter().HandleCommand(lldb_command.c_str(), eLazyBoolNo, result);
243        const char *output = result.GetOutputData();
244        if (output && output[0])
245            puts(output);
246    }
247
248
249    do {
250        GDBRemoteCommunicationServer gdb_server (true);
251
252        if (port_offset > 0)
253            gdb_server.SetPortOffset(port_offset);
254
255        if (!gdbserver_portmap.empty())
256        {
257            gdb_server.SetPortMap(std::move(gdbserver_portmap));
258        }
259
260        if (!listen_host_port.empty())
261        {
262            std::unique_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor());
263            if (conn_ap.get())
264            {
265                for (int j = 0; j < listen_host_port.size(); j++)
266                {
267                    char c = listen_host_port[j];
268                    if (c > '9' || c < '0')
269                        printf("WARNING: passing anything but a number as argument to --listen will most probably make connecting impossible.\n");
270                }
271                std::auto_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor());
272                if (conn_ap.get())
273                {
274                    std::string connect_url ("listen://");
275                    connect_url.append(listen_host_port.c_str());
276
277                    printf ("Listening for a connection on %s...\n", listen_host_port.c_str());
278                    if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess)
279                    {
280                        printf ("Connection established.\n");
281                        gdb_server.SetConnection (conn_ap.release());
282                    }
283                }
284            }
285
286            if (gdb_server.IsConnected())
287            {
288                // After we connected, we need to get an initial ack from...
289                if (gdb_server.HandshakeWithClient(&error))
290                {
291                    bool interrupt = false;
292                    bool done = false;
293                    while (!interrupt && !done)
294                    {
295                        if (!gdb_server.GetPacketAndSendResponse (UINT32_MAX, error, interrupt, done))
296                            break;
297                    }
298
299                    if (error.Fail())
300                    {
301                        fprintf(stderr, "error: %s\n", error.AsCString());
302                    }
303                }
304                else
305                {
306                    fprintf(stderr, "error: handshake with client failed\n");
307                }
308            }
309        }
310    } while (g_stay_alive);
311
312    Debugger::Terminate();
313
314    fprintf(stderr, "lldb-platform exiting...\n");
315
316    return 0;
317}
318