1 //===-- SBProcess.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/API/SBProcess.h"
11 
12 #include "lldb/lldb-defines.h"
13 #include "lldb/lldb-types.h"
14 
15 #include "lldb/Interpreter/Args.h"
16 #include "lldb/Core/DataBufferHeap.h"
17 #include "lldb/Core/DataExtractor.h"
18 #include "lldb/Core/Debugger.h"
19 #include "lldb/Core/Log.h"
20 #include "lldb/Core/State.h"
21 #include "lldb/Core/Stream.h"
22 #include "lldb/Core/StreamFile.h"
23 #include "lldb/Target/Process.h"
24 #include "lldb/Target/RegisterContext.h"
25 #include "lldb/Target/Target.h"
26 #include "lldb/Target/Thread.h"
27 
28 // Project includes
29 
30 #include "lldb/API/SBBroadcaster.h"
31 #include "lldb/API/SBDebugger.h"
32 #include "lldb/API/SBCommandReturnObject.h"
33 #include "lldb/API/SBEvent.h"
34 #include "lldb/API/SBThread.h"
35 #include "lldb/API/SBStream.h"
36 #include "lldb/API/SBStringList.h"
37 
38 using namespace lldb;
39 using namespace lldb_private;
40 
41 
42 
43 SBProcess::SBProcess () :
44     m_opaque_sp()
45 {
46 }
47 
48 
49 //----------------------------------------------------------------------
50 // SBProcess constructor
51 //----------------------------------------------------------------------
52 
53 SBProcess::SBProcess (const SBProcess& rhs) :
54     m_opaque_sp (rhs.m_opaque_sp)
55 {
56 }
57 
58 
59 SBProcess::SBProcess (const lldb::ProcessSP &process_sp) :
60     m_opaque_sp (process_sp)
61 {
62 }
63 
64 const SBProcess&
65 SBProcess::operator = (const SBProcess& rhs)
66 {
67     if (this != &rhs)
68         m_opaque_sp = rhs.m_opaque_sp;
69     return *this;
70 }
71 
72 //----------------------------------------------------------------------
73 // Destructor
74 //----------------------------------------------------------------------
75 SBProcess::~SBProcess()
76 {
77 }
78 
79 void
80 SBProcess::SetProcess (const ProcessSP &process_sp)
81 {
82     m_opaque_sp = process_sp;
83 }
84 
85 void
86 SBProcess::Clear ()
87 {
88     m_opaque_sp.reset();
89 }
90 
91 
92 bool
93 SBProcess::IsValid() const
94 {
95     return m_opaque_sp.get() != NULL;
96 }
97 
98 
99 uint32_t
100 SBProcess::GetNumThreads ()
101 {
102     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
103 
104     uint32_t num_threads = 0;
105     if (m_opaque_sp)
106     {
107         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
108         const bool can_update = true;
109         num_threads = m_opaque_sp->GetThreadList().GetSize(can_update);
110     }
111 
112     if (log)
113         log->Printf ("SBProcess(%p)::GetNumThreads () => %d", m_opaque_sp.get(), num_threads);
114 
115     return num_threads;
116 }
117 
118 SBThread
119 SBProcess::GetSelectedThread () const
120 {
121     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
122 
123     SBThread sb_thread;
124     if (m_opaque_sp)
125     {
126         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
127         sb_thread.SetThread (m_opaque_sp->GetThreadList().GetSelectedThread());
128     }
129 
130     if (log)
131     {
132         log->Printf ("SBProcess(%p)::GetSelectedThread () => SBThread(%p)", m_opaque_sp.get(), sb_thread.get());
133     }
134 
135     return sb_thread;
136 }
137 
138 SBTarget
139 SBProcess::GetTarget() const
140 {
141     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
142 
143     SBTarget sb_target;
144     if (m_opaque_sp)
145         sb_target = m_opaque_sp->GetTarget().GetSP();
146 
147     if (log)
148         log->Printf ("SBProcess(%p)::GetTarget () => SBTarget(%p)", m_opaque_sp.get(), sb_target.get());
149 
150     return sb_target;
151 }
152 
153 
154 size_t
155 SBProcess::PutSTDIN (const char *src, size_t src_len)
156 {
157     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
158 
159     size_t ret_val = 0;
160     if (m_opaque_sp)
161     {
162         Error error;
163         ret_val =  m_opaque_sp->PutSTDIN (src, src_len, error);
164     }
165 
166     if (log)
167         log->Printf ("SBProcess(%p)::PutSTDIN (src=\"%s\", src_len=%d) => %d",
168                      m_opaque_sp.get(),
169                      src,
170                      (uint32_t) src_len,
171                      ret_val);
172 
173     return ret_val;
174 }
175 
176 size_t
177 SBProcess::GetSTDOUT (char *dst, size_t dst_len) const
178 {
179     size_t bytes_read = 0;
180     if (m_opaque_sp)
181     {
182         Error error;
183         bytes_read = m_opaque_sp->GetSTDOUT (dst, dst_len, error);
184     }
185 
186     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
187     if (log)
188         log->Printf ("SBProcess(%p)::GetSTDOUT (dst=\"%.*s\", dst_len=%zu) => %zu",
189                      m_opaque_sp.get(), (int) bytes_read, dst, dst_len, bytes_read);
190 
191     return bytes_read;
192 }
193 
194 size_t
195 SBProcess::GetSTDERR (char *dst, size_t dst_len) const
196 {
197     size_t bytes_read = 0;
198     if (m_opaque_sp)
199     {
200         Error error;
201         bytes_read = m_opaque_sp->GetSTDERR (dst, dst_len, error);
202     }
203 
204     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
205     if (log)
206         log->Printf ("SBProcess(%p)::GetSTDERR (dst=\"%.*s\", dst_len=%zu) => %zu",
207                      m_opaque_sp.get(), (int) bytes_read, dst, dst_len, bytes_read);
208 
209     return bytes_read;
210 }
211 
212 void
213 SBProcess::ReportEventState (const SBEvent &event, FILE *out) const
214 {
215     if (out == NULL)
216         return;
217 
218     if (m_opaque_sp)
219     {
220         const StateType event_state = SBProcess::GetStateFromEvent (event);
221         char message[1024];
222         int message_len = ::snprintf (message,
223                                       sizeof (message),
224                                       "Process %d %s\n",
225                                       m_opaque_sp->GetID(),
226                                       SBDebugger::StateAsCString (event_state));
227 
228         if (message_len > 0)
229             ::fwrite (message, 1, message_len, out);
230     }
231 }
232 
233 void
234 SBProcess::AppendEventStateReport (const SBEvent &event, SBCommandReturnObject &result)
235 {
236     if (m_opaque_sp)
237     {
238         const StateType event_state = SBProcess::GetStateFromEvent (event);
239         char message[1024];
240         ::snprintf (message,
241                     sizeof (message),
242                     "Process %d %s\n",
243                     m_opaque_sp->GetID(),
244                     SBDebugger::StateAsCString (event_state));
245 
246         result.AppendMessage (message);
247     }
248 }
249 
250 bool
251 SBProcess::SetSelectedThread (const SBThread &thread)
252 {
253     if (m_opaque_sp)
254     {
255         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
256         return m_opaque_sp->GetThreadList().SetSelectedThreadByID (thread.GetThreadID());
257     }
258     return false;
259 }
260 
261 bool
262 SBProcess::SetSelectedThreadByID (uint32_t tid)
263 {
264     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
265 
266     bool ret_val = false;
267     if (m_opaque_sp)
268     {
269         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
270         ret_val = m_opaque_sp->GetThreadList().SetSelectedThreadByID (tid);
271     }
272 
273     if (log)
274         log->Printf ("SBProcess(%p)::SetSelectedThreadByID (tid=0x%4.4x) => %s",
275                      m_opaque_sp.get(), tid, (ret_val ? "true" : "false"));
276 
277     return ret_val;
278 }
279 
280 SBThread
281 SBProcess::GetThreadAtIndex (size_t index)
282 {
283     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
284 
285     SBThread thread;
286     if (m_opaque_sp)
287     {
288         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
289         thread.SetThread (m_opaque_sp->GetThreadList().GetThreadAtIndex(index));
290     }
291 
292     if (log)
293     {
294         log->Printf ("SBProcess(%p)::GetThreadAtIndex (index=%d) => SBThread(%p)",
295                      m_opaque_sp.get(), (uint32_t) index, thread.get());
296     }
297 
298     return thread;
299 }
300 
301 StateType
302 SBProcess::GetState ()
303 {
304 
305     StateType ret_val = eStateInvalid;
306     if (m_opaque_sp)
307     {
308         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
309         ret_val = m_opaque_sp->GetState();
310     }
311 
312     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
313     if (log)
314         log->Printf ("SBProcess(%p)::GetState () => %s",
315                      m_opaque_sp.get(),
316                      lldb_private::StateAsCString (ret_val));
317 
318     return ret_val;
319 }
320 
321 
322 int
323 SBProcess::GetExitStatus ()
324 {
325     int exit_status = 0;
326     if (m_opaque_sp)
327     {
328         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
329         exit_status = m_opaque_sp->GetExitStatus ();
330     }
331     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
332     if (log)
333         log->Printf ("SBProcess(%p)::GetExitStatus () => %i (0x%8.8x)",
334                      m_opaque_sp.get(), exit_status, exit_status);
335 
336     return exit_status;
337 }
338 
339 const char *
340 SBProcess::GetExitDescription ()
341 {
342     const char *exit_desc = NULL;
343     if (m_opaque_sp)
344     {
345         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
346         exit_desc = m_opaque_sp->GetExitDescription ();
347     }
348     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
349     if (log)
350         log->Printf ("SBProcess(%p)::GetExitDescription () => %s",
351                      m_opaque_sp.get(), exit_desc);
352     return exit_desc;
353 }
354 
355 lldb::pid_t
356 SBProcess::GetProcessID ()
357 {
358     lldb::pid_t ret_val = LLDB_INVALID_PROCESS_ID;
359     if (m_opaque_sp)
360         ret_val = m_opaque_sp->GetID();
361 
362     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
363     if (log)
364         log->Printf ("SBProcess(%p)::GetProcessID () => %d", m_opaque_sp.get(), ret_val);
365 
366     return ret_val;
367 }
368 
369 uint32_t
370 SBProcess::GetAddressByteSize () const
371 {
372     uint32_t size = 0;
373     if (m_opaque_sp)
374         size =  m_opaque_sp->GetAddressByteSize();
375 
376     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
377     if (log)
378         log->Printf ("SBProcess(%p)::GetAddressByteSize () => %d", m_opaque_sp.get(), size);
379 
380     return size;
381 }
382 
383 SBError
384 SBProcess::Continue ()
385 {
386     Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
387 
388     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
389     if (log)
390         log->Printf ("SBProcess(%p)::Continue ()...", m_opaque_sp.get());
391 
392     SBError sb_error;
393     if (IsValid())
394     {
395         Error error (m_opaque_sp->Resume());
396         if (error.Success())
397         {
398             if (m_opaque_sp->GetTarget().GetDebugger().GetAsyncExecution () == false)
399             {
400                 if (log)
401                     log->Printf ("SBProcess(%p)::Continue () waiting for process to stop...", m_opaque_sp.get());
402                 m_opaque_sp->WaitForProcessToStop (NULL);
403             }
404         }
405         sb_error.SetError(error);
406     }
407     else
408         sb_error.SetErrorString ("SBProcess is invalid");
409 
410     if (log)
411     {
412         SBStream sstr;
413         sb_error.GetDescription (sstr);
414         log->Printf ("SBProcess(%p)::Continue () => SBError (%p): %s", m_opaque_sp.get(), sb_error.get(), sstr.GetData());
415     }
416 
417     return sb_error;
418 }
419 
420 
421 SBError
422 SBProcess::Destroy ()
423 {
424     SBError sb_error;
425     if (m_opaque_sp)
426     {
427         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
428         sb_error.SetError(m_opaque_sp->Destroy());
429     }
430     else
431         sb_error.SetErrorString ("SBProcess is invalid");
432 
433     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
434     if (log)
435     {
436         SBStream sstr;
437         sb_error.GetDescription (sstr);
438         log->Printf ("SBProcess(%p)::Destroy () => SBError (%p): %s",
439                      m_opaque_sp.get(),
440                      sb_error.get(),
441                      sstr.GetData());
442     }
443 
444     return sb_error;
445 }
446 
447 
448 SBError
449 SBProcess::Stop ()
450 {
451     SBError sb_error;
452     if (IsValid())
453     {
454         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
455         sb_error.SetError (m_opaque_sp->Halt());
456     }
457     else
458         sb_error.SetErrorString ("SBProcess is invalid");
459 
460     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
461     if (log)
462     {
463         SBStream sstr;
464         sb_error.GetDescription (sstr);
465         log->Printf ("SBProcess(%p)::Stop () => SBError (%p): %s",
466                      m_opaque_sp.get(),
467                      sb_error.get(),
468                      sstr.GetData());
469     }
470 
471     return sb_error;
472 }
473 
474 SBError
475 SBProcess::Kill ()
476 {
477     SBError sb_error;
478     if (m_opaque_sp)
479     {
480         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
481         sb_error.SetError (m_opaque_sp->Destroy());
482     }
483     else
484         sb_error.SetErrorString ("SBProcess is invalid");
485 
486     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
487     if (log)
488     {
489         SBStream sstr;
490         sb_error.GetDescription (sstr);
491         log->Printf ("SBProcess(%p)::Kill () => SBError (%p): %s",
492                      m_opaque_sp.get(),
493                      sb_error.get(),
494                      sstr.GetData());
495     }
496 
497     return sb_error;
498 }
499 
500 SBError
501 SBProcess::Detach ()
502 {
503     SBError sb_error;
504     if (m_opaque_sp)
505     {
506         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
507         sb_error.SetError (m_opaque_sp->Detach());
508     }
509     else
510         sb_error.SetErrorString ("SBProcess is invalid");
511 
512     return sb_error;
513 }
514 
515 SBError
516 SBProcess::Signal (int signo)
517 {
518     SBError sb_error;
519     if (m_opaque_sp)
520     {
521         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
522         sb_error.SetError (m_opaque_sp->Signal (signo));
523     }
524     else
525         sb_error.SetErrorString ("SBProcess is invalid");
526     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
527     if (log)
528     {
529         SBStream sstr;
530         sb_error.GetDescription (sstr);
531         log->Printf ("SBProcess(%p)::Signal (signo=%i) => SBError (%p): %s",
532                      m_opaque_sp.get(),
533                      signo,
534                      sb_error.get(),
535                      sstr.GetData());
536     }
537     return sb_error;
538 }
539 
540 SBThread
541 SBProcess::GetThreadByID (tid_t tid)
542 {
543     SBThread sb_thread;
544     if (m_opaque_sp)
545     {
546         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
547         sb_thread.SetThread (m_opaque_sp->GetThreadList().FindThreadByID ((tid_t) tid));
548     }
549 
550     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
551     if (log)
552     {
553         log->Printf ("SBProcess(%p)::GetThreadByID (tid=0x%4.4x) => SBThread (%p)",
554                      m_opaque_sp.get(),
555                      tid,
556                      sb_thread.get());
557     }
558 
559     return sb_thread;
560 }
561 
562 StateType
563 SBProcess::GetStateFromEvent (const SBEvent &event)
564 {
565     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
566 
567     StateType ret_val = Process::ProcessEventData::GetStateFromEvent (event.get());
568 
569     if (log)
570         log->Printf ("SBProcess::GetStateFromEvent (event.sp=%p) => %s", event.get(),
571                      lldb_private::StateAsCString (ret_val));
572 
573     return ret_val;
574 }
575 
576 bool
577 SBProcess::GetRestartedFromEvent (const SBEvent &event)
578 {
579     return Process::ProcessEventData::GetRestartedFromEvent (event.get());
580 }
581 
582 SBProcess
583 SBProcess::GetProcessFromEvent (const SBEvent &event)
584 {
585     SBProcess process(Process::ProcessEventData::GetProcessFromEvent (event.get()));
586     return process;
587 }
588 
589 
590 SBBroadcaster
591 SBProcess::GetBroadcaster () const
592 {
593     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
594 
595     SBBroadcaster broadcaster(m_opaque_sp.get(), false);
596 
597     if (log)
598         log->Printf ("SBProcess(%p)::GetBroadcaster () => SBBroadcaster (%p)",  m_opaque_sp.get(),
599                      broadcaster.get());
600 
601     return broadcaster;
602 }
603 
604 lldb_private::Process *
605 SBProcess::operator->() const
606 {
607     return m_opaque_sp.get();
608 }
609 
610 size_t
611 SBProcess::ReadMemory (addr_t addr, void *dst, size_t dst_len, SBError &sb_error)
612 {
613     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
614 
615     size_t bytes_read = 0;
616 
617     if (log)
618     {
619         log->Printf ("SBProcess(%p)::ReadMemory (addr=0x%llx, dst=%p, dst_len=%zu, SBError (%p))...",
620                      m_opaque_sp.get(),
621                      addr,
622                      dst,
623                      (uint32_t) dst_len,
624                      sb_error.get());
625     }
626 
627     if (m_opaque_sp)
628     {
629         Error error;
630         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
631         bytes_read = m_opaque_sp->ReadMemory (addr, dst, dst_len, error);
632         sb_error.SetError (error);
633     }
634     else
635     {
636         sb_error.SetErrorString ("SBProcess is invalid");
637     }
638 
639     if (log)
640     {
641         SBStream sstr;
642         sb_error.GetDescription (sstr);
643         log->Printf ("SBProcess(%p)::ReadMemory (addr=0x%llx, dst=%p, dst_len=%zu, SBError (%p): %s) => %d",
644                      m_opaque_sp.get(),
645                      addr,
646                      dst,
647                      (uint32_t) dst_len,
648                      sb_error.get(),
649                      sstr.GetData(),
650                      (uint32_t) bytes_read);
651     }
652 
653     return bytes_read;
654 }
655 
656 size_t
657 SBProcess::WriteMemory (addr_t addr, const void *src, size_t src_len, SBError &sb_error)
658 {
659     size_t bytes_written = 0;
660 
661     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
662     if (log)
663     {
664         log->Printf ("SBProcess(%p)::WriteMemory (addr=0x%llx, src=%p, dst_len=%zu, SBError (%p))...",
665                      m_opaque_sp.get(),
666                      addr,
667                      src,
668                      (uint32_t) src_len,
669                      sb_error.get());
670     }
671 
672     if (m_opaque_sp)
673     {
674         Error error;
675         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
676         bytes_written = m_opaque_sp->WriteMemory (addr, src, src_len, error);
677         sb_error.SetError (error);
678     }
679 
680     if (log)
681     {
682         SBStream sstr;
683         sb_error.GetDescription (sstr);
684         log->Printf ("SBProcess(%p)::WriteMemory (addr=0x%llx, src=%p, dst_len=%zu, SBError (%p): %s) => %d",
685                      m_opaque_sp.get(),
686                      addr,
687                      src,
688                      (uint32_t) src_len,
689                      sb_error.get(),
690                      sstr.GetData(),
691                      (uint32_t) bytes_written);
692     }
693 
694     return bytes_written;
695 }
696 
697 // Mimic shared pointer...
698 lldb_private::Process *
699 SBProcess::get() const
700 {
701     return m_opaque_sp.get();
702 }
703 
704 bool
705 SBProcess::GetDescription (SBStream &description)
706 {
707     if (m_opaque_sp)
708     {
709         char path[PATH_MAX];
710         GetTarget().GetExecutable().GetPath (path, sizeof(path));
711         Module *exe_module = m_opaque_sp->GetTarget().GetExecutableModule ().get();
712         const char *exe_name = NULL;
713         if (exe_module)
714             exe_name = exe_module->GetFileSpec().GetFilename().AsCString();
715 
716         description.Printf ("SBProcess: pid = %d, state = %s, threads = %d%s%s",
717                             m_opaque_sp->GetID(),
718                             lldb_private::StateAsCString (GetState()),
719                             GetNumThreads(),
720                             exe_name ? ", executable = " : "",
721                             exe_name ? exe_name : "");
722     }
723     else
724         description.Printf ("No value");
725 
726     return true;
727 }
728 
729 uint32_t
730 SBProcess::LoadImage (lldb::SBFileSpec &sb_image_spec, lldb::SBError &sb_error)
731 {
732     if (m_opaque_sp)
733     {
734         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
735         return m_opaque_sp->LoadImage (*sb_image_spec, sb_error.ref());
736     }
737     return LLDB_INVALID_IMAGE_TOKEN;
738 }
739 
740 lldb::SBError
741 SBProcess::UnloadImage (uint32_t image_token)
742 {
743     lldb::SBError sb_error;
744     if (m_opaque_sp)
745     {
746         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
747         sb_error.SetError (m_opaque_sp->UnloadImage (image_token));
748     }
749     else
750         sb_error.SetErrorString("invalid process");
751     return sb_error;
752 }
753 
754 
755