blob: df4f92199d13854821db00c47e6c9dfa4ef90ff7 [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
Greg Clayton6bd1a9e2010-10-19 18:15:50 +000012#include <libproc.h>
13#include <sys/proc.h>
Greg Clayton24b48ff2010-10-17 22:03:32 +000014#include <sys/stat.h>
Greg Clayton6bd1a9e2010-10-19 18:15:50 +000015#include <sys/types.h>
Greg Clayton24b48ff2010-10-17 22:03:32 +000016
17#include "lldb/Core/ArchSpec.h"
Greg Clayton36f63a92010-10-19 03:25:40 +000018#include "lldb/Core/Communication.h"
19#include "lldb/Core/ConnectionFileDescriptor.h"
Greg Clayton8f3b21d2010-09-07 20:11:56 +000020#include "lldb/Core/FileSpec.h"
21#include "lldb/Core/Log.h"
Greg Clayton24b48ff2010-10-17 22:03:32 +000022#include "lldb/Core/StreamFile.h"
23#include "lldb/Core/StreamString.h"
Chris Lattner24943d22010-06-08 16:52:24 +000024
Greg Clayton54e7afa2010-07-09 20:39:50 +000025#include "cfcpp/CFCBundle.h"
Greg Clayton24b48ff2010-10-17 22:03:32 +000026#include "cfcpp/CFCMutableArray.h"
Greg Clayton4b407112010-09-30 21:49:03 +000027#include "cfcpp/CFCMutableDictionary.h"
Greg Clayton54e7afa2010-07-09 20:39:50 +000028#include "cfcpp/CFCReleaser.h"
29#include "cfcpp/CFCString.h"
Chris Lattner24943d22010-06-08 16:52:24 +000030
Greg Clayton8f3b21d2010-09-07 20:11:56 +000031#include <objc/objc-auto.h>
32
Greg Clayton4b407112010-09-30 21:49:03 +000033#include <ApplicationServices/ApplicationServices.h>
Greg Clayton8f3b21d2010-09-07 20:11:56 +000034#include <Carbon/Carbon.h>
35#include <Foundation/Foundation.h>
Chris Lattner24943d22010-06-08 16:52:24 +000036
37using namespace lldb;
38using namespace lldb_private;
39
Chris Lattner24943d22010-06-08 16:52:24 +000040class MacOSXDarwinThread
41{
42public:
43 MacOSXDarwinThread(const char *thread_name) :
44 m_pool (nil)
45 {
46 // Register our thread with the collector if garbage collection is enabled.
47 if (objc_collectingEnabled())
48 {
49#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
50 // On Leopard and earlier there is no way objc_registerThreadWithCollector
51 // function, so we do it manually.
52 auto_zone_register_thread(auto_zone());
53#else
54 // On SnowLoepard and later we just call the thread registration function.
55 objc_registerThreadWithCollector();
56#endif
57 }
58 else
59 {
60 m_pool = [[NSAutoreleasePool alloc] init];
61 }
62
63
64 Host::SetThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name);
65 }
66
67 ~MacOSXDarwinThread()
68 {
69 if (m_pool)
70 [m_pool release];
71 }
72
73 static void PThreadDestructor (void *v)
74 {
75 delete (MacOSXDarwinThread*)v;
76 }
77
78protected:
79 NSAutoreleasePool * m_pool;
80private:
81 DISALLOW_COPY_AND_ASSIGN (MacOSXDarwinThread);
82};
83
84static pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT;
85static pthread_key_t g_thread_create_key = 0;
86
87static void
88InitThreadCreated()
89{
90 ::pthread_key_create (&g_thread_create_key, MacOSXDarwinThread::PThreadDestructor);
91}
92
Chris Lattner24943d22010-06-08 16:52:24 +000093void
94Host::ThreadCreated (const char *thread_name)
95{
96 ::pthread_once (&g_thread_create_once, InitThreadCreated);
97 if (g_thread_create_key)
98 {
99 ::pthread_setspecific (g_thread_create_key, new MacOSXDarwinThread(thread_name));
100 }
101}
102
Chris Lattner24943d22010-06-08 16:52:24 +0000103
104bool
Greg Clayton24b48ff2010-10-17 22:03:32 +0000105Host::ResolveExecutableInBundle (FileSpec &file)
Chris Lattner24943d22010-06-08 16:52:24 +0000106{
Greg Clayton8f3b21d2010-09-07 20:11:56 +0000107#if defined (__APPLE__)
Greg Clayton24b48ff2010-10-17 22:03:32 +0000108 if (file.GetFileType () == FileSpec::eFileTypeDirectory)
Chris Lattner24943d22010-06-08 16:52:24 +0000109 {
Greg Clayton24b48ff2010-10-17 22:03:32 +0000110 char path[PATH_MAX];
111 if (file.GetPath(path, sizeof(path)))
Chris Lattner24943d22010-06-08 16:52:24 +0000112 {
Greg Clayton24b48ff2010-10-17 22:03:32 +0000113 CFCBundle bundle (path);
114 CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL ());
115 if (url.get())
116 {
117 if (::CFURLGetFileSystemRepresentation (url.get(), YES, (UInt8*)path, sizeof(path)))
118 {
Greg Clayton537a7a82010-10-20 20:54:39 +0000119 file.SetFile(path, false);
Greg Clayton24b48ff2010-10-17 22:03:32 +0000120 return true;
121 }
122 }
Chris Lattner24943d22010-06-08 16:52:24 +0000123 }
124 }
Greg Clayton8f3b21d2010-09-07 20:11:56 +0000125#endif
126 return false;
Jim Ingham7508e732010-08-09 23:31:02 +0000127}
Jim Ingham74989e82010-08-30 19:44:40 +0000128
Greg Clayton4b407112010-09-30 21:49:03 +0000129lldb::pid_t
130Host::LaunchApplication (const FileSpec &app_file_spec)
131{
132 char app_path[PATH_MAX];
133 app_file_spec.GetPath(app_path, sizeof(app_path));
134
135 LSApplicationParameters app_params;
136 ::bzero (&app_params, sizeof (app_params));
137 app_params.flags = kLSLaunchDefaults |
138 kLSLaunchDontAddToRecents |
Greg Clayton24b48ff2010-10-17 22:03:32 +0000139 kLSLaunchNewInstance;
140
Greg Clayton4b407112010-09-30 21:49:03 +0000141
142 FSRef app_fsref;
143 CFCString app_cfstr (app_path, kCFStringEncodingUTF8);
144
145 OSStatus error = ::FSPathMakeRef ((const UInt8 *)app_path, &app_fsref, false);
146
147 // If we found the app, then store away the name so we don't have to re-look it up.
148 if (error != noErr)
149 return LLDB_INVALID_PROCESS_ID;
150
151 app_params.application = &app_fsref;
152
153 ProcessSerialNumber psn;
154
155 error = ::LSOpenApplication (&app_params, &psn);
156
157 if (error != noErr)
158 return LLDB_INVALID_PROCESS_ID;
159
160 ::pid_t pid = LLDB_INVALID_PROCESS_ID;
161 error = ::GetProcessPID(&psn, &pid);
162 return pid;
163}
164
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000165
166static void *
167AcceptPIDFromInferior (void *arg)
Greg Clayton24b48ff2010-10-17 22:03:32 +0000168{
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000169 const char *connect_url = (const char *)arg;
Greg Claytonea3259d2010-10-19 17:03:58 +0000170 ConnectionFileDescriptor file_conn;
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000171 Error error;
172 if (file_conn.Connect (connect_url, &error) == eConnectionStatusSuccess)
Greg Claytonea3259d2010-10-19 17:03:58 +0000173 {
174 char pid_str[256];
175 ::bzero (pid_str, sizeof(pid_str));
176 ConnectionStatus status;
177 const size_t pid_str_len = file_conn.Read (pid_str, sizeof(pid_str), status, NULL);
178 if (pid_str_len > 0)
179 {
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000180 int pid = atoi (pid_str);
181 return (void *)(intptr_t)pid;
Greg Claytonea3259d2010-10-19 17:03:58 +0000182 }
183 }
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000184 return NULL;
Greg Claytonea3259d2010-10-19 17:03:58 +0000185}
186
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000187static bool
188WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds)
189{
190 const int time_delta_usecs = 100000;
191 const int num_retries = timeout_in_seconds/time_delta_usecs;
192 for (int i=0; i<num_retries; i++)
193 {
194 struct proc_bsdinfo bsd_info;
195 int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO,
196 (uint64_t) 0,
197 &bsd_info,
198 PROC_PIDTBSDINFO_SIZE);
199
200 switch (error)
201 {
202 case EINVAL:
203 case ENOTSUP:
204 case ESRCH:
205 case EPERM:
206 return false;
207
208 default:
209 break;
210
211 case 0:
212 if (bsd_info.pbi_status == SSTOP)
213 return true;
214 }
215 ::usleep (time_delta_usecs);
216 }
217 return false;
218}
219
220static lldb::pid_t
221LaunchInNewTerminalWithCommandFile
Greg Claytonea3259d2010-10-19 17:03:58 +0000222(
223 const char **argv,
224 const char **envp,
225 const ArchSpec *arch_spec,
226 bool stop_at_entry,
227 bool disable_aslr
228)
229{
230 if (!argv || !argv[0])
231 return LLDB_INVALID_PROCESS_ID;
Greg Clayton24b48ff2010-10-17 22:03:32 +0000232
233 OSStatus error = 0;
234
Greg Clayton537a7a82010-10-20 20:54:39 +0000235 FileSpec program (argv[0], false);
Greg Clayton24b48ff2010-10-17 22:03:32 +0000236
237
Greg Clayton36f63a92010-10-19 03:25:40 +0000238 std::string unix_socket_name;
239
Greg Clayton24b48ff2010-10-17 22:03:32 +0000240 char temp_file_path[PATH_MAX];
241 const char *tmpdir = ::getenv ("TMPDIR");
242 if (tmpdir == NULL)
243 tmpdir = "/tmp/";
244 ::snprintf (temp_file_path, sizeof(temp_file_path), "%s%s-XXXXXX", tmpdir, program.GetFilename().AsCString());
245
246 if (::mktemp (temp_file_path) == NULL)
247 return LLDB_INVALID_PROCESS_ID;
248
Greg Clayton36f63a92010-10-19 03:25:40 +0000249 unix_socket_name.assign (temp_file_path);
250
Greg Clayton24b48ff2010-10-17 22:03:32 +0000251 ::strncat (temp_file_path, ".command", sizeof (temp_file_path));
252
253 StreamFile command_file (temp_file_path, "w");
254
255 FileSpec darwin_debug_file_spec;
256 if (!Host::GetLLDBPath (ePathTypeSupportExecutableDir, darwin_debug_file_spec))
257 return LLDB_INVALID_PROCESS_ID;
258 darwin_debug_file_spec.GetFilename().SetCString("darwin-debug");
259
260 if (!darwin_debug_file_spec.Exists())
261 return LLDB_INVALID_PROCESS_ID;
262
263 char launcher_path[PATH_MAX];
264 darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path));
265 command_file.Printf("\"%s\" ", launcher_path);
266
Greg Clayton36f63a92010-10-19 03:25:40 +0000267 command_file.Printf("--unix-socket=%s ", unix_socket_name.c_str());
268
Greg Clayton24b48ff2010-10-17 22:03:32 +0000269 if (arch_spec && arch_spec->IsValid())
270 {
271 command_file.Printf("--arch=%s ", arch_spec->AsCString());
272 }
273
274 if (disable_aslr)
275 {
276 command_file.PutCString("--disable-aslr ");
277 }
278
279 command_file.PutCString("-- ");
280
281 if (argv)
282 {
283 for (size_t i=0; argv[i] != NULL; ++i)
284 {
285 command_file.Printf("\"%s\" ", argv[i]);
286 }
287 }
Greg Claytonc1d37752010-10-18 01:45:30 +0000288 command_file.PutCString("\necho Process exited with status $?\n");
Greg Clayton24b48ff2010-10-17 22:03:32 +0000289 command_file.Close();
290 if (::chmod (temp_file_path, S_IRWXU | S_IRWXG) != 0)
291 return LLDB_INVALID_PROCESS_ID;
292
293 CFCMutableDictionary cf_env_dict;
294
295 const bool can_create = true;
296 if (envp)
297 {
298 for (size_t i=0; envp[i] != NULL; ++i)
299 {
300 const char *env_entry = envp[i];
301 const char *equal_pos = strchr(env_entry, '=');
302 if (equal_pos)
303 {
304 std::string env_key (env_entry, equal_pos);
305 std::string env_val (equal_pos + 1);
306 CFCString cf_env_key (env_key.c_str(), kCFStringEncodingUTF8);
307 CFCString cf_env_val (env_val.c_str(), kCFStringEncodingUTF8);
308 cf_env_dict.AddValue (cf_env_key.get(), cf_env_val.get(), can_create);
309 }
310 }
311 }
312
313 LSApplicationParameters app_params;
314 ::bzero (&app_params, sizeof (app_params));
315 app_params.flags = kLSLaunchDontAddToRecents | kLSLaunchAsync;
316 app_params.argv = NULL;
317 app_params.environment = (CFDictionaryRef)cf_env_dict.get();
318
319 CFCReleaser<CFURLRef> command_file_url (::CFURLCreateFromFileSystemRepresentation (NULL,
320 (const UInt8 *)temp_file_path,
Greg Clayton36f63a92010-10-19 03:25:40 +0000321 strlen(temp_file_path),
Greg Clayton24b48ff2010-10-17 22:03:32 +0000322 false));
323
324 CFCMutableArray urls;
325
326 // Terminal.app will open the ".command" file we have created
327 // and run our process inside it which will wait at the entry point
328 // for us to attach.
329 urls.AppendValue(command_file_url.get());
330
Greg Clayton36f63a92010-10-19 03:25:40 +0000331
332 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
333
334 Error lldb_error;
335 // Sleep and wait a bit for debugserver to start to listen...
Greg Clayton36f63a92010-10-19 03:25:40 +0000336 char connect_url[128];
337 ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name.c_str());
338
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000339 // Spawn a new thread to accept incoming connection on the connect_url
340 // so we can grab the pid from the inferior
341 lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name.c_str(),
342 AcceptPIDFromInferior,
343 connect_url,
344 &lldb_error);
345
Greg Clayton24b48ff2010-10-17 22:03:32 +0000346 ProcessSerialNumber psn;
347 error = LSOpenURLsWithRole(urls.get(), kLSRolesShell, NULL, &app_params, &psn, 1);
Greg Clayton36f63a92010-10-19 03:25:40 +0000348 if (error == noErr)
349 {
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000350 thread_result_t accept_thread_result = NULL;
351 if (Host::ThreadJoin (accept_thread, &accept_thread_result, &lldb_error))
Greg Clayton36f63a92010-10-19 03:25:40 +0000352 {
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000353 if (accept_thread_result)
Greg Clayton36f63a92010-10-19 03:25:40 +0000354 {
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000355 pid = (intptr_t)accept_thread_result;
356
357 // Wait for process to be stopped the the entry point by watching
358 // for the process status to be set to SSTOP which indicates it it
359 // SIGSTOP'ed at the entry point
360 WaitForProcessToSIGSTOP (pid, 5);
Greg Clayton36f63a92010-10-19 03:25:40 +0000361 }
362 }
363 }
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000364 else
365 {
366 Host::ThreadCancel (accept_thread, &lldb_error);
367 }
368
Greg Clayton24b48ff2010-10-17 22:03:32 +0000369 return pid;
370}
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000371
Greg Claytonbb0c91f2010-10-19 23:16:00 +0000372const char *applscript_in_new_tty =
373"tell application \"Terminal\"\n"
374" do script \"%s\"\n"
375"end tell\n";
376
377
378const char *applscript_in_existing_tty = "\
379set the_shell_script to \"%s\"\n\
380tell application \"Terminal\"\n\
381 repeat with the_window in (get windows)\n\
382 repeat with the_tab in tabs of the_window\n\
383 set the_tty to tty in the_tab\n\
384 if the_tty contains \"%s\" then\n\
385 if the_tab is not busy then\n\
386 set selected of the_tab to true\n\
387 set frontmost of the_window to true\n\
388 do script the_shell_script in the_tab\n\
389 return\n\
390 end if\n\
391 end if\n\
392 end repeat\n\
393 end repeat\n\
394 do script the_shell_script\n\
395end tell\n";
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000396
397lldb::pid_t
398LaunchInNewTerminalWithAppleScript
399(
Greg Claytonbb0c91f2010-10-19 23:16:00 +0000400 const char *tty_name,
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000401 const char **argv,
402 const char **envp,
403 const ArchSpec *arch_spec,
404 bool stop_at_entry,
405 bool disable_aslr
406)
407{
408 if (!argv || !argv[0])
409 return LLDB_INVALID_PROCESS_ID;
410
411 std::string unix_socket_name;
412
413 char temp_file_path[PATH_MAX] = "/tmp/XXXXXX";
414 if (::mktemp (temp_file_path) == NULL)
415 return LLDB_INVALID_PROCESS_ID;
416
417 unix_socket_name.assign (temp_file_path);
418
419 StreamString command;
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000420 FileSpec darwin_debug_file_spec;
421 if (!Host::GetLLDBPath (ePathTypeSupportExecutableDir, darwin_debug_file_spec))
422 return LLDB_INVALID_PROCESS_ID;
423 darwin_debug_file_spec.GetFilename().SetCString("darwin-debug");
424
425 if (!darwin_debug_file_spec.Exists())
426 return LLDB_INVALID_PROCESS_ID;
427
428 char launcher_path[PATH_MAX];
429 darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path));
Greg Claytonbb0c91f2010-10-19 23:16:00 +0000430
431 command.Printf("'%s' --unix-socket=%s", launcher_path, unix_socket_name.c_str());
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000432
433 if (arch_spec && arch_spec->IsValid())
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000434 command.Printf(" --arch=%s", arch_spec->AsCString());
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000435
436 if (disable_aslr)
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000437 command.PutCString(" --disable-aslr");
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000438
439 command.PutCString(" --");
440
441 if (argv)
442 {
443 for (size_t i=0; argv[i] != NULL; ++i)
444 {
445 command.Printf(" '%s'", argv[i]);
446 }
447 }
Greg Claytonbb0c91f2010-10-19 23:16:00 +0000448 command.PutCString (" ; echo Process exited with status $?");
449
450 StreamString applescript_source;
451
452 if (tty_name && tty_name[0])
453 {
454 applescript_source.Printf (applscript_in_existing_tty,
455 command.GetString().c_str(),
456 tty_name);
457 }
458 else
459 {
460 applescript_source.Printf (applscript_in_new_tty,
461 command.GetString().c_str());
462 }
463
464
465
466 const char *script_source = applescript_source.GetString().c_str();
467 //puts (script_source);
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000468 NSAppleScript* applescript = [[NSAppleScript alloc] initWithSource:[NSString stringWithCString:script_source encoding:NSUTF8StringEncoding]];
469
470 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
471
472 Error lldb_error;
473 // Sleep and wait a bit for debugserver to start to listen...
474 ConnectionFileDescriptor file_conn;
475 char connect_url[128];
476 ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name.c_str());
477
478 // Spawn a new thread to accept incoming connection on the connect_url
479 // so we can grab the pid from the inferior
480 lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name.c_str(),
481 AcceptPIDFromInferior,
482 connect_url,
483 &lldb_error);
484
485
486 [applescript executeAndReturnError:nil];
487
488 thread_result_t accept_thread_result = NULL;
489 if (Host::ThreadJoin (accept_thread, &accept_thread_result, &lldb_error))
490 {
491 if (accept_thread_result)
492 {
493 pid = (intptr_t)accept_thread_result;
494
495 // Wait for process to be stopped the the entry point by watching
496 // for the process status to be set to SSTOP which indicates it it
497 // SIGSTOP'ed at the entry point
498 WaitForProcessToSIGSTOP (pid, 5);
499 }
500 }
Greg Claytonbb0c91f2010-10-19 23:16:00 +0000501 ::unlink (unix_socket_name.c_str());
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000502 [applescript release];
503 return pid;
504}
505
506
507#define LLDB_HOST_USE_APPLESCRIPT
508
509lldb::pid_t
510Host::LaunchInNewTerminal
511(
Greg Claytonbb0c91f2010-10-19 23:16:00 +0000512 const char *tty_name,
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000513 const char **argv,
514 const char **envp,
515 const ArchSpec *arch_spec,
516 bool stop_at_entry,
517 bool disable_aslr
518)
519{
520#if defined (LLDB_HOST_USE_APPLESCRIPT)
Greg Claytonbb0c91f2010-10-19 23:16:00 +0000521 return LaunchInNewTerminalWithAppleScript (tty_name, argv, envp, arch_spec, stop_at_entry, disable_aslr);
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000522#else
523 return LaunchInNewTerminalWithCommandFile (argv, envp, arch_spec, stop_at_entry, disable_aslr);
Greg Claytonea3259d2010-10-19 17:03:58 +0000524#endif
Greg Clayton6bd1a9e2010-10-19 18:15:50 +0000525}
526
Greg Clayton24b48ff2010-10-17 22:03:32 +0000527
Jim Ingham74989e82010-08-30 19:44:40 +0000528bool
Greg Clayton4b407112010-09-30 21:49:03 +0000529Host::OpenFileInExternalEditor (const FileSpec &file_spec, uint32_t line_no)
Jim Ingham74989e82010-08-30 19:44:40 +0000530{
531 // We attach this to an 'odoc' event to specify a particular selection
532 typedef struct {
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000533 int16_t reserved0; // must be zero
534 int16_t fLineNumber;
535 int32_t fSelStart;
536 int32_t fSelEnd;
537 uint32_t reserved1; // must be zero
538 uint32_t reserved2; // must be zero
Jim Ingham74989e82010-08-30 19:44:40 +0000539 } BabelAESelInfo;
540
Jim Inghamb0fff352010-08-31 18:05:13 +0000541 Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST);
Jim Ingham74989e82010-08-30 19:44:40 +0000542 char file_path[PATH_MAX];
543 file_spec.GetPath(file_path, PATH_MAX);
544 CFCString file_cfstr (file_path, kCFStringEncodingUTF8);
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000545 CFCReleaser<CFURLRef> file_URL (::CFURLCreateWithFileSystemPath (NULL,
546 file_cfstr.get(),
547 kCFURLPOSIXPathStyle,
548 false));
Jim Inghamb0fff352010-08-31 18:05:13 +0000549
550 if (log)
551 log->Printf("Sending source file: \"%s\" and line: %d to external editor.\n", file_path, line_no);
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000552
Jim Ingham74989e82010-08-30 19:44:40 +0000553 OSStatus error;
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000554 BabelAESelInfo file_and_line_info =
555 {
556 0, // reserved0
557 line_no - 1, // fLineNumber (zero based line number)
558 1, // fSelStart
559 1024, // fSelEnd
560 0, // reserved1
561 0 // reserved2
562 };
Jim Ingham74989e82010-08-30 19:44:40 +0000563
564 AEKeyDesc file_and_line_desc;
565
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000566 error = ::AECreateDesc (typeUTF8Text,
567 &file_and_line_info,
568 sizeof (file_and_line_info),
569 &(file_and_line_desc.descContent));
Jim Ingham74989e82010-08-30 19:44:40 +0000570
Jim Ingham74989e82010-08-30 19:44:40 +0000571 if (error != noErr)
572 {
Jim Inghamb0fff352010-08-31 18:05:13 +0000573 if (log)
574 log->Printf("Error creating AEDesc: %d.\n", error);
Jim Ingham74989e82010-08-30 19:44:40 +0000575 return false;
576 }
577
578 file_and_line_desc.descKey = keyAEPosition;
579
Greg Claytoncb0989a2010-08-31 18:56:24 +0000580 static std::string g_app_name;
Greg Clayton452bf612010-08-31 18:35:14 +0000581 static FSRef g_app_fsref;
582
Jim Ingham74989e82010-08-30 19:44:40 +0000583 LSApplicationParameters app_params;
Greg Clayton452bf612010-08-31 18:35:14 +0000584 ::bzero (&app_params, sizeof (app_params));
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000585 app_params.flags = kLSLaunchDefaults |
586 kLSLaunchDontAddToRecents |
587 kLSLaunchDontSwitch;
Greg Clayton452bf612010-08-31 18:35:14 +0000588
Jim Ingham363180d2010-08-30 23:48:25 +0000589 char *external_editor = ::getenv ("LLDB_EXTERNAL_EDITOR");
590
Greg Clayton452bf612010-08-31 18:35:14 +0000591 if (external_editor)
Jim Ingham363180d2010-08-30 23:48:25 +0000592 {
Jim Inghamb0fff352010-08-31 18:05:13 +0000593 if (log)
594 log->Printf("Looking for external editor \"%s\".\n", external_editor);
595
Greg Claytoncb0989a2010-08-31 18:56:24 +0000596 if (g_app_name.empty() || strcmp (g_app_name.c_str(), external_editor) != 0)
Jim Ingham363180d2010-08-30 23:48:25 +0000597 {
Jim Ingham363180d2010-08-30 23:48:25 +0000598 CFCString editor_name (external_editor, kCFStringEncodingUTF8);
Greg Clayton452bf612010-08-31 18:35:14 +0000599 error = ::LSFindApplicationForInfo (kLSUnknownCreator,
600 NULL,
601 editor_name.get(),
602 &g_app_fsref,
603 NULL);
Jim Ingham363180d2010-08-30 23:48:25 +0000604
605 // 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 +0000606 if (error != noErr)
Jim Inghamb0fff352010-08-31 18:05:13 +0000607 {
608 if (log)
609 log->Printf("Could not find External Editor application, error: %d.\n", error);
Jim Ingham363180d2010-08-30 23:48:25 +0000610 return false;
Jim Inghamb0fff352010-08-31 18:05:13 +0000611 }
Jim Ingham363180d2010-08-30 23:48:25 +0000612
613 }
Greg Clayton452bf612010-08-31 18:35:14 +0000614 app_params.application = &g_app_fsref;
Jim Ingham363180d2010-08-30 23:48:25 +0000615 }
616
Jim Ingham74989e82010-08-30 19:44:40 +0000617 ProcessSerialNumber psn;
618 CFCReleaser<CFArrayRef> file_array(CFArrayCreate (NULL, (const void **) file_URL.ptr_address(false), 1, NULL));
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000619 error = ::LSOpenURLsWithRole (file_array.get(),
Jim Ingham363180d2010-08-30 23:48:25 +0000620 kLSRolesAll,
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000621 &file_and_line_desc,
622 &app_params,
623 &psn,
624 1);
625
Jim Ingham74989e82010-08-30 19:44:40 +0000626 AEDisposeDesc (&(file_and_line_desc.descContent));
627
628 if (error != noErr)
629 {
Jim Inghamb0fff352010-08-31 18:05:13 +0000630 if (log)
631 log->Printf("LSOpenURLsWithRole failed, error: %d.\n", error);
632
Jim Ingham74989e82010-08-30 19:44:40 +0000633 return false;
634 }
635
636 ProcessInfoRec which_process;
637 bzero(&which_process, sizeof(which_process));
638 unsigned char ap_name[PATH_MAX];
639 which_process.processName = ap_name;
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000640 error = ::GetProcessInformation (&psn, &which_process);
Jim Ingham74989e82010-08-30 19:44:40 +0000641
Jim Inghamb0fff352010-08-31 18:05:13 +0000642 bool using_xcode;
643 if (error != noErr)
644 {
645 if (log)
646 log->Printf("GetProcessInformation failed, error: %d.\n", error);
647 using_xcode = false;
648 }
649 else
650 using_xcode = strncmp((char *) ap_name+1, "Xcode", (int) ap_name[0]) == 0;
Jim Ingham74989e82010-08-30 19:44:40 +0000651
652 // Xcode doesn't obey the line number in the Open Apple Event. So I have to send
653 // it an AppleScript to focus on the right line.
654
655 if (using_xcode)
656 {
657 static ComponentInstance osa_component = NULL;
658 static const char *as_template = "tell application \"Xcode\"\n"
659 "set doc to the first document whose path is \"%s\"\n"
660 "set the selection to paragraph %d of doc\n"
661 "--- set the selected paragraph range to {%d, %d} of doc\n"
662 "end tell\n";
663 const int chars_for_int = 32;
664 static int as_template_len = strlen (as_template);
665
666
667 char *as_str;
668 AEDesc as_desc;
669
670 if (osa_component == NULL)
671 {
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000672 osa_component = ::OpenDefaultComponent (kOSAComponentType,
673 kAppleScriptSubtype);
Jim Ingham74989e82010-08-30 19:44:40 +0000674 }
675
676 if (osa_component == NULL)
677 {
Jim Inghamb0fff352010-08-31 18:05:13 +0000678 if (log)
679 log->Printf("Could not get default AppleScript component.\n");
Jim Ingham74989e82010-08-30 19:44:40 +0000680 return false;
681 }
682
683 uint32_t as_str_size = as_template_len + strlen (file_path) + 3 * chars_for_int + 1;
684 as_str = (char *) malloc (as_str_size);
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000685 ::snprintf (as_str,
686 as_str_size - 1,
687 as_template,
688 file_path,
689 line_no,
690 line_no,
691 line_no);
Jim Ingham74989e82010-08-30 19:44:40 +0000692
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000693 error = ::AECreateDesc (typeChar,
694 as_str,
695 strlen (as_str),
696 &as_desc);
697
698 ::free (as_str);
Jim Ingham74989e82010-08-30 19:44:40 +0000699
700 if (error != noErr)
Jim Inghamb0fff352010-08-31 18:05:13 +0000701 {
702 if (log)
703 log->Printf("Failed to create AEDesc for Xcode AppleEvent: %d.\n", error);
Jim Ingham74989e82010-08-30 19:44:40 +0000704 return false;
Jim Inghamb0fff352010-08-31 18:05:13 +0000705 }
Jim Ingham74989e82010-08-30 19:44:40 +0000706
707 OSAID ret_OSAID;
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000708 error = ::OSACompileExecute (osa_component,
709 &as_desc,
710 kOSANullScript,
711 kOSAModeNeverInteract,
712 &ret_OSAID);
713
714 ::OSADispose (osa_component, ret_OSAID);
Jim Ingham74989e82010-08-30 19:44:40 +0000715
Greg Claytonf4fa8a92010-08-30 22:00:34 +0000716 ::AEDisposeDesc (&as_desc);
Jim Ingham74989e82010-08-30 19:44:40 +0000717
718 if (error != noErr)
Jim Inghamb0fff352010-08-31 18:05:13 +0000719 {
720 if (log)
721 log->Printf("Sending AppleEvent to Xcode failed, error: %d.\n", error);
Jim Ingham74989e82010-08-30 19:44:40 +0000722 return false;
Jim Inghamb0fff352010-08-31 18:05:13 +0000723 }
Jim Ingham74989e82010-08-30 19:44:40 +0000724 }
725
726 return true;
727}