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     Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
425 
426     SBError sb_error;
427     if (m_opaque_sp)
428         sb_error.SetError(m_opaque_sp->Destroy());
429     else
430         sb_error.SetErrorString ("SBProcess is invalid");
431 
432     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
433     if (log)
434     {
435         SBStream sstr;
436         sb_error.GetDescription (sstr);
437         log->Printf ("SBProcess(%p)::Destroy () => SBError (%p): %s", m_opaque_sp.get(), sb_error.get(), sstr.GetData());
438     }
439 
440     return sb_error;
441 }
442 
443 
444 SBError
445 SBProcess::Stop ()
446 {
447     SBError sb_error;
448     if (IsValid())
449     {
450         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
451         sb_error.SetError (m_opaque_sp->Halt());
452     }
453     else
454         sb_error.SetErrorString ("SBProcess is invalid");
455 
456     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
457     if (log)
458     {
459         SBStream sstr;
460         sb_error.GetDescription (sstr);
461         log->Printf ("SBProcess(%p)::Stop () => SBError (%p): %s",
462                      m_opaque_sp.get(),
463                      sb_error.get(),
464                      sstr.GetData());
465     }
466 
467     return sb_error;
468 }
469 
470 SBError
471 SBProcess::Kill ()
472 {
473     SBError sb_error;
474     if (m_opaque_sp)
475     {
476         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
477         sb_error.SetError (m_opaque_sp->Destroy());
478     }
479     else
480         sb_error.SetErrorString ("SBProcess is invalid");
481 
482     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
483     if (log)
484     {
485         SBStream sstr;
486         sb_error.GetDescription (sstr);
487         log->Printf ("SBProcess(%p)::Kill () => SBError (%p): %s",
488                      m_opaque_sp.get(),
489                      sb_error.get(),
490                      sstr.GetData());
491     }
492 
493     return sb_error;
494 }
495 
496 SBError
497 SBProcess::Detach ()
498 {
499     SBError sb_error;
500     if (m_opaque_sp)
501     {
502         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
503         sb_error.SetError (m_opaque_sp->Detach());
504     }
505     else
506         sb_error.SetErrorString ("SBProcess is invalid");
507 
508     return sb_error;
509 }
510 
511 SBError
512 SBProcess::Signal (int signo)
513 {
514     SBError sb_error;
515     if (m_opaque_sp)
516     {
517         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
518         sb_error.SetError (m_opaque_sp->Signal (signo));
519     }
520     else
521         sb_error.SetErrorString ("SBProcess is invalid");
522     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
523     if (log)
524     {
525         SBStream sstr;
526         sb_error.GetDescription (sstr);
527         log->Printf ("SBProcess(%p)::Signal (signo=%i) => SBError (%p): %s",
528                      m_opaque_sp.get(),
529                      signo,
530                      sb_error.get(),
531                      sstr.GetData());
532     }
533     return sb_error;
534 }
535 
536 SBThread
537 SBProcess::GetThreadByID (tid_t tid)
538 {
539     SBThread sb_thread;
540     if (m_opaque_sp)
541     {
542         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
543         sb_thread.SetThread (m_opaque_sp->GetThreadList().FindThreadByID ((tid_t) tid));
544     }
545 
546     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
547     if (log)
548     {
549         log->Printf ("SBProcess(%p)::GetThreadByID (tid=0x%4.4x) => SBThread (%p)",
550                      m_opaque_sp.get(),
551                      tid,
552                      sb_thread.get());
553     }
554 
555     return sb_thread;
556 }
557 
558 StateType
559 SBProcess::GetStateFromEvent (const SBEvent &event)
560 {
561     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
562 
563     StateType ret_val = Process::ProcessEventData::GetStateFromEvent (event.get());
564 
565     if (log)
566         log->Printf ("SBProcess::GetStateFromEvent (event.sp=%p) => %s", event.get(),
567                      lldb_private::StateAsCString (ret_val));
568 
569     return ret_val;
570 }
571 
572 bool
573 SBProcess::GetRestartedFromEvent (const SBEvent &event)
574 {
575     return Process::ProcessEventData::GetRestartedFromEvent (event.get());
576 }
577 
578 SBProcess
579 SBProcess::GetProcessFromEvent (const SBEvent &event)
580 {
581     SBProcess process(Process::ProcessEventData::GetProcessFromEvent (event.get()));
582     return process;
583 }
584 
585 
586 SBBroadcaster
587 SBProcess::GetBroadcaster () const
588 {
589     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
590 
591     SBBroadcaster broadcaster(m_opaque_sp.get(), false);
592 
593     if (log)
594         log->Printf ("SBProcess(%p)::GetBroadcaster () => SBBroadcaster (%p)",  m_opaque_sp.get(),
595                      broadcaster.get());
596 
597     return broadcaster;
598 }
599 
600 lldb_private::Process *
601 SBProcess::operator->() const
602 {
603     return m_opaque_sp.get();
604 }
605 
606 size_t
607 SBProcess::ReadMemory (addr_t addr, void *dst, size_t dst_len, SBError &sb_error)
608 {
609     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
610 
611     size_t bytes_read = 0;
612 
613     if (log)
614     {
615         log->Printf ("SBProcess(%p)::ReadMemory (addr=0x%llx, dst=%p, dst_len=%zu, SBError (%p))...",
616                      m_opaque_sp.get(),
617                      addr,
618                      dst,
619                      (uint32_t) dst_len,
620                      sb_error.get());
621     }
622 
623     if (m_opaque_sp)
624     {
625         Error error;
626         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
627         bytes_read = m_opaque_sp->ReadMemory (addr, dst, dst_len, error);
628         sb_error.SetError (error);
629     }
630     else
631     {
632         sb_error.SetErrorString ("SBProcess is invalid");
633     }
634 
635     if (log)
636     {
637         SBStream sstr;
638         sb_error.GetDescription (sstr);
639         log->Printf ("SBProcess(%p)::ReadMemory (addr=0x%llx, dst=%p, dst_len=%zu, SBError (%p): %s) => %d",
640                      m_opaque_sp.get(),
641                      addr,
642                      dst,
643                      (uint32_t) dst_len,
644                      sb_error.get(),
645                      sstr.GetData(),
646                      (uint32_t) bytes_read);
647     }
648 
649     return bytes_read;
650 }
651 
652 size_t
653 SBProcess::WriteMemory (addr_t addr, const void *src, size_t src_len, SBError &sb_error)
654 {
655     size_t bytes_written = 0;
656 
657     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
658     if (log)
659     {
660         log->Printf ("SBProcess(%p)::WriteMemory (addr=0x%llx, src=%p, dst_len=%zu, SBError (%p))...",
661                      m_opaque_sp.get(),
662                      addr,
663                      src,
664                      (uint32_t) src_len,
665                      sb_error.get());
666     }
667 
668     if (m_opaque_sp)
669     {
670         Error error;
671         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
672         bytes_written = m_opaque_sp->WriteMemory (addr, src, src_len, error);
673         sb_error.SetError (error);
674     }
675 
676     if (log)
677     {
678         SBStream sstr;
679         sb_error.GetDescription (sstr);
680         log->Printf ("SBProcess(%p)::WriteMemory (addr=0x%llx, src=%p, dst_len=%zu, SBError (%p): %s) => %d",
681                      m_opaque_sp.get(),
682                      addr,
683                      src,
684                      (uint32_t) src_len,
685                      sb_error.get(),
686                      sstr.GetData(),
687                      (uint32_t) bytes_written);
688     }
689 
690     return bytes_written;
691 }
692 
693 // Mimic shared pointer...
694 lldb_private::Process *
695 SBProcess::get() const
696 {
697     return m_opaque_sp.get();
698 }
699 
700 bool
701 SBProcess::GetDescription (SBStream &description)
702 {
703     if (m_opaque_sp)
704     {
705         char path[PATH_MAX];
706         GetTarget().GetExecutable().GetPath (path, sizeof(path));
707         Module *exe_module = m_opaque_sp->GetTarget().GetExecutableModule ().get();
708         const char *exe_name = NULL;
709         if (exe_module)
710             exe_name = exe_module->GetFileSpec().GetFilename().AsCString();
711 
712         description.Printf ("SBProcess: pid = %d, state = %s, threads = %d%s%s",
713                             m_opaque_sp->GetID(),
714                             lldb_private::StateAsCString (GetState()),
715                             GetNumThreads(),
716                             exe_name ? ", executable = " : "",
717                             exe_name ? exe_name : "");
718     }
719     else
720         description.Printf ("No value");
721 
722     return true;
723 }
724 
725 uint32_t
726 SBProcess::LoadImage (lldb::SBFileSpec &sb_image_spec, lldb::SBError &sb_error)
727 {
728     if (m_opaque_sp)
729     {
730         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
731         return m_opaque_sp->LoadImage (*sb_image_spec, sb_error.ref());
732     }
733     return LLDB_INVALID_IMAGE_TOKEN;
734 }
735 
736 lldb::SBError
737 SBProcess::UnloadImage (uint32_t image_token)
738 {
739     lldb::SBError sb_error;
740     if (m_opaque_sp)
741     {
742         Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
743         sb_error.SetError (m_opaque_sp->UnloadImage (image_token));
744     }
745     else
746         sb_error.SetErrorString("invalid process");
747     return sb_error;
748 }
749 
750 
751