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