blob: 8dec00b48677cf7c2974064d4f2dc611939f23c1 [file] [log] [blame]
Chris Lattner24943d22010-06-08 16:52:24 +00001//===-- Host.mm -------------------------------------------------*- 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
Greg Clayton8f3b21d2010-09-07 20:11:56 +000010#include "lldb/Host/Host.h"
Greg Clayton24b48ff2010-10-17 22:03:32 +000011
12#include <sys/types.h>
13#include <sys/stat.h>
14
15#include "lldb/Core/ArchSpec.h"
Greg Clayton36f63a92010-10-19 03:25:40 +000016#include "lldb/Core/Communication.h"
17#include "lldb/Core/ConnectionFileDescriptor.h"
Greg Clayton8f3b21d2010-09-07 20:11:56 +000018#include "lldb/Core/FileSpec.h"
19#include "lldb/Core/Log.h"
Greg Clayton24b48ff2010-10-17 22:03:32 +000020#include "lldb/Core/StreamFile.h"
21#include "lldb/Core/StreamString.h"
Chris Lattner24943d22010-06-08 16:52:24 +000022
Greg Clayton54e7afa2010-07-09 20:39:50 +000023#include "cfcpp/CFCBundle.h"
Greg Clayton24b48ff2010-10-17 22:03:32 +000024#include "cfcpp/CFCMutableArray.h"
Greg Clayton4b407112010-09-30 21:49:03 +000025#include "cfcpp/CFCMutableDictionary.h"
Greg Clayton54e7afa2010-07-09 20:39:50 +000026#include "cfcpp/CFCReleaser.h"
27#include "cfcpp/CFCString.h"
Chris Lattner24943d22010-06-08 16:52:24 +000028
Greg Clayton8f3b21d2010-09-07 20:11:56 +000029#include <objc/objc-auto.h>
30
Greg Clayton4b407112010-09-30 21:49:03 +000031#include <ApplicationServices/ApplicationServices.h>
Greg Clayton8f3b21d2010-09-07 20:11:56 +000032#include <Carbon/Carbon.h>
33#include <Foundation/Foundation.h>
Chris Lattner24943d22010-06-08 16:52:24 +000034
35using namespace lldb;
36using namespace lldb_private;
37
Chris Lattner24943d22010-06-08 16:52:24 +000038class MacOSXDarwinThread
39{
40public:
41 MacOSXDarwinThread(const char *thread_name) :
42 m_pool (nil)
43 {
44 // Register our thread with the collector if garbage collection is enabled.
45 if (objc_collectingEnabled())
46 {
47#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
48 // On Leopard and earlier there is no way objc_registerThreadWithCollector
49 // function, so we do it manually.
50 auto_zone_register_thread(auto_zone());
51#else
52 // On SnowLoepard and later we just call the thread registration function.
53 objc_registerThreadWithCollector();
54#endif
55 }
56 else
57 {
58 m_pool = [[NSAutoreleasePool alloc] init];
59 }
60
61
62 Host::SetThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name);
63 }
64
65 ~MacOSXDarwinThread()
66 {
67 if (m_pool)
68 [m_pool release];
69 }
70
71 static void PThreadDestructor (void *v)
72 {
73 delete (MacOSXDarwinThread*)v;
74 }
75
76protected:
77 NSAutoreleasePool * m_pool;
78private:
79 DISALLOW_COPY_AND_ASSIGN (MacOSXDarwinThread);
80};
81
82static pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT;
83static pthread_key_t g_thread_create_key = 0;
84
85static void
86InitThreadCreated()
87{
88 ::pthread_key_create (&g_thread_create_key, MacOSXDarwinThread::PThreadDestructor);
89}
90
Chris Lattner24943d22010-06-08 16:52:24 +000091void
92Host::ThreadCreated (const char *thread_name)
93{
94 ::pthread_once (&g_thread_create_once, InitThreadCreated);
95 if (g_thread_create_key)
96 {
97 ::pthread_setspecific (g_thread_create_key, new MacOSXDarwinThread(thread_name));
98 }
99}
100
Chris Lattner24943d22010-06-08 16:52:24 +0000101
102bool
Greg Clayton24b48ff2010-10-17 22:03:32 +0000103Host::ResolveExecutableInBundle (FileSpec &file)
Chris Lattner24943d22010-06-08 16:52:24 +0000104{
Greg Clayton8f3b21d2010-09-07 20:11:56 +0000105#if defined (__APPLE__)
Greg Clayton24b48ff2010-10-17 22:03:32 +0000106 if (file.GetFileType () == FileSpec::eFileTypeDirectory)
Chris Lattner24943d22010-06-08 16:52:24 +0000107 {
Greg Clayton24b48ff2010-10-17 22:03:32 +0000108 char path[PATH_MAX];
109 if (file.GetPath(path, sizeof(path)))
Chris Lattner24943d22010-06-08 16:52:24 +0000110 {
Greg Clayton24b48ff2010-10-17 22:03:32 +0000111 CFCBundle bundle (path);
112 CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL ());
113 if (url.get())
114 {
115 if (::CFURLGetFileSystemRepresentation (url.get(), YES, (UInt8*)path, sizeof(path)))
116 {
117 file.SetFile(path);
118 return true;
119 }
120 }
Chris Lattner24943d22010-06-08 16:52:24 +0000121 }
122 }
Greg Clayton8f3b21d2010-09-07 20:11:56 +0000123#endif
124 return false;
Jim Ingham7508e732010-08-09 23:31:02 +0000125}
Jim Ingham74989e82010-08-30 19:44:40 +0000126
Greg Clayton4b407112010-09-30 21:49:03 +0000127lldb::pid_t
128Host::LaunchApplication (const FileSpec &app_file_spec)
129{
130 char app_path[PATH_MAX];
131 app_file_spec.GetPath(app_path, sizeof(app_path));
132
133 LSApplicationParameters app_params;
134 ::bzero (&app_params, sizeof (app_params));
135 app_params.flags = kLSLaunchDefaults |
136 kLSLaunchDontAddToRecents |
Greg Clayton24b48ff2010-10-17 22:03:32 +0000137 kLSLaunchNewInstance;
138
Greg Clayton4b407112010-09-30 21:49:03 +0000139
140 FSRef app_fsref;
141 CFCString app_cfstr (app_path, kCFStringEncodingUTF8);
142
143 OSStatus error = ::FSPathMakeRef ((const UInt8 *)app_path, &app_fsref, false);
144
145 // If we found the app, then store away the name so we don't have to re-look it up.
146 if (error != noErr)
147 return LLDB_INVALID_PROCESS_ID;
148
149 app_params.application = &app_fsref;
150
151 ProcessSerialNumber psn;
152
153 error = ::LSOpenApplication (&app_params, &psn);
154
155 if (error != noErr)
156 return LLDB_INVALID_PROCESS_ID;
157
158 ::pid_t pid = LLDB_INVALID_PROCESS_ID;
159 error = ::GetProcessPID(&psn, &pid);
160 return pid;
161}
162
Greg Clayton24b48ff2010-10-17 22:03:32 +0000163lldb::pid_t
164Host::LaunchInNewTerminal
165(
166 const char **argv,
167 const char **envp,
168 const ArchSpec *arch_spec,
169 bool stop_at_entry,
170 bool disable_aslr
171)
172{
173 if (!argv || !argv[0])
174 return LLDB_INVALID_PROCESS_ID;
175
176 OSStatus error = 0;
177
178 FileSpec program (argv[0]);
179
180
Greg Clayton36f63a92010-10-19 03:25:40 +0000181 std::string unix_socket_name;
182
Greg Clayton24b48ff2010-10-17 22:03:32 +0000183 char temp_file_path[PATH_MAX];
184 const char *tmpdir = ::getenv ("TMPDIR");
185 if (tmpdir == NULL)
186 tmpdir = "/tmp/";
187 ::snprintf (temp_file_path, sizeof(temp_file_path), "%s%s-XXXXXX", tmpdir, program.GetFilename().AsCString());
188
189 if (::mktemp (temp_file_path) == NULL)
190 return LLDB_INVALID_PROCESS_ID;
191
Greg Clayton36f63a92010-10-19 03:25:40 +0000192 unix_socket_name.assign (temp_file_path);
193
Greg Clayton24b48ff2010-10-17 22:03:32 +0000194 ::strncat (temp_file_path, ".command", sizeof (temp_file_path));
195
196 StreamFile command_file (temp_file_path, "w");
197
198 FileSpec darwin_debug_file_spec;
199 if (!Host::GetLLDBPath (ePathTypeSupportExecutableDir, darwin_debug_file_spec))
200 return LLDB_INVALID_PROCESS_ID;
201 darwin_debug_file_spec.GetFilename().SetCString("darwin-debug");
202
203 if (!darwin_debug_file_spec.Exists())
204 return LLDB_INVALID_PROCESS_ID;
205
206 char launcher_path[PATH_MAX];
207 darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path));
208 command_file.Printf("\"%s\" ", launcher_path);
209
Greg Clayton36f63a92010-10-19 03:25:40 +0000210 command_file.Printf("--unix-socket=%s ", unix_socket_name.c_str());
211
Greg Clayton24b48ff2010-10-17 22:03:32 +0000212 if (arch_spec && arch_spec->IsValid())
213 {
214 command_file.Printf("--arch=%s ", arch_spec->AsCString());
215 }
216
217 if (disable_aslr)
218 {
219 command_file.PutCString("--disable-aslr ");
220 }
221
222 command_file.PutCString("-- ");
223
224 if (argv)
225 {
226 for (size_t i=0; argv[i] != NULL; ++i)
227 {
228 command_file.Printf("\"%s\" ", argv[i]);
229 }
230 }
Greg Claytonc1d37752010-10-18 01:45:30 +0000231 command_file.PutCString("\necho Process exited with status $?\n");
Greg Clayton24b48ff2010-10-17 22:03:32 +0000232 command_file.Close();
233 if (::chmod (temp_file_path, S_IRWXU | S_IRWXG) != 0)
234 return LLDB_INVALID_PROCESS_ID;
235
236 CFCMutableDictionary cf_env_dict;
237
238 const bool can_create = true;
239 if (envp)
240 {
241 for (size_t i=0; envp[i] != NULL; ++i)
242 {
243 const char *env_entry = envp[i];
244 const char *equal_pos = strchr(env_entry, '=');
245 if (equal_pos)
246 {
247 std::string env_key (env_entry, equal_pos);
248 std::string env_val (equal_pos + 1);
249 CFCString cf_env_key (env_key.c_str(), kCFStringEncodingUTF8);
250 CFCString cf_env_val (env_val.c_str(), kCFStringEncodingUTF8);
251 cf_env_dict.AddValue (cf_env_key.get(), cf_env_val.get(), can_create);
252 }
253 }
254 }
255
256 LSApplicationParameters app_params;
257 ::bzero (&app_params, sizeof (app_params));
258 app_params.flags = kLSLaunchDontAddToRecents | kLSLaunchAsync;
259 app_params.argv = NULL;
260 app_params.environment = (CFDictionaryRef)cf_env_dict.get();
261
262 CFCReleaser<CFURLRef> command_file_url (::CFURLCreateFromFileSystemRepresentation (NULL,
263 (const UInt8 *)temp_file_path,
Greg Clayton36f63a92010-10-19 03:25:40 +0000264 strlen(temp_file_path),
Greg Clayton24b48ff2010-10-17 22:03:32 +0000265 false));
266
267 CFCMutableArray urls;
268
269 // Terminal.app will open the ".command" file we have created
270 // and run our process inside it which will wait at the entry point
271 // for us to attach.
272 urls.AppendValue(command_file_url.get());
273
Greg Clayton36f63a92010-10-19 03:25:40 +0000274
275 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
276
277 Error lldb_error;
278 // Sleep and wait a bit for debugserver to start to listen...
279 ConnectionFileDescriptor file_conn;
280 char connect_url[128];
281 ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name.c_str());
282
Greg Clayton24b48ff2010-10-17 22:03:32 +0000283 ProcessSerialNumber psn;
284 error = LSOpenURLsWithRole(urls.get(), kLSRolesShell, NULL, &app_params, &psn, 1);
Greg Clayton36f63a92010-10-19 03:25:40 +0000285 if (error == noErr)
286 {
287 if (file_conn.Connect(connect_url, &lldb_error) == eConnectionStatusSuccess)
288 {
289 char pid_str[256];
290 ::bzero (pid_str, sizeof(pid_str));
291 ConnectionStatus status;
292 const size_t pid_str_len = file_conn.Read (pid_str, sizeof(pid_str), status, NULL);
293 if (pid_str_len > 0)
294 {
295 pid = atoi (pid_str);
296 // Sleep for a bit to allow the process to exec and stop at the entry point...
297 sleep(1);
298 }
299 }
300 }
Greg Clayton24b48ff2010-10-17 22:03:32 +0000301 return pid;
302}
303
Jim Ingham74989e82010-08-30 19:44:40 +0000304bool
Greg Clayton4b407112010-09-30 21:49:03 +0000305Host::OpenFileInExternalEditor (const FileSpec &file_spec, uint32_t line_no)
Jim Ingham74989e82010-08-30 19:44:40 +0000306{
307 // We attach this to an 'odoc' event to specify a particular selection
308 typedef struct {
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000309 int16_t reserved0; // must be zero
310 int16_t fLineNumber;
311 int32_t fSelStart;
312 int32_t fSelEnd;
313 uint32_t reserved1; // must be zero
314 uint32_t reserved2; // must be zero
Jim Ingham74989e82010-08-30 19:44:40 +0000315 } BabelAESelInfo;
316
Jim Inghamb0fff352010-08-31 18:05:13 +0000317 Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST);
Jim Ingham74989e82010-08-30 19:44:40 +0000318 char file_path[PATH_MAX];
319 file_spec.GetPath(file_path, PATH_MAX);
320 CFCString file_cfstr (file_path, kCFStringEncodingUTF8);
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000321 CFCReleaser<CFURLRef> file_URL (::CFURLCreateWithFileSystemPath (NULL,
322 file_cfstr.get(),
323 kCFURLPOSIXPathStyle,
324 false));
Jim Inghamb0fff352010-08-31 18:05:13 +0000325
326 if (log)
327 log->Printf("Sending source file: \"%s\" and line: %d to external editor.\n", file_path, line_no);
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000328
Jim Ingham74989e82010-08-30 19:44:40 +0000329 OSStatus error;
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000330 BabelAESelInfo file_and_line_info =
331 {
332 0, // reserved0
333 line_no - 1, // fLineNumber (zero based line number)
334 1, // fSelStart
335 1024, // fSelEnd
336 0, // reserved1
337 0 // reserved2
338 };
Jim Ingham74989e82010-08-30 19:44:40 +0000339
340 AEKeyDesc file_and_line_desc;
341
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000342 error = ::AECreateDesc (typeUTF8Text,
343 &file_and_line_info,
344 sizeof (file_and_line_info),
345 &(file_and_line_desc.descContent));
Jim Ingham74989e82010-08-30 19:44:40 +0000346
Jim Ingham74989e82010-08-30 19:44:40 +0000347 if (error != noErr)
348 {
Jim Inghamb0fff352010-08-31 18:05:13 +0000349 if (log)
350 log->Printf("Error creating AEDesc: %d.\n", error);
Jim Ingham74989e82010-08-30 19:44:40 +0000351 return false;
352 }
353
354 file_and_line_desc.descKey = keyAEPosition;
355
Greg Claytoncb0989a2010-08-31 18:56:24 +0000356 static std::string g_app_name;
Greg Clayton452bf612010-08-31 18:35:14 +0000357 static FSRef g_app_fsref;
358
Jim Ingham74989e82010-08-30 19:44:40 +0000359 LSApplicationParameters app_params;
Greg Clayton452bf612010-08-31 18:35:14 +0000360 ::bzero (&app_params, sizeof (app_params));
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000361 app_params.flags = kLSLaunchDefaults |
362 kLSLaunchDontAddToRecents |
363 kLSLaunchDontSwitch;
Greg Clayton452bf612010-08-31 18:35:14 +0000364
Jim Ingham363180d2010-08-30 23:48:25 +0000365 char *external_editor = ::getenv ("LLDB_EXTERNAL_EDITOR");
366
Greg Clayton452bf612010-08-31 18:35:14 +0000367 if (external_editor)
Jim Ingham363180d2010-08-30 23:48:25 +0000368 {
Jim Inghamb0fff352010-08-31 18:05:13 +0000369 if (log)
370 log->Printf("Looking for external editor \"%s\".\n", external_editor);
371
Greg Claytoncb0989a2010-08-31 18:56:24 +0000372 if (g_app_name.empty() || strcmp (g_app_name.c_str(), external_editor) != 0)
Jim Ingham363180d2010-08-30 23:48:25 +0000373 {
Jim Ingham363180d2010-08-30 23:48:25 +0000374 CFCString editor_name (external_editor, kCFStringEncodingUTF8);
Greg Clayton452bf612010-08-31 18:35:14 +0000375 error = ::LSFindApplicationForInfo (kLSUnknownCreator,
376 NULL,
377 editor_name.get(),
378 &g_app_fsref,
379 NULL);
Jim Ingham363180d2010-08-30 23:48:25 +0000380
381 // If we found the app, then store away the name so we don't have to re-look it up.
Greg Clayton452bf612010-08-31 18:35:14 +0000382 if (error != noErr)
Jim Inghamb0fff352010-08-31 18:05:13 +0000383 {
384 if (log)
385 log->Printf("Could not find External Editor application, error: %d.\n", error);
Jim Ingham363180d2010-08-30 23:48:25 +0000386 return false;
Jim Inghamb0fff352010-08-31 18:05:13 +0000387 }
Jim Ingham363180d2010-08-30 23:48:25 +0000388
389 }
Greg Clayton452bf612010-08-31 18:35:14 +0000390 app_params.application = &g_app_fsref;
Jim Ingham363180d2010-08-30 23:48:25 +0000391 }
392
Jim Ingham74989e82010-08-30 19:44:40 +0000393 ProcessSerialNumber psn;
394 CFCReleaser<CFArrayRef> file_array(CFArrayCreate (NULL, (const void **) file_URL.ptr_address(false), 1, NULL));
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000395 error = ::LSOpenURLsWithRole (file_array.get(),
Jim Ingham363180d2010-08-30 23:48:25 +0000396 kLSRolesAll,
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000397 &file_and_line_desc,
398 &app_params,
399 &psn,
400 1);
401
Jim Ingham74989e82010-08-30 19:44:40 +0000402 AEDisposeDesc (&(file_and_line_desc.descContent));
403
404 if (error != noErr)
405 {
Jim Inghamb0fff352010-08-31 18:05:13 +0000406 if (log)
407 log->Printf("LSOpenURLsWithRole failed, error: %d.\n", error);
408
Jim Ingham74989e82010-08-30 19:44:40 +0000409 return false;
410 }
411
412 ProcessInfoRec which_process;
413 bzero(&which_process, sizeof(which_process));
414 unsigned char ap_name[PATH_MAX];
415 which_process.processName = ap_name;
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000416 error = ::GetProcessInformation (&psn, &which_process);
Jim Ingham74989e82010-08-30 19:44:40 +0000417
Jim Inghamb0fff352010-08-31 18:05:13 +0000418 bool using_xcode;
419 if (error != noErr)
420 {
421 if (log)
422 log->Printf("GetProcessInformation failed, error: %d.\n", error);
423 using_xcode = false;
424 }
425 else
426 using_xcode = strncmp((char *) ap_name+1, "Xcode", (int) ap_name[0]) == 0;
Jim Ingham74989e82010-08-30 19:44:40 +0000427
428 // Xcode doesn't obey the line number in the Open Apple Event. So I have to send
429 // it an AppleScript to focus on the right line.
430
431 if (using_xcode)
432 {
433 static ComponentInstance osa_component = NULL;
434 static const char *as_template = "tell application \"Xcode\"\n"
435 "set doc to the first document whose path is \"%s\"\n"
436 "set the selection to paragraph %d of doc\n"
437 "--- set the selected paragraph range to {%d, %d} of doc\n"
438 "end tell\n";
439 const int chars_for_int = 32;
440 static int as_template_len = strlen (as_template);
441
442
443 char *as_str;
444 AEDesc as_desc;
445
446 if (osa_component == NULL)
447 {
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000448 osa_component = ::OpenDefaultComponent (kOSAComponentType,
449 kAppleScriptSubtype);
Jim Ingham74989e82010-08-30 19:44:40 +0000450 }
451
452 if (osa_component == NULL)
453 {
Jim Inghamb0fff352010-08-31 18:05:13 +0000454 if (log)
455 log->Printf("Could not get default AppleScript component.\n");
Jim Ingham74989e82010-08-30 19:44:40 +0000456 return false;
457 }
458
459 uint32_t as_str_size = as_template_len + strlen (file_path) + 3 * chars_for_int + 1;
460 as_str = (char *) malloc (as_str_size);
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000461 ::snprintf (as_str,
462 as_str_size - 1,
463 as_template,
464 file_path,
465 line_no,
466 line_no,
467 line_no);
Jim Ingham74989e82010-08-30 19:44:40 +0000468
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000469 error = ::AECreateDesc (typeChar,
470 as_str,
471 strlen (as_str),
472 &as_desc);
473
474 ::free (as_str);
Jim Ingham74989e82010-08-30 19:44:40 +0000475
476 if (error != noErr)
Jim Inghamb0fff352010-08-31 18:05:13 +0000477 {
478 if (log)
479 log->Printf("Failed to create AEDesc for Xcode AppleEvent: %d.\n", error);
Jim Ingham74989e82010-08-30 19:44:40 +0000480 return false;
Jim Inghamb0fff352010-08-31 18:05:13 +0000481 }
Jim Ingham74989e82010-08-30 19:44:40 +0000482
483 OSAID ret_OSAID;
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000484 error = ::OSACompileExecute (osa_component,
485 &as_desc,
486 kOSANullScript,
487 kOSAModeNeverInteract,
488 &ret_OSAID);
489
490 ::OSADispose (osa_component, ret_OSAID);
Jim Ingham74989e82010-08-30 19:44:40 +0000491
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000492 ::AEDisposeDesc (&as_desc);
Jim Ingham74989e82010-08-30 19:44:40 +0000493
494 if (error != noErr)
Jim Inghamb0fff352010-08-31 18:05:13 +0000495 {
496 if (log)
497 log->Printf("Sending AppleEvent to Xcode failed, error: %d.\n", error);
Jim Ingham74989e82010-08-30 19:44:40 +0000498 return false;
Jim Inghamb0fff352010-08-31 18:05:13 +0000499 }
Jim Ingham74989e82010-08-30 19:44:40 +0000500 }
501
502 return true;
503}