Index: projects/clang380-import/contrib/llvm/tools/lldb/include/lldb/Core/StringList.h =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/include/lldb/Core/StringList.h (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/include/lldb/Core/StringList.h (revision 293283) @@ -1,156 +1,180 @@ //===-- StringList.h --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_StringList_h_ #define liblldb_StringList_h_ // C Includes #include // C++ Includes #include // Other libraries and framework includes #include "llvm/ADT/StringRef.h" // Project includes #include "lldb/lldb-forward.h" #include "lldb/Core/STLUtils.h" namespace lldb_private { class StringList { public: StringList (); StringList (const char *str); StringList (const char **strv, int strc); virtual ~StringList (); void AppendString (const std::string &s); void AppendString (std::string &&s); void AppendString (const char *str); void AppendString (const char *str, size_t str_len); void AppendString(llvm::StringRef str); void AppendList (const char ** strv, int strc); void AppendList (StringList strings); bool ReadFileLines (FileSpec &input_file); size_t GetSize () const; void SetSize (size_t n) { m_strings.resize(n); } size_t GetMaxStringLength () const; std::string & operator [](size_t idx) { // No bounds checking, verify "idx" is good prior to calling this function return m_strings[idx]; } const std::string & operator [](size_t idx) const { // No bounds checking, verify "idx" is good prior to calling this function return m_strings[idx]; } void PopBack () { m_strings.pop_back(); } const char * GetStringAtIndex (size_t idx) const; void Join (const char *separator, Stream &strm); void Clear (); void LongestCommonPrefix (std::string &common_prefix); void InsertStringAtIndex (size_t idx, const std::string &str); void InsertStringAtIndex (size_t idx, std::string &&str); void InsertStringAtIndex (size_t id, const char *str); void DeleteStringAtIndex (size_t id); void RemoveBlankLines (); size_t SplitIntoLines (const std::string &lines); size_t SplitIntoLines (const char *lines, size_t len); std::string CopyList(const char* item_preamble = nullptr, const char* items_sep = "\n") const; StringList& operator << (const char* str); StringList& + operator << (const std::string &s); + + StringList& operator << (StringList strings); + // Copy assignment for a vector of strings + StringList& + operator = (const std::vector &rhs); + // This string list contains a list of valid auto completion // strings, and the "s" is passed in. "matches" is filled in // with zero or more string values that start with "s", and // the first string to exactly match one of the string // values in this collection, will have "exact_matches_idx" // filled in to match the index, or "exact_matches_idx" will // have SIZE_MAX size_t AutoComplete (const char *s, StringList &matches, size_t &exact_matches_idx) const; + // Dump the StringList to the given lldb_private::Log, `log`, one item per line. + // If given, `name` will be used to identify the start and end of the list in the output. + virtual void LogDump(Log *log, const char *name = nullptr); + + // Static helper to convert an iterable of strings to a StringList, and then + // dump it with the semantics of the `LogDump` method. + template static void LogDump(Log *log, T s_iterable, const char *name = nullptr) + { + if (!log) + return; + // Make a copy of the iterable as a StringList + StringList l{}; + for (const auto &s : s_iterable) + l << s; + + l.LogDump(log, name); + } private: STLStringArray m_strings; }; } // namespace lldb_private #endif // liblldb_StringList_h_ Index: projects/clang380-import/contrib/llvm/tools/lldb/source/API/SBProcess.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/API/SBProcess.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/API/SBProcess.cpp (revision 293283) @@ -1,1465 +1,1472 @@ //===-- SBProcess.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/API/SBProcess.h" // C Includes #include #include "lldb/lldb-defines.h" #include "lldb/lldb-types.h" #include "lldb/Interpreter/Args.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/Stream.h" #include "lldb/Core/StreamFile.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" // Project includes #include "lldb/API/SBBroadcaster.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBThreadCollection.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBUnixSignals.h" using namespace lldb; using namespace lldb_private; SBProcess::SBProcess () : m_opaque_wp() { } //---------------------------------------------------------------------- // SBProcess constructor //---------------------------------------------------------------------- SBProcess::SBProcess (const SBProcess& rhs) : m_opaque_wp (rhs.m_opaque_wp) { } SBProcess::SBProcess (const lldb::ProcessSP &process_sp) : m_opaque_wp (process_sp) { } const SBProcess& SBProcess::operator = (const SBProcess& rhs) { if (this != &rhs) m_opaque_wp = rhs.m_opaque_wp; return *this; } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- SBProcess::~SBProcess() { } const char * SBProcess::GetBroadcasterClassName () { return Process::GetStaticBroadcasterClass().AsCString(); } const char * SBProcess::GetPluginName () { ProcessSP process_sp(GetSP()); if (process_sp) { return process_sp->GetPluginName().GetCString(); } return ""; } const char * SBProcess::GetShortPluginName () { ProcessSP process_sp(GetSP()); if (process_sp) { return process_sp->GetPluginName().GetCString(); } return ""; } lldb::ProcessSP SBProcess::GetSP() const { return m_opaque_wp.lock(); } void SBProcess::SetSP (const ProcessSP &process_sp) { m_opaque_wp = process_sp; } void SBProcess::Clear () { m_opaque_wp.reset(); } bool SBProcess::IsValid() const { ProcessSP process_sp(m_opaque_wp.lock()); return ((bool) process_sp && process_sp->IsValid()); } bool SBProcess::RemoteLaunch (char const **argv, char const **envp, const char *stdin_path, const char *stdout_path, const char *stderr_path, const char *working_directory, uint32_t launch_flags, bool stop_at_entry, lldb::SBError& error) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::RemoteLaunch (argv=%p, envp=%p, stdin=%s, stdout=%s, stderr=%s, working-dir=%s, launch_flags=0x%x, stop_at_entry=%i, &error (%p))...", static_cast(m_opaque_wp.lock().get()), static_cast(argv), static_cast(envp), stdin_path ? stdin_path : "NULL", stdout_path ? stdout_path : "NULL", stderr_path ? stderr_path : "NULL", working_directory ? working_directory : "NULL", launch_flags, stop_at_entry, static_cast(error.get())); ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetState() == eStateConnected) { if (stop_at_entry) launch_flags |= eLaunchFlagStopAtEntry; ProcessLaunchInfo launch_info(FileSpec{stdin_path, false}, FileSpec{stdout_path, false}, FileSpec{stderr_path, false}, FileSpec{working_directory, false}, launch_flags); Module *exe_module = process_sp->GetTarget().GetExecutableModulePointer(); if (exe_module) launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); if (argv) launch_info.GetArguments().AppendArguments (argv); if (envp) launch_info.GetEnvironmentEntries ().SetArguments (envp); error.SetError (process_sp->Launch (launch_info)); } else { error.SetErrorString ("must be in eStateConnected to call RemoteLaunch"); } } else { error.SetErrorString ("unable to attach pid"); } if (log) { SBStream sstr; error.GetDescription (sstr); log->Printf ("SBProcess(%p)::RemoteLaunch (...) => SBError (%p): %s", static_cast(process_sp.get()), static_cast(error.get()), sstr.GetData()); } return error.Success(); } bool SBProcess::RemoteAttachToProcessWithID (lldb::pid_t pid, lldb::SBError& error) { ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetState() == eStateConnected) { ProcessAttachInfo attach_info; attach_info.SetProcessID (pid); error.SetError (process_sp->Attach (attach_info)); } else { error.SetErrorString ("must be in eStateConnected to call RemoteAttachToProcessWithID"); } } else { error.SetErrorString ("unable to attach pid"); } Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) { SBStream sstr; error.GetDescription (sstr); log->Printf ("SBProcess(%p)::RemoteAttachToProcessWithID (%" PRIu64 ") => SBError (%p): %s", static_cast(process_sp.get()), pid, static_cast(error.get()), sstr.GetData()); } return error.Success(); } uint32_t SBProcess::GetNumThreads () { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); uint32_t num_threads = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); num_threads = process_sp->GetThreadList().GetSize(can_update); } if (log) log->Printf ("SBProcess(%p)::GetNumThreads () => %d", static_cast(process_sp.get()), num_threads); return num_threads; } SBThread SBProcess::GetSelectedThread () const { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().GetSelectedThread(); sb_thread.SetThread (thread_sp); } if (log) log->Printf ("SBProcess(%p)::GetSelectedThread () => SBThread(%p)", static_cast(process_sp.get()), static_cast(thread_sp.get())); return sb_thread; } SBThread SBProcess::CreateOSPluginThread (lldb::tid_t tid, lldb::addr_t context) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->CreateOSPluginThread(tid, context); sb_thread.SetThread (thread_sp); } if (log) log->Printf ("SBProcess(%p)::CreateOSPluginThread (tid=0x%" PRIx64 ", context=0x%" PRIx64 ") => SBThread(%p)", static_cast(process_sp.get()), tid, context, static_cast(thread_sp.get())); return sb_thread; } SBTarget SBProcess::GetTarget() const { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); SBTarget sb_target; TargetSP target_sp; ProcessSP process_sp(GetSP()); if (process_sp) { target_sp = process_sp->GetTarget().shared_from_this(); sb_target.SetSP (target_sp); } if (log) log->Printf ("SBProcess(%p)::GetTarget () => SBTarget(%p)", static_cast(process_sp.get()), static_cast(target_sp.get())); return sb_target; } size_t SBProcess::PutSTDIN (const char *src, size_t src_len) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); size_t ret_val = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Error error; ret_val = process_sp->PutSTDIN (src, src_len, error); } if (log) log->Printf("SBProcess(%p)::PutSTDIN (src=\"%s\", src_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), src, static_cast(src_len), static_cast(ret_val)); return ret_val; } size_t SBProcess::GetSTDOUT (char *dst, size_t dst_len) const { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Error error; bytes_read = process_sp->GetSTDOUT (dst, dst_len, error); } Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::GetSTDOUT (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), static_cast(bytes_read), dst, static_cast(dst_len), static_cast(bytes_read)); return bytes_read; } size_t SBProcess::GetSTDERR (char *dst, size_t dst_len) const { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Error error; bytes_read = process_sp->GetSTDERR (dst, dst_len, error); } Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::GetSTDERR (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), static_cast(bytes_read), dst, static_cast(dst_len), static_cast(bytes_read)); return bytes_read; } size_t SBProcess::GetAsyncProfileData(char *dst, size_t dst_len) const { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Error error; bytes_read = process_sp->GetAsyncProfileData (dst, dst_len, error); } Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::GetAsyncProfileData (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), static_cast(bytes_read), dst, static_cast(dst_len), static_cast(bytes_read)); return bytes_read; } void SBProcess::ReportEventState (const SBEvent &event, FILE *out) const { if (out == NULL) return; ProcessSP process_sp(GetSP()); if (process_sp) { const StateType event_state = SBProcess::GetStateFromEvent (event); char message[1024]; int message_len = ::snprintf (message, sizeof (message), "Process %" PRIu64 " %s\n", process_sp->GetID(), SBDebugger::StateAsCString (event_state)); if (message_len > 0) ::fwrite (message, 1, message_len, out); } } void SBProcess::AppendEventStateReport (const SBEvent &event, SBCommandReturnObject &result) { ProcessSP process_sp(GetSP()); if (process_sp) { const StateType event_state = SBProcess::GetStateFromEvent (event); char message[1024]; ::snprintf (message, sizeof (message), "Process %" PRIu64 " %s\n", process_sp->GetID(), SBDebugger::StateAsCString (event_state)); result.AppendMessage (message); } } bool SBProcess::SetSelectedThread (const SBThread &thread) { ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); return process_sp->GetThreadList().SetSelectedThreadByID (thread.GetThreadID()); } return false; } bool SBProcess::SetSelectedThreadByID (lldb::tid_t tid) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); bool ret_val = false; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); ret_val = process_sp->GetThreadList().SetSelectedThreadByID (tid); } if (log) log->Printf ("SBProcess(%p)::SetSelectedThreadByID (tid=0x%4.4" PRIx64 ") => %s", static_cast(process_sp.get()), tid, (ret_val ? "true" : "false")); return ret_val; } bool SBProcess::SetSelectedThreadByIndexID (uint32_t index_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); bool ret_val = false; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); ret_val = process_sp->GetThreadList().SetSelectedThreadByIndexID (index_id); } if (log) log->Printf ("SBProcess(%p)::SetSelectedThreadByID (tid=0x%x) => %s", static_cast(process_sp.get()), index_id, (ret_val ? "true" : "false")); return ret_val; } SBThread SBProcess::GetThreadAtIndex (size_t index) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().GetThreadAtIndex(index, can_update); sb_thread.SetThread (thread_sp); } if (log) log->Printf ("SBProcess(%p)::GetThreadAtIndex (index=%d) => SBThread(%p)", static_cast(process_sp.get()), static_cast(index), static_cast(thread_sp.get())); return sb_thread; } uint32_t SBProcess::GetNumQueues () { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); uint32_t num_queues = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); num_queues = process_sp->GetQueueList().GetSize(); } if (log) log->Printf ("SBProcess(%p)::GetNumQueues () => %d", static_cast(process_sp.get()), num_queues); return num_queues; } SBQueue SBProcess::GetQueueAtIndex (size_t index) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); SBQueue sb_queue; QueueSP queue_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); queue_sp = process_sp->GetQueueList().GetQueueAtIndex(index); sb_queue.SetQueue (queue_sp); } if (log) log->Printf ("SBProcess(%p)::GetQueueAtIndex (index=%d) => SBQueue(%p)", static_cast(process_sp.get()), static_cast(index), static_cast(queue_sp.get())); return sb_queue; } uint32_t SBProcess::GetStopID(bool include_expression_stops) { ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); if (include_expression_stops) return process_sp->GetStopID(); else return process_sp->GetLastNaturalStopID(); } return 0; } SBEvent SBProcess::GetStopEventForStopID(uint32_t stop_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); SBEvent sb_event; EventSP event_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); event_sp = process_sp->GetStopEventForStopID(stop_id); sb_event.reset(event_sp); } if (log) log->Printf ("SBProcess(%p)::GetStopEventForStopID (stop_id=%" PRIu32 ") => SBEvent(%p)", static_cast(process_sp.get()), stop_id, static_cast(event_sp.get())); return sb_event; } StateType SBProcess::GetState () { StateType ret_val = eStateInvalid; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); ret_val = process_sp->GetState(); } Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::GetState () => %s", static_cast(process_sp.get()), lldb_private::StateAsCString (ret_val)); return ret_val; } int SBProcess::GetExitStatus () { int exit_status = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); exit_status = process_sp->GetExitStatus (); } Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::GetExitStatus () => %i (0x%8.8x)", static_cast(process_sp.get()), exit_status, exit_status); return exit_status; } const char * SBProcess::GetExitDescription () { const char *exit_desc = NULL; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); exit_desc = process_sp->GetExitDescription (); } Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::GetExitDescription () => %s", static_cast(process_sp.get()), exit_desc); return exit_desc; } lldb::pid_t SBProcess::GetProcessID () { lldb::pid_t ret_val = LLDB_INVALID_PROCESS_ID; ProcessSP process_sp(GetSP()); if (process_sp) ret_val = process_sp->GetID(); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::GetProcessID () => %" PRIu64, static_cast(process_sp.get()), ret_val); return ret_val; } uint32_t SBProcess::GetUniqueID() { uint32_t ret_val = 0; ProcessSP process_sp(GetSP()); if (process_sp) ret_val = process_sp->GetUniqueID(); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::GetUniqueID () => %" PRIu32, static_cast(process_sp.get()), ret_val); return ret_val; } ByteOrder SBProcess::GetByteOrder () const { ByteOrder byteOrder = eByteOrderInvalid; ProcessSP process_sp(GetSP()); if (process_sp) byteOrder = process_sp->GetTarget().GetArchitecture().GetByteOrder(); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::GetByteOrder () => %d", static_cast(process_sp.get()), byteOrder); return byteOrder; } uint32_t SBProcess::GetAddressByteSize () const { uint32_t size = 0; ProcessSP process_sp(GetSP()); if (process_sp) size = process_sp->GetTarget().GetArchitecture().GetAddressByteSize(); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::GetAddressByteSize () => %d", static_cast(process_sp.get()), size); return size; } SBError SBProcess::Continue () { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); SBError sb_error; ProcessSP process_sp(GetSP()); if (log) log->Printf ("SBProcess(%p)::Continue ()...", static_cast(process_sp.get())); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetTarget().GetDebugger().GetAsyncExecution ()) sb_error.ref() = process_sp->Resume (); else sb_error.ref() = process_sp->ResumeSynchronous (NULL); } else sb_error.SetErrorString ("SBProcess is invalid"); if (log) { SBStream sstr; sb_error.GetDescription (sstr); log->Printf ("SBProcess(%p)::Continue () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Destroy () { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Destroy(false)); } else sb_error.SetErrorString ("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription (sstr); log->Printf ("SBProcess(%p)::Destroy () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Stop () { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); sb_error.SetError (process_sp->Halt()); } else sb_error.SetErrorString ("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription (sstr); log->Printf ("SBProcess(%p)::Stop () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Kill () { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); sb_error.SetError (process_sp->Destroy(true)); } else sb_error.SetErrorString ("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription (sstr); log->Printf ("SBProcess(%p)::Kill () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Detach () { // FIXME: This should come from a process default. bool keep_stopped = false; return Detach (keep_stopped); } SBError SBProcess::Detach (bool keep_stopped) { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); sb_error.SetError (process_sp->Detach(keep_stopped)); } else sb_error.SetErrorString ("SBProcess is invalid"); return sb_error; } SBError SBProcess::Signal (int signo) { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); sb_error.SetError (process_sp->Signal (signo)); } else sb_error.SetErrorString ("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription (sstr); log->Printf ("SBProcess(%p)::Signal (signo=%i) => SBError (%p): %s", static_cast(process_sp.get()), signo, static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBUnixSignals SBProcess::GetUnixSignals() { if (auto process_sp = GetSP()) return SBUnixSignals{process_sp}; return {}; } void SBProcess::SendAsyncInterrupt () { ProcessSP process_sp(GetSP()); if (process_sp) { process_sp->SendAsyncInterrupt (); } } SBThread SBProcess::GetThreadByID (tid_t tid) { SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().FindThreadByID (tid, can_update); sb_thread.SetThread (thread_sp); } Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::GetThreadByID (tid=0x%4.4" PRIx64 ") => SBThread (%p)", static_cast(process_sp.get()), tid, static_cast(thread_sp.get())); return sb_thread; } SBThread SBProcess::GetThreadByIndexID (uint32_t index_id) { SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().FindThreadByIndexID (index_id, can_update); sb_thread.SetThread (thread_sp); } Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::GetThreadByID (tid=0x%x) => SBThread (%p)", static_cast(process_sp.get()), index_id, static_cast(thread_sp.get())); return sb_thread; } StateType SBProcess::GetStateFromEvent (const SBEvent &event) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); StateType ret_val = Process::ProcessEventData::GetStateFromEvent (event.get()); if (log) log->Printf ("SBProcess::GetStateFromEvent (event.sp=%p) => %s", static_cast(event.get()), lldb_private::StateAsCString (ret_val)); return ret_val; } bool SBProcess::GetRestartedFromEvent (const SBEvent &event) { - return Process::ProcessEventData::GetRestartedFromEvent (event.get()); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool ret_val = Process::ProcessEventData::GetRestartedFromEvent (event.get()); + + if (log) + log->Printf ("SBProcess::%s (event.sp=%p) => %d", __FUNCTION__, event.get(), ret_val); + + return ret_val; } size_t SBProcess::GetNumRestartedReasonsFromEvent (const lldb::SBEvent &event) { return Process::ProcessEventData::GetNumRestartedReasons(event.get()); } const char * SBProcess::GetRestartedReasonAtIndexFromEvent (const lldb::SBEvent &event, size_t idx) { return Process::ProcessEventData::GetRestartedReasonAtIndex(event.get(), idx); } SBProcess SBProcess::GetProcessFromEvent (const SBEvent &event) { SBProcess process(Process::ProcessEventData::GetProcessFromEvent (event.get())); return process; } bool SBProcess::GetInterruptedFromEvent (const SBEvent &event) { return Process::ProcessEventData::GetInterruptedFromEvent(event.get()); } bool SBProcess::EventIsProcessEvent (const SBEvent &event) { return event.GetBroadcasterClass() == SBProcess::GetBroadcasterClass(); } SBBroadcaster SBProcess::GetBroadcaster () const { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); ProcessSP process_sp(GetSP()); SBBroadcaster broadcaster(process_sp.get(), false); if (log) log->Printf ("SBProcess(%p)::GetBroadcaster () => SBBroadcaster (%p)", static_cast(process_sp.get()), static_cast(broadcaster.get())); return broadcaster; } const char * SBProcess::GetBroadcasterClass () { return Process::GetStaticBroadcasterClass().AsCString(); } size_t SBProcess::ReadMemory (addr_t addr, void *dst, size_t dst_len, SBError &sb_error) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (log) log->Printf ("SBProcess(%p)::ReadMemory (addr=0x%" PRIx64 ", dst=%p, dst_len=%" PRIu64 ", SBError (%p))...", static_cast(process_sp.get()), addr, static_cast(dst), static_cast(dst_len), static_cast(sb_error.get())); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); bytes_read = process_sp->ReadMemory (addr, dst, dst_len, sb_error.ref()); } else { if (log) log->Printf ("SBProcess(%p)::ReadMemory() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString ("SBProcess is invalid"); } if (log) { SBStream sstr; sb_error.GetDescription (sstr); log->Printf ("SBProcess(%p)::ReadMemory (addr=0x%" PRIx64 ", dst=%p, dst_len=%" PRIu64 ", SBError (%p): %s) => %" PRIu64, static_cast(process_sp.get()), addr, static_cast(dst), static_cast(dst_len), static_cast(sb_error.get()), sstr.GetData(), static_cast(bytes_read)); } return bytes_read; } size_t SBProcess::ReadCStringFromMemory (addr_t addr, void *buf, size_t size, lldb::SBError &sb_error) { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); bytes_read = process_sp->ReadCStringFromMemory (addr, (char *)buf, size, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::ReadCStringFromMemory() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString ("SBProcess is invalid"); } return bytes_read; } uint64_t SBProcess::ReadUnsignedFromMemory (addr_t addr, uint32_t byte_size, lldb::SBError &sb_error) { uint64_t value = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); value = process_sp->ReadUnsignedIntegerFromMemory (addr, byte_size, 0, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::ReadUnsignedFromMemory() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString ("SBProcess is invalid"); } return value; } lldb::addr_t SBProcess::ReadPointerFromMemory (addr_t addr, lldb::SBError &sb_error) { lldb::addr_t ptr = LLDB_INVALID_ADDRESS; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); ptr = process_sp->ReadPointerFromMemory (addr, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::ReadPointerFromMemory() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString ("SBProcess is invalid"); } return ptr; } size_t SBProcess::WriteMemory (addr_t addr, const void *src, size_t src_len, SBError &sb_error) { size_t bytes_written = 0; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); ProcessSP process_sp(GetSP()); if (log) log->Printf ("SBProcess(%p)::WriteMemory (addr=0x%" PRIx64 ", src=%p, src_len=%" PRIu64 ", SBError (%p))...", static_cast(process_sp.get()), addr, static_cast(src), static_cast(src_len), static_cast(sb_error.get())); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); bytes_written = process_sp->WriteMemory (addr, src, src_len, sb_error.ref()); } else { if (log) log->Printf ("SBProcess(%p)::WriteMemory() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } if (log) { SBStream sstr; sb_error.GetDescription (sstr); log->Printf ("SBProcess(%p)::WriteMemory (addr=0x%" PRIx64 ", src=%p, src_len=%" PRIu64 ", SBError (%p): %s) => %" PRIu64, static_cast(process_sp.get()), addr, static_cast(src), static_cast(src_len), static_cast(sb_error.get()), sstr.GetData(), static_cast(bytes_written)); } return bytes_written; } bool SBProcess::GetDescription (SBStream &description) { Stream &strm = description.ref(); ProcessSP process_sp(GetSP()); if (process_sp) { char path[PATH_MAX]; GetTarget().GetExecutable().GetPath (path, sizeof(path)); Module *exe_module = process_sp->GetTarget().GetExecutableModulePointer(); const char *exe_name = NULL; if (exe_module) exe_name = exe_module->GetFileSpec().GetFilename().AsCString(); strm.Printf ("SBProcess: pid = %" PRIu64 ", state = %s, threads = %d%s%s", process_sp->GetID(), lldb_private::StateAsCString (GetState()), GetNumThreads(), exe_name ? ", executable = " : "", exe_name ? exe_name : ""); } else strm.PutCString ("No value"); return true; } uint32_t SBProcess::GetNumSupportedHardwareWatchpoints (lldb::SBError &sb_error) const { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); uint32_t num = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->GetWatchpointSupportInfo (num)); if (log) log->Printf ("SBProcess(%p)::GetNumSupportedHardwareWatchpoints () => %u", static_cast(process_sp.get()), num); } else { sb_error.SetErrorString ("SBProcess is invalid"); } return num; } uint32_t SBProcess::LoadImage (lldb::SBFileSpec &sb_remote_image_spec, lldb::SBError &sb_error) { return LoadImage(SBFileSpec(), sb_remote_image_spec, sb_error); } uint32_t SBProcess::LoadImage (const lldb::SBFileSpec &sb_local_image_spec, const lldb::SBFileSpec &sb_remote_image_spec, lldb::SBError &sb_error) { ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); PlatformSP platform_sp = process_sp->GetTarget().GetPlatform(); return platform_sp->LoadImage (process_sp.get(), *sb_local_image_spec, *sb_remote_image_spec, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::LoadImage() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } return LLDB_INVALID_IMAGE_TOKEN; } lldb::SBError SBProcess::UnloadImage (uint32_t image_token) { lldb::SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); PlatformSP platform_sp = process_sp->GetTarget().GetPlatform(); sb_error.SetError (platform_sp->UnloadImage (process_sp.get(), image_token)); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::UnloadImage() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else sb_error.SetErrorString("invalid process"); return sb_error; } lldb::SBError SBProcess::SendEventData (const char *event_data) { lldb::SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); sb_error.SetError (process_sp->SendEventData (event_data)); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBProcess(%p)::SendEventData() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else sb_error.SetErrorString("invalid process"); return sb_error; } uint32_t SBProcess::GetNumExtendedBacktraceTypes () { ProcessSP process_sp(GetSP()); if (process_sp && process_sp->GetSystemRuntime()) { SystemRuntime *runtime = process_sp->GetSystemRuntime(); return runtime->GetExtendedBacktraceTypes().size(); } return 0; } const char * SBProcess::GetExtendedBacktraceTypeAtIndex (uint32_t idx) { ProcessSP process_sp(GetSP()); if (process_sp && process_sp->GetSystemRuntime()) { SystemRuntime *runtime = process_sp->GetSystemRuntime(); const std::vector &names = runtime->GetExtendedBacktraceTypes(); if (idx < names.size()) { return names[idx].AsCString(); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetExtendedBacktraceTypeAtIndex() => error: requested extended backtrace name out of bounds", static_cast(process_sp.get())); } } return NULL; } SBThreadCollection SBProcess::GetHistoryThreads (addr_t addr) { ProcessSP process_sp(GetSP()); SBThreadCollection threads; if (process_sp) { threads = SBThreadCollection(process_sp->GetHistoryThreads(addr)); } return threads; } bool SBProcess::IsInstrumentationRuntimePresent(InstrumentationRuntimeType type) { ProcessSP process_sp(GetSP()); if (! process_sp) return false; InstrumentationRuntimeSP runtime_sp = process_sp->GetInstrumentationRuntime(type); if (! runtime_sp.get()) return false; return runtime_sp->IsActive(); } lldb::SBError SBProcess::SaveCore(const char *file_name) { lldb::SBError error; ProcessSP process_sp(GetSP()); if (!process_sp) { error.SetErrorString("SBProcess is invalid"); return error; } Mutex::Locker api_locker(process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetState() != eStateStopped) { error.SetErrorString("the process is not stopped"); return error; } FileSpec core_file(file_name, false); error.ref() = PluginManager::SaveCore(process_sp, core_file); return error; } Index: projects/clang380-import/contrib/llvm/tools/lldb/source/API/SystemInitializerFull.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/API/SystemInitializerFull.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/API/SystemInitializerFull.cpp (revision 293283) @@ -1,419 +1,419 @@ //===-- SystemInitializerFull.cpp -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #if !defined(LLDB_DISABLE_PYTHON) #include "Plugins/ScriptInterpreter/Python/lldb-python.h" #endif #include "lldb/API/SystemInitializerFull.h" #include "lldb/API/SBCommandInterpreter.h" #if !defined(LLDB_DISABLE_PYTHON) #include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h" #endif #include "lldb/Core/Debugger.h" #include "lldb/Core/Timer.h" #include "lldb/Host/Host.h" #include "lldb/Initialization/SystemInitializerCommon.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/GoASTContext.h" #include "Plugins/ABI/SysV-arm/ABISysV_arm.h" #include "Plugins/ABI/SysV-arm64/ABISysV_arm64.h" #include "Plugins/ABI/SysV-i386/ABISysV_i386.h" #include "Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h" #include "Plugins/ABI/SysV-ppc/ABISysV_ppc.h" #include "Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h" #include "Plugins/ABI/SysV-mips/ABISysV_mips.h" #include "Plugins/ABI/SysV-mips64/ABISysV_mips64.h" #include "Plugins/Disassembler/llvm/DisassemblerLLVMC.h" #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" #include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h" #include "Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.h" #include "Plugins/JITLoader/GDB/JITLoaderGDB.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" #include "Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h" #include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h" #include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" #include "Plugins/Process/elf-core/ProcessElfCore.h" #include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" #include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h" #include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h" #include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h" #include "Plugins/SymbolVendor/ELF/SymbolVendorELF.h" #include "Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h" #include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h" #if defined(__APPLE__) #include "Plugins/Process/mach-core/ProcessMachCore.h" #include "Plugins/Process/MacOSX-Kernel/ProcessKDP.h" #include "Plugins/Platform/MacOSX/PlatformAppleTVSimulator.h" #include "Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.h" #include "Plugins/Platform/MacOSX/PlatformRemoteAppleTV.h" #include "Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.h" #endif #if defined(__FreeBSD__) #include "Plugins/Process/FreeBSD/ProcessFreeBSD.h" #endif #if defined(_MSC_VER) #include "lldb/Host/windows/windows.h" #include "Plugins/Process/Windows/Live/ProcessWindowsLive.h" #include "Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h" #endif #include "llvm/Support/TargetSelect.h" #include using namespace lldb_private; #ifndef LLDB_DISABLE_PYTHON // Defined in the SWIG source file #if PY_MAJOR_VERSION >= 3 extern "C" PyObject* PyInit__lldb(void); #define LLDBSwigPyInit PyInit__lldb #else -extern "C" void +extern "C" void init_lldb(void); #define LLDBSwigPyInit init_lldb #endif // these are the Pythonic implementations of the required callbacks // these are scripting-language specific, which is why they belong here // we still need to use function pointers to them instead of relying // on linkage-time resolution because the SWIG stuff and this file // get built at different times extern "C" bool LLDBSwigPythonBreakpointCallbackFunction (const char *python_function_name, const char *session_dictionary_name, const lldb::StackFrameSP& sb_frame, const lldb::BreakpointLocationSP& sb_bp_loc); extern "C" bool LLDBSwigPythonWatchpointCallbackFunction (const char *python_function_name, const char *session_dictionary_name, const lldb::StackFrameSP& sb_frame, const lldb::WatchpointSP& sb_wp); extern "C" bool LLDBSwigPythonCallTypeScript (const char *python_function_name, void *session_dictionary, const lldb::ValueObjectSP& valobj_sp, void** pyfunct_wrapper, const lldb::TypeSummaryOptionsSP& options_sp, std::string& retval); extern "C" void* LLDBSwigPythonCreateSyntheticProvider (const char *python_class_name, const char *session_dictionary_name, const lldb::ValueObjectSP& valobj_sp); extern "C" void* LLDBSwigPythonCreateCommandObject (const char *python_class_name, const char *session_dictionary_name, const lldb::DebuggerSP debugger_sp); extern "C" void* LLDBSwigPythonCreateScriptedThreadPlan (const char *python_class_name, const char *session_dictionary_name, const lldb::ThreadPlanSP& thread_plan_sp); extern "C" bool LLDBSWIGPythonCallThreadPlan (void *implementor, const char *method_name, Event *event_sp, bool &got_error); extern "C" size_t LLDBSwigPython_CalculateNumChildren (void *implementor, uint32_t max); extern "C" void * LLDBSwigPython_GetChildAtIndex (void *implementor, uint32_t idx); extern "C" int LLDBSwigPython_GetIndexOfChildWithName (void *implementor, const char* child_name); extern "C" void * LLDBSWIGPython_CastPyObjectToSBValue (void* data); extern lldb::ValueObjectSP LLDBSWIGPython_GetValueObjectSPFromSBValue (void* data); extern "C" bool LLDBSwigPython_UpdateSynthProviderInstance (void* implementor); extern "C" bool LLDBSwigPython_MightHaveChildrenSynthProviderInstance (void* implementor); extern "C" void * LLDBSwigPython_GetValueSynthProviderInstance (void* implementor); extern "C" bool LLDBSwigPythonCallCommand (const char *python_function_name, const char *session_dictionary_name, lldb::DebuggerSP& debugger, const char* args, lldb_private::CommandReturnObject &cmd_retobj, lldb::ExecutionContextRefSP exe_ctx_ref_sp); extern "C" bool LLDBSwigPythonCallCommandObject (void *implementor, lldb::DebuggerSP& debugger, const char* args, lldb_private::CommandReturnObject& cmd_retobj, lldb::ExecutionContextRefSP exe_ctx_ref_sp); extern "C" bool LLDBSwigPythonCallModuleInit (const char *python_module_name, const char *session_dictionary_name, lldb::DebuggerSP& debugger); extern "C" void* LLDBSWIGPythonCreateOSPlugin (const char *python_class_name, const char *session_dictionary_name, const lldb::ProcessSP& process_sp); extern "C" bool LLDBSWIGPythonRunScriptKeywordProcess (const char* python_function_name, const char* session_dictionary_name, lldb::ProcessSP& process, std::string& output); extern "C" bool LLDBSWIGPythonRunScriptKeywordThread (const char* python_function_name, const char* session_dictionary_name, lldb::ThreadSP& thread, std::string& output); extern "C" bool LLDBSWIGPythonRunScriptKeywordTarget (const char* python_function_name, const char* session_dictionary_name, lldb::TargetSP& target, std::string& output); extern "C" bool LLDBSWIGPythonRunScriptKeywordFrame (const char* python_function_name, const char* session_dictionary_name, lldb::StackFrameSP& frame, std::string& output); extern "C" bool LLDBSWIGPythonRunScriptKeywordValue (const char* python_function_name, const char* session_dictionary_name, lldb::ValueObjectSP& value, std::string& output); extern "C" void* LLDBSWIGPython_GetDynamicSetting (void* module, const char* setting, const lldb::TargetSP& target_sp); #endif SystemInitializerFull::SystemInitializerFull() { } SystemInitializerFull::~SystemInitializerFull() { } void SystemInitializerFull::Initialize() { SystemInitializerCommon::Initialize(); ScriptInterpreterNone::Initialize(); #if !defined(LLDB_DISABLE_PYTHON) InitializeSWIG(); // ScriptInterpreterPython::Initialize() depends on things like HostInfo being initialized // so it can compute the python directory etc, so we need to do this after // SystemInitializerCommon::Initialize(). ScriptInterpreterPython::Initialize(); #endif // Initialize LLVM and Clang llvm::InitializeAllTargets(); llvm::InitializeAllAsmPrinters(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllDisassemblers(); ClangASTContext::Initialize(); GoASTContext::Initialize(); ABISysV_arm::Initialize(); ABISysV_arm64::Initialize(); ABISysV_i386::Initialize(); ABISysV_x86_64::Initialize(); ABISysV_ppc::Initialize(); ABISysV_ppc64::Initialize(); ABISysV_mips::Initialize(); ABISysV_mips64::Initialize(); DisassemblerLLVMC::Initialize(); JITLoaderGDB::Initialize(); ProcessElfCore::Initialize(); #if defined(_MSC_VER) ProcessWinMiniDump::Initialize(); #endif MemoryHistoryASan::Initialize(); AddressSanitizerRuntime::Initialize(); SymbolVendorELF::Initialize(); SymbolFileDWARF::Initialize(); SymbolFileSymtab::Initialize(); UnwindAssemblyInstEmulation::Initialize(); UnwindAssembly_x86::Initialize(); EmulateInstructionARM64::Initialize(); SymbolFileDWARFDebugMap::Initialize(); ItaniumABILanguageRuntime::Initialize(); - + CPlusPlusLanguage::Initialize(); #if defined(_MSC_VER) ProcessWindowsLive::Initialize(); #endif #if defined(__FreeBSD__) ProcessFreeBSD::Initialize(); #endif #if defined(__APPLE__) SymbolVendorMacOSX::Initialize(); ProcessKDP::Initialize(); ProcessMachCore::Initialize(); PlatformAppleTVSimulator::Initialize(); PlatformAppleWatchSimulator::Initialize(); PlatformRemoteAppleTV::Initialize(); PlatformRemoteAppleWatch::Initialize(); #endif //---------------------------------------------------------------------- // Platform agnostic plugins //---------------------------------------------------------------------- platform_gdb_server::PlatformRemoteGDBServer::Initialize(); process_gdb_remote::ProcessGDBRemote::Initialize(); DynamicLoaderStatic::Initialize(); // Scan for any system or user LLDB plug-ins PluginManager::Initialize(); // The process settings need to know about installed plug-ins, so the Settings must be initialized // AFTER PluginManager::Initialize is called. Debugger::SettingsInitialize(); } void SystemInitializerFull::InitializeSWIG() { #if !defined(LLDB_DISABLE_PYTHON) ScriptInterpreterPython::InitializeInterpreter( LLDBSwigPyInit, LLDBSwigPythonBreakpointCallbackFunction, LLDBSwigPythonWatchpointCallbackFunction, LLDBSwigPythonCallTypeScript, LLDBSwigPythonCreateSyntheticProvider, LLDBSwigPythonCreateCommandObject, LLDBSwigPython_CalculateNumChildren, LLDBSwigPython_GetChildAtIndex, LLDBSwigPython_GetIndexOfChildWithName, LLDBSWIGPython_CastPyObjectToSBValue, LLDBSWIGPython_GetValueObjectSPFromSBValue, LLDBSwigPython_UpdateSynthProviderInstance, LLDBSwigPython_MightHaveChildrenSynthProviderInstance, LLDBSwigPython_GetValueSynthProviderInstance, LLDBSwigPythonCallCommand, LLDBSwigPythonCallCommandObject, LLDBSwigPythonCallModuleInit, LLDBSWIGPythonCreateOSPlugin, LLDBSWIGPythonRunScriptKeywordProcess, LLDBSWIGPythonRunScriptKeywordThread, LLDBSWIGPythonRunScriptKeywordTarget, LLDBSWIGPythonRunScriptKeywordFrame, LLDBSWIGPythonRunScriptKeywordValue, LLDBSWIGPython_GetDynamicSetting, LLDBSwigPythonCreateScriptedThreadPlan, LLDBSWIGPythonCallThreadPlan); #endif } void SystemInitializerFull::Terminate() { Timer scoped_timer(__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); Debugger::SettingsTerminate(); // Terminate and unload and loaded system or user LLDB plug-ins PluginManager::Terminate(); ClangASTContext::Terminate(); GoASTContext::Terminate(); ABISysV_arm::Terminate(); ABISysV_arm64::Terminate(); ABISysV_i386::Terminate(); ABISysV_x86_64::Terminate(); ABISysV_ppc::Terminate(); ABISysV_ppc64::Terminate(); ABISysV_mips::Terminate(); ABISysV_mips64::Terminate(); DisassemblerLLVMC::Terminate(); JITLoaderGDB::Terminate(); ProcessElfCore::Terminate(); #if defined(_MSC_VER) ProcessWinMiniDump::Terminate(); #endif MemoryHistoryASan::Terminate(); AddressSanitizerRuntime::Terminate(); SymbolVendorELF::Terminate(); SymbolFileDWARF::Terminate(); SymbolFileSymtab::Terminate(); UnwindAssembly_x86::Terminate(); UnwindAssemblyInstEmulation::Terminate(); EmulateInstructionARM64::Terminate(); SymbolFileDWARFDebugMap::Terminate(); ItaniumABILanguageRuntime::Terminate(); CPlusPlusLanguage::Terminate(); - + #if defined(__APPLE__) ProcessMachCore::Terminate(); ProcessKDP::Terminate(); SymbolVendorMacOSX::Terminate(); PlatformAppleTVSimulator::Terminate(); PlatformAppleWatchSimulator::Terminate(); PlatformRemoteAppleTV::Terminate(); PlatformRemoteAppleWatch::Terminate(); #endif #if defined(__FreeBSD__) ProcessFreeBSD::Terminate(); #endif Debugger::SettingsTerminate(); platform_gdb_server::PlatformRemoteGDBServer::Terminate(); process_gdb_remote::ProcessGDBRemote::Terminate(); DynamicLoaderStatic::Terminate(); // Now shutdown the common parts, in reverse order. SystemInitializerCommon::Terminate(); } Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.cpp (revision 293283) @@ -1,928 +1,1505 @@ //===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "CommandObjectSource.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/Debugger.h" #include "lldb/Core/FileLineResolver.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/SourceManager.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/StringConvert.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StackFrame.h" #include "lldb/Target/TargetList.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/Options.h" using namespace lldb; using namespace lldb_private; -//------------------------------------------------------------------------- -// CommandObjectSourceInfo -//------------------------------------------------------------------------- +#pragma mark CommandObjectSourceInfo +//---------------------------------------------------------------------- +// CommandObjectSourceInfo - debug line entries dumping command +//---------------------------------------------------------------------- + class CommandObjectSourceInfo : public CommandObjectParsed { class CommandOptions : public Options { public: - CommandOptions (CommandInterpreter &interpreter) : - Options(interpreter) - { - } + CommandOptions (CommandInterpreter &interpreter) : Options(interpreter) {} - ~CommandOptions () override - { - } + ~CommandOptions () override {} Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = g_option_table[option_idx].short_option; switch (short_option) { - case 'l': - start_line = StringConvert::ToUInt32 (option_arg, 0); - if (start_line == 0) - error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); - break; + case 'l': + start_line = StringConvert::ToUInt32(option_arg, 0); + if (start_line == 0) + error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); + break; - case 'f': - file_name = option_arg; - break; + case 'e': + end_line = StringConvert::ToUInt32(option_arg, 0); + if (end_line == 0) + error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); + break; - default: - error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + case 'c': + num_lines = StringConvert::ToUInt32(option_arg, 0); + if (num_lines == 0) + error.SetErrorStringWithFormat("invalid line count: '%s'", option_arg); + break; + + case 'f': + file_name = option_arg; + break; + + case 'n': + symbol_name = option_arg; + break; + + case 'a': + { + ExecutionContext exe_ctx(m_interpreter.GetExecutionContext()); + address = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + } break; + case 's': + modules.push_back(std::string(option_arg)); + break; + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; } return error; } void OptionParsingStarting () override { file_spec.Clear(); file_name.clear(); + symbol_name.clear(); + address = LLDB_INVALID_ADDRESS; start_line = 0; + end_line = 0; + num_lines = 0; + modules.clear(); } - const OptionDefinition* + const OptionDefinition * GetDefinitions () override { return g_option_table; } static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. FileSpec file_spec; std::string file_name; + std::string symbol_name; + lldb::addr_t address; uint32_t start_line; - + uint32_t end_line; + uint32_t num_lines; + STLStringArray modules; }; - -public: - CommandObjectSourceInfo(CommandInterpreter &interpreter) : - CommandObjectParsed (interpreter, - "source info", - "Display information about the source lines from the current executable's debug info.", - "source info []"), - m_options (interpreter) - { - } - ~CommandObjectSourceInfo () override +public: + CommandObjectSourceInfo (CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "source info", "Display source line information (as specified) based " + "on the current executable's debug info.", + NULL, eCommandRequiresTarget), + m_options(interpreter) { } + ~CommandObjectSourceInfo () override {} Options * GetOptions () override { return &m_options; } protected: + + // Dump the line entries in each symbol context. + // Return the number of entries found. + // If module_list is set, only dump lines contained in one of the modules. + // If file_spec is set, only dump lines in the file. + // If the start_line option was specified, don't print lines less than start_line. + // If the end_line option was specified, don't print lines greater than end_line. + // If the num_lines option was specified, dont print more than num_lines entries. + uint32_t + DumpLinesInSymbolContexts (Stream &strm, const SymbolContextList &sc_list, + const ModuleList &module_list, const FileSpec &file_spec) + { + uint32_t start_line = m_options.start_line; + uint32_t end_line = m_options.end_line; + uint32_t num_lines = m_options.num_lines; + Target *target = m_exe_ctx.GetTargetPtr(); + + uint32_t num_matches = 0; + bool has_path = false; + if (file_spec) + { + assert(file_spec.GetFilename().AsCString()); + has_path = (file_spec.GetDirectory().AsCString() != 0); + } + + // Dump all the line entries for the file in the list. + ConstString last_module_file_name; + uint32_t num_scs = sc_list.GetSize(); + for (uint32_t i = 0; i < num_scs; ++i) + { + SymbolContext sc; + sc_list.GetContextAtIndex(i, sc); + if (sc.comp_unit) + { + Module *module = sc.module_sp.get(); + CompileUnit *cu = sc.comp_unit; + const LineEntry &line_entry = sc.line_entry; + assert(module && cu); + + // Are we looking for specific modules, files or lines? + if (module_list.GetSize() && module_list.GetIndexForModule(module) == LLDB_INVALID_INDEX32) + continue; + if (file_spec && !lldb_private::FileSpec::Equal(file_spec, line_entry.file, has_path)) + continue; + if (start_line > 0 && line_entry.line < start_line) + continue; + if (end_line > 0 && line_entry.line > end_line) + continue; + if (num_lines > 0 && num_matches > num_lines) + continue; + + // Print a new header if the module changed. + const ConstString &module_file_name = module->GetFileSpec().GetFilename(); + assert(module_file_name); + if (module_file_name != last_module_file_name) + { + if (num_matches > 0) + strm << "\n\n"; + strm << "Lines found in module `" << module_file_name << "\n"; + } + // Dump the line entry. + line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu, + target, /*show_address_only=*/false); + strm << "\n"; + last_module_file_name = module_file_name; + num_matches++; + } + } + return num_matches; + } + + // Dump the requested line entries for the file in the compilation unit. + // Return the number of entries found. + // If module_list is set, only dump lines contained in one of the modules. + // If the start_line option was specified, don't print lines less than start_line. + // If the end_line option was specified, don't print lines greater than end_line. + // If the num_lines option was specified, dont print more than num_lines entries. + uint32_t + DumpFileLinesInCompUnit (Stream &strm, Module *module, CompileUnit *cu, const FileSpec &file_spec) + { + uint32_t start_line = m_options.start_line; + uint32_t end_line = m_options.end_line; + uint32_t num_lines = m_options.num_lines; + Target *target = m_exe_ctx.GetTargetPtr(); + + uint32_t num_matches = 0; + assert(module); + if (cu) + { + assert(file_spec.GetFilename().AsCString()); + bool has_path = (file_spec.GetDirectory().AsCString() != 0); + const FileSpecList &cu_file_list = cu->GetSupportFiles(); + size_t file_idx = cu_file_list.FindFileIndex(0, file_spec, has_path); + if (file_idx != UINT32_MAX) + { + // Update the file to how it appears in the CU. + const FileSpec &cu_file_spec = cu_file_list.GetFileSpecAtIndex(file_idx); + + // Dump all matching lines at or above start_line for the file in the CU. + const ConstString &file_spec_name = file_spec.GetFilename(); + const ConstString &module_file_name = module->GetFileSpec().GetFilename(); + bool cu_header_printed = false; + uint32_t line = start_line; + while (true) + { + LineEntry line_entry; + + // Find the lowest index of a line entry with a line equal to + // or higher than 'line'. + uint32_t start_idx = 0; + start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec, + /*exact=*/false, &line_entry); + if (start_idx == UINT32_MAX) + // No more line entries for our file in this CU. + break; + + if (end_line > 0 && line_entry.line > end_line) + break; + + // Loop through to find any other entries for this line, dumping each. + line = line_entry.line; + do + { + num_matches++; + if (num_lines > 0 && num_matches > num_lines) + break; + assert(lldb_private::FileSpec::Equal(cu_file_spec, line_entry.file, has_path)); + if (!cu_header_printed) + { + if (num_matches > 0) + strm << "\n\n"; + strm << "Lines found for file " << file_spec_name + << " in compilation unit " << cu->GetFilename() + << " in `" << module_file_name << "\n"; + cu_header_printed = true; + } + line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu, + target, /*show_address_only=*/false); + strm << "\n"; + + // Anymore after this one? + start_idx++; + start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec, + /*exact=*/true, &line_entry); + } while (start_idx != UINT32_MAX); + + // Try the next higher line, starting over at start_idx 0. + line++; + } + } + } + return num_matches; + } + + // Dump the requested line entries for the file in the module. + // Return the number of entries found. + // If module_list is set, only dump lines contained in one of the modules. + // If the start_line option was specified, don't print lines less than start_line. + // If the end_line option was specified, don't print lines greater than end_line. + // If the num_lines option was specified, dont print more than num_lines entries. + uint32_t + DumpFileLinesInModule (Stream &strm, Module *module, const FileSpec &file_spec) + { + uint32_t num_matches = 0; + if (module) + { + // Look through all the compilation units (CUs) in this module for ones that + // contain lines of code from this source file. + for (size_t i = 0; i < module->GetNumCompileUnits(); i++) + { + // Look for a matching source file in this CU. + CompUnitSP cu_sp(module->GetCompileUnitAtIndex(i)); + if (cu_sp) + { + num_matches += DumpFileLinesInCompUnit(strm, module, cu_sp.get(), file_spec); + } + } + } + return num_matches; + } + + // Given an address and a list of modules, append the symbol contexts of all line entries + // containing the address found in the modules and return the count of matches. If none + // is found, return an error in 'error_strm'. + size_t + GetSymbolContextsForAddress (const ModuleList &module_list, lldb::addr_t addr, + SymbolContextList &sc_list, StreamString &error_strm) + { + Address so_addr; + size_t num_matches = 0; + assert(module_list.GetSize() > 0); + Target *target = m_exe_ctx.GetTargetPtr(); + if (target->GetSectionLoadList().IsEmpty()) + { + // The target isn't loaded yet, we need to lookup the file address in + // all modules. Note: the module list option does not apply to addresses. + const size_t num_modules = module_list.GetSize(); + for (size_t i = 0; i < num_modules; ++i) + { + ModuleSP module_sp(module_list.GetModuleAtIndex(i)); + if (!module_sp) + continue; + if (module_sp->ResolveFileAddress(addr, so_addr)) + { + SymbolContext sc; + sc.Clear(true); + if (module_sp->ResolveSymbolContextForAddress(so_addr, eSymbolContextEverything, sc) & + eSymbolContextLineEntry) + { + sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false); + ++num_matches; + } + } + } + if (num_matches == 0) + error_strm.Printf("Source information for file address 0x%" PRIx64 + " not found in any modules.\n", addr); + } + else + { + // The target has some things loaded, resolve this address to a + // compile unit + file + line and display + if (target->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) + { + ModuleSP module_sp(so_addr.GetModule()); + // Check to make sure this module is in our list. + if (module_sp && + module_list.GetIndexForModule(module_sp.get()) != LLDB_INVALID_INDEX32) + { + SymbolContext sc; + sc.Clear(true); + if (module_sp->ResolveSymbolContextForAddress(so_addr, eSymbolContextEverything, sc) & + eSymbolContextLineEntry) + { + sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false); + ++num_matches; + } + else + { + StreamString addr_strm; + so_addr.Dump(&addr_strm, NULL, Address::DumpStyleModuleWithFileAddress); + error_strm.Printf("Address 0x%" PRIx64 " resolves to %s, but there is" + " no source information available for this address.\n", + addr, addr_strm.GetData()); + } + } + else + { + StreamString addr_strm; + so_addr.Dump(&addr_strm, NULL, Address::DumpStyleModuleWithFileAddress); + error_strm.Printf("Address 0x%" PRIx64 " resolves to %s, but it cannot" + " be found in any modules.\n", + addr, addr_strm.GetData()); + } + } + else + error_strm.Printf("Unable to resolve address 0x%" PRIx64 ".\n", addr); + } + return num_matches; + } + + // Dump the line entries found in functions matching the name specified in the option. bool - DoExecute (Args& command, CommandReturnObject &result) override + DumpLinesInFunctions (CommandReturnObject &result) { - result.AppendError ("Not yet implemented"); - result.SetStatus (eReturnStatusFailed); - return false; + SymbolContextList sc_list_funcs; + ConstString name(m_options.symbol_name.c_str()); + SymbolContextList sc_list_lines; + Target *target = m_exe_ctx.GetTargetPtr(); + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + + // Note: module_list can't be const& because FindFunctionSymbols isn't const. + ModuleList module_list = (m_module_list.GetSize() > 0) ? + m_module_list : target->GetImages(); + size_t num_matches = module_list.FindFunctions(name, + eFunctionNameTypeAuto, + /*include_symbols=*/false, + /*include_inlines=*/true, + /*append=*/true, + sc_list_funcs); + if (!num_matches) + { + // If we didn't find any functions with that name, try searching for + // symbols that line up exactly with function addresses. + SymbolContextList sc_list_symbols; + size_t num_symbol_matches = module_list.FindFunctionSymbols(name, + eFunctionNameTypeAuto, + sc_list_symbols); + for (size_t i = 0; i < num_symbol_matches; i++) + { + SymbolContext sc; + sc_list_symbols.GetContextAtIndex(i, sc); + if (sc.symbol && sc.symbol->ValueIsAddress()) + { + const Address &base_address = sc.symbol->GetAddressRef(); + Function *function = base_address.CalculateSymbolContextFunction(); + if (function) + { + sc_list_funcs.Append(SymbolContext(function)); + num_matches++; + } + } + } + } + if (num_matches == 0) + { + result.AppendErrorWithFormat("Could not find function named \'%s\'.\n", + m_options.symbol_name.c_str()); + return false; + } + for (size_t i = 0; i < num_matches; i++) + { + SymbolContext sc; + sc_list_funcs.GetContextAtIndex(i, sc); + bool context_found_for_symbol = false; + // Loop through all the ranges in the function. + AddressRange range; + for (uint32_t r = 0; + sc.GetAddressRange(eSymbolContextEverything, + r, + /*use_inline_block_range=*/true, + range); + ++r) + { + // Append the symbol contexts for each address in the range to sc_list_lines. + const Address &base_address = range.GetBaseAddress(); + const addr_t size = range.GetByteSize(); + lldb::addr_t start_addr = base_address.GetLoadAddress(target); + if (start_addr == LLDB_INVALID_ADDRESS) + start_addr = base_address.GetFileAddress(); + lldb::addr_t end_addr = start_addr + size; + for (lldb::addr_t addr = start_addr; addr < end_addr; addr += addr_byte_size) + { + StreamString error_strm; + if (!GetSymbolContextsForAddress(module_list, addr, sc_list_lines, error_strm)) + result.AppendWarningWithFormat("in symbol '%s': %s", + sc.GetFunctionName().AsCString(), + error_strm.GetData()); + else + context_found_for_symbol = true; + } + } + if (!context_found_for_symbol) + result.AppendWarningWithFormat("Unable to find line information" + " for matching symbol '%s'.\n", + sc.GetFunctionName().AsCString()); + } + if (sc_list_lines.GetSize() == 0) + { + result.AppendErrorWithFormat("No line information could be found" + " for any symbols matching '%s'.\n", + name.AsCString()); + return false; + } + FileSpec file_spec; + if (!DumpLinesInSymbolContexts(result.GetOutputStream(), + sc_list_lines, module_list, file_spec)) + { + result.AppendErrorWithFormat("Unable to dump line information for symbol '%s'.\n", + name.AsCString()); + return false; + } + return true; } + // Dump the line entries found for the address specified in the option. + bool + DumpLinesForAddress (CommandReturnObject &result) + { + Target *target = m_exe_ctx.GetTargetPtr(); + SymbolContextList sc_list; + + StreamString error_strm; + if (!GetSymbolContextsForAddress(target->GetImages(), m_options.address, sc_list, error_strm)) + { + result.AppendErrorWithFormat("%s.\n", error_strm.GetData()); + return false; + } + ModuleList module_list; + FileSpec file_spec; + if (!DumpLinesInSymbolContexts(result.GetOutputStream(), + sc_list, module_list, file_spec)) + { + result.AppendErrorWithFormat("No modules contain load address 0x%" PRIx64 ".\n", + m_options.address); + return false; + } + return true; + } + + // Dump the line entries found in the file specified in the option. + bool + DumpLinesForFile (CommandReturnObject &result) + { + FileSpec file_spec(m_options.file_name, false); + const char *filename = m_options.file_name.c_str(); + Target *target = m_exe_ctx.GetTargetPtr(); + const ModuleList &module_list = (m_module_list.GetSize() > 0) ? + m_module_list : target->GetImages(); + + bool displayed_something = false; + const size_t num_modules = module_list.GetSize(); + for (uint32_t i = 0; i < num_modules; ++i) + { + // Dump lines for this module. + Module *module = module_list.GetModulePointerAtIndex(i); + assert(module); + if (DumpFileLinesInModule(result.GetOutputStream(), module, file_spec)) + displayed_something = true; + } + if (!displayed_something) + { + result.AppendErrorWithFormat("No source filenames matched '%s'.\n", filename); + return false; + } + return true; + } + + // Dump the line entries for the current frame. + bool + DumpLinesForFrame (CommandReturnObject &result) + { + StackFrame *cur_frame = m_exe_ctx.GetFramePtr(); + if (cur_frame == NULL) + { + result.AppendError("No selected frame to use to find the default source."); + return false; + } + else if (!cur_frame->HasDebugInformation()) + { + result.AppendError("No debug info for the selected frame."); + return false; + } + else + { + const SymbolContext &sc = cur_frame->GetSymbolContext(eSymbolContextLineEntry); + SymbolContextList sc_list; + sc_list.Append(sc); + ModuleList module_list; + FileSpec file_spec; + if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list, module_list, file_spec)) + { + result.AppendError("No source line info available for the selected frame."); + return false; + } + } + return true; + } + + bool + DoExecute (Args &command, CommandReturnObject &result) override + { + const size_t argc = command.GetArgumentCount(); + + if (argc != 0) + { + result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", + GetCommandName()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Target *target = m_exe_ctx.GetTargetPtr(); + if (target == NULL) + { + target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError("invalid target, create a debug target using the " + "'target create' command."); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + result.GetOutputStream().SetAddressByteSize(addr_byte_size); + result.GetErrorStream().SetAddressByteSize(addr_byte_size); + + // Collect the list of modules to search. + m_module_list.Clear(); + if (m_options.modules.size() > 0) + { + for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) + { + FileSpec module_file_spec(m_options.modules[i].c_str(), false); + if (module_file_spec) + { + ModuleSpec module_spec(module_file_spec); + if (target->GetImages().FindModules(module_spec, m_module_list) == 0) + result.AppendWarningWithFormat("No module found for '%s'.\n", + m_options.modules[i].c_str()); + } + } + if (!m_module_list.GetSize()) + { + result.AppendError("No modules match the input."); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + else if (target->GetImages().GetSize() == 0) + { + result.AppendError("The target has no associated executable images."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // Check the arguments to see what lines we should dump. + if (!m_options.symbol_name.empty()) + { + // Print lines for symbol. + if (DumpLinesInFunctions(result)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + result.SetStatus(eReturnStatusFailed); + } + else if (m_options.address != LLDB_INVALID_ADDRESS) + { + // Print lines for an address. + if (DumpLinesForAddress(result)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + result.SetStatus(eReturnStatusFailed); + } + else if (!m_options.file_name.empty()) + { + // Dump lines for a file. + if (DumpLinesForFile(result)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + result.SetStatus(eReturnStatusFailed); + } + else + { + // Dump the line for the current frame. + if (DumpLinesForFrame(result)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + result.SetStatus(eReturnStatusFailed); + } + return result.Succeeded(); + } + CommandOptions m_options; + ModuleList m_module_list; }; -OptionDefinition -CommandObjectSourceInfo::CommandOptions::g_option_table[] = -{ -{ LLDB_OPT_SET_1, false, "line", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, "The line number at which to start the display source."}, -{ LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, NULL, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, -{ 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } +OptionDefinition CommandObjectSourceInfo::CommandOptions::g_option_table[] = { + {LLDB_OPT_SET_ALL, false, "count", 'c', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeCount, + "The number of line entries to display."}, + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "shlib", 's', OptionParser::eRequiredArgument, NULL, NULL, + CommandCompletions::eModuleCompletion, eArgTypeShlibName, + "Look up the source in the given module or shared library (can be " + "specified more than once)."}, + {LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, NULL, NULL, + CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, + {LLDB_OPT_SET_1, false, "line", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, + "The line number at which to start the displaying lines."}, + {LLDB_OPT_SET_1, false, "end-line", 'e', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, + "The line number at which to stop displaying lines."}, + {LLDB_OPT_SET_2, false, "name", 'n', OptionParser::eRequiredArgument, NULL, NULL, + CommandCompletions::eSymbolCompletion, eArgTypeSymbol, "The name of a function whose source to display."}, + {LLDB_OPT_SET_3, false, "address", 'a', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeAddressOrExpression, + "Lookup the address and display the source information for the " + "corresponding file and line."}, + {0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL} }; + #pragma mark CommandObjectSourceList //------------------------------------------------------------------------- // CommandObjectSourceList //------------------------------------------------------------------------- class CommandObjectSourceList : public CommandObjectParsed { class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options(interpreter) { } ~CommandOptions () override { } Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = g_option_table[option_idx].short_option; switch (short_option) { case 'l': start_line = StringConvert::ToUInt32 (option_arg, 0); if (start_line == 0) error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); break; case 'c': num_lines = StringConvert::ToUInt32 (option_arg, 0); if (num_lines == 0) error.SetErrorStringWithFormat("invalid line count: '%s'", option_arg); break; case 'f': file_name = option_arg; break; case 'n': symbol_name = option_arg; break; case 'a': { ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); address = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); } break; case 's': modules.push_back (std::string (option_arg)); break; case 'b': show_bp_locs = true; break; case 'r': reverse = true; break; default: error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); break; } return error; } void OptionParsingStarting () override { file_spec.Clear(); file_name.clear(); symbol_name.clear(); address = LLDB_INVALID_ADDRESS; start_line = 0; num_lines = 0; show_bp_locs = false; reverse = false; modules.clear(); } const OptionDefinition* GetDefinitions () override { return g_option_table; } static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. FileSpec file_spec; std::string file_name; std::string symbol_name; lldb::addr_t address; uint32_t start_line; uint32_t num_lines; STLStringArray modules; bool show_bp_locs; bool reverse; }; public: CommandObjectSourceList(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "source list", "Display source code (as specified) based on the current executable's debug info.", NULL, eCommandRequiresTarget), m_options (interpreter) { } ~CommandObjectSourceList () override { } Options * GetOptions () override { return &m_options; } const char * GetRepeatCommand (Args ¤t_command_args, uint32_t index) override { // This is kind of gross, but the command hasn't been parsed yet so we can't look at the option // values for this invocation... I have to scan the arguments directly. size_t num_args = current_command_args.GetArgumentCount(); bool is_reverse = false; for (size_t i = 0 ; i < num_args; i++) { const char *arg = current_command_args.GetArgumentAtIndex(i); if (arg && (strcmp(arg, "-r") == 0 || strcmp(arg, "--reverse") == 0)) { is_reverse = true; } } if (is_reverse) { if (m_reverse_name.empty()) { m_reverse_name = m_cmd_name; m_reverse_name.append (" -r"); } return m_reverse_name.c_str(); } else return m_cmd_name.c_str(); } protected: struct SourceInfo { ConstString function; LineEntry line_entry; SourceInfo (const ConstString &name, const LineEntry &line_entry) : function(name), line_entry(line_entry) { } SourceInfo () : function(), line_entry() { } bool IsValid () const { return (bool)function && line_entry.IsValid(); } bool operator == (const SourceInfo &rhs) const { return function == rhs.function && line_entry.file == rhs.line_entry.file && line_entry.line == rhs.line_entry.line; } bool operator != (const SourceInfo &rhs) const { return function != rhs.function || line_entry.file != rhs.line_entry.file || line_entry.line != rhs.line_entry.line; } bool operator < (const SourceInfo &rhs) const { if (function.GetCString() < rhs.function.GetCString()) return true; if (line_entry.file.GetDirectory().GetCString() < rhs.line_entry.file.GetDirectory().GetCString()) return true; if (line_entry.file.GetFilename().GetCString() < rhs.line_entry.file.GetFilename().GetCString()) return true; if (line_entry.line < rhs.line_entry.line) return true; return false; } }; size_t DisplayFunctionSource (const SymbolContext &sc, SourceInfo &source_info, CommandReturnObject &result) { if (!source_info.IsValid()) { source_info.function = sc.GetFunctionName(); source_info.line_entry = sc.GetFunctionStartLineEntry(); } if (sc.function) { Target *target = m_exe_ctx.GetTargetPtr(); FileSpec start_file; uint32_t start_line; uint32_t end_line; FileSpec end_file; if (sc.block == NULL) { // Not an inlined function sc.function->GetStartLineSourceInfo (start_file, start_line); if (start_line == 0) { result.AppendErrorWithFormat("Could not find line information for start of function: \"%s\".\n", source_info.function.GetCString()); result.SetStatus (eReturnStatusFailed); return 0; } sc.function->GetEndLineSourceInfo (end_file, end_line); } else { // We have an inlined function start_file = source_info.line_entry.file; start_line = source_info.line_entry.line; end_line = start_line + m_options.num_lines; } // This is a little hacky, but the first line table entry for a function points to the "{" that // starts the function block. It would be nice to actually get the function // declaration in there too. So back up a bit, but not further than what you're going to display. uint32_t extra_lines; if (m_options.num_lines >= 10) extra_lines = 5; else extra_lines = m_options.num_lines/2; uint32_t line_no; if (start_line <= extra_lines) line_no = 1; else line_no = start_line - extra_lines; // For fun, if the function is shorter than the number of lines we're supposed to display, // only display the function... if (end_line != 0) { if (m_options.num_lines > end_line - line_no) m_options.num_lines = end_line - line_no + extra_lines; } m_breakpoint_locations.Clear(); if (m_options.show_bp_locs) { const bool show_inlines = true; m_breakpoint_locations.Reset (start_file, 0, show_inlines); SearchFilterForUnconstrainedSearches target_search_filter (m_exe_ctx.GetTargetSP()); target_search_filter.Search (m_breakpoint_locations); } result.AppendMessageWithFormat("File: %s\n", start_file.GetPath().c_str()); return target->GetSourceManager().DisplaySourceLinesWithLineNumbers (start_file, line_no, 0, m_options.num_lines, "", &result.GetOutputStream(), GetBreakpointLocations ()); } else { result.AppendErrorWithFormat("Could not find function info for: \"%s\".\n", m_options.symbol_name.c_str()); } return 0; } // From Jim: The FindMatchingFunctions / FindMatchingFunctionSymbols functions // "take a possibly empty vector of strings which are names of modules, and // run the two search functions on the subset of the full module list that // matches the strings in the input vector". If we wanted to put these somewhere, // there should probably be a module-filter-list that can be passed to the // various ModuleList::Find* calls, which would either be a vector of string // names or a ModuleSpecList. size_t FindMatchingFunctions (Target *target, const ConstString &name, SymbolContextList& sc_list) { // Displaying the source for a symbol: bool include_inlines = true; bool append = true; bool include_symbols = false; size_t num_matches = 0; if (m_options.num_lines == 0) m_options.num_lines = 10; const size_t num_modules = m_options.modules.size(); if (num_modules > 0) { ModuleList matching_modules; for (size_t i = 0; i < num_modules; ++i) { FileSpec module_file_spec(m_options.modules[i].c_str(), false); if (module_file_spec) { ModuleSpec module_spec (module_file_spec); matching_modules.Clear(); target->GetImages().FindModules (module_spec, matching_modules); num_matches += matching_modules.FindFunctions (name, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); } } } else { num_matches = target->GetImages().FindFunctions (name, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); } return num_matches; } size_t FindMatchingFunctionSymbols (Target *target, const ConstString &name, SymbolContextList& sc_list) { size_t num_matches = 0; const size_t num_modules = m_options.modules.size(); if (num_modules > 0) { ModuleList matching_modules; for (size_t i = 0; i < num_modules; ++i) { FileSpec module_file_spec(m_options.modules[i].c_str(), false); if (module_file_spec) { ModuleSpec module_spec (module_file_spec); matching_modules.Clear(); target->GetImages().FindModules (module_spec, matching_modules); num_matches += matching_modules.FindFunctionSymbols (name, eFunctionNameTypeAuto, sc_list); } } } else { num_matches = target->GetImages().FindFunctionSymbols (name, eFunctionNameTypeAuto, sc_list); } return num_matches; } bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc != 0) { result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", GetCommandName()); result.SetStatus (eReturnStatusFailed); return false; } Target *target = m_exe_ctx.GetTargetPtr(); if (!m_options.symbol_name.empty()) { SymbolContextList sc_list; ConstString name(m_options.symbol_name.c_str()); // Displaying the source for a symbol. Search for function named name. size_t num_matches = FindMatchingFunctions (target, name, sc_list); if (!num_matches) { // If we didn't find any functions with that name, try searching for symbols // that line up exactly with function addresses. SymbolContextList sc_list_symbols; size_t num_symbol_matches = FindMatchingFunctionSymbols (target, name, sc_list_symbols); for (size_t i = 0; i < num_symbol_matches; i++) { SymbolContext sc; sc_list_symbols.GetContextAtIndex (i, sc); if (sc.symbol && sc.symbol->ValueIsAddress()) { const Address &base_address = sc.symbol->GetAddressRef(); Function *function = base_address.CalculateSymbolContextFunction(); if (function) { sc_list.Append (SymbolContext(function)); num_matches++; break; } } } } if (num_matches == 0) { result.AppendErrorWithFormat("Could not find function named: \"%s\".\n", m_options.symbol_name.c_str()); result.SetStatus (eReturnStatusFailed); return false; } if (num_matches > 1) { std::set source_match_set; bool displayed_something = false; for (size_t i = 0; i < num_matches; i++) { SymbolContext sc; sc_list.GetContextAtIndex (i, sc); SourceInfo source_info (sc.GetFunctionName(), sc.GetFunctionStartLineEntry()); if (source_info.IsValid()) { if (source_match_set.find(source_info) == source_match_set.end()) { source_match_set.insert(source_info); if (DisplayFunctionSource (sc, source_info, result)) displayed_something = true; } } } if (displayed_something) result.SetStatus (eReturnStatusSuccessFinishResult); else result.SetStatus (eReturnStatusFailed); } else { SymbolContext sc; sc_list.GetContextAtIndex (0, sc); SourceInfo source_info; if (DisplayFunctionSource (sc, source_info, result)) { result.SetStatus (eReturnStatusSuccessFinishResult); } else { result.SetStatus (eReturnStatusFailed); } } return result.Succeeded(); } else if (m_options.address != LLDB_INVALID_ADDRESS) { Address so_addr; StreamString error_strm; SymbolContextList sc_list; if (target->GetSectionLoadList().IsEmpty()) { // The target isn't loaded yet, we need to lookup the file address // in all modules const ModuleList &module_list = target->GetImages(); const size_t num_modules = module_list.GetSize(); for (size_t i=0; iResolveFileAddress(m_options.address, so_addr)) { SymbolContext sc; sc.Clear(true); if (module_sp->ResolveSymbolContextForAddress (so_addr, eSymbolContextEverything, sc) & eSymbolContextLineEntry) sc_list.Append(sc); } } if (sc_list.GetSize() == 0) { result.AppendErrorWithFormat("no modules have source information for file address 0x%" PRIx64 ".\n", m_options.address); result.SetStatus (eReturnStatusFailed); return false; } } else { // The target has some things loaded, resolve this address to a // compile unit + file + line and display if (target->GetSectionLoadList().ResolveLoadAddress (m_options.address, so_addr)) { ModuleSP module_sp (so_addr.GetModule()); if (module_sp) { SymbolContext sc; sc.Clear(true); if (module_sp->ResolveSymbolContextForAddress (so_addr, eSymbolContextEverything, sc) & eSymbolContextLineEntry) { sc_list.Append(sc); } else { so_addr.Dump(&error_strm, NULL, Address::DumpStyleModuleWithFileAddress); result.AppendErrorWithFormat("address resolves to %s, but there is no line table information available for this address.\n", error_strm.GetData()); result.SetStatus (eReturnStatusFailed); return false; } } } if (sc_list.GetSize() == 0) { result.AppendErrorWithFormat("no modules contain load address 0x%" PRIx64 ".\n", m_options.address); result.SetStatus (eReturnStatusFailed); return false; } } uint32_t num_matches = sc_list.GetSize(); for (uint32_t i=0; ishared_from_this()); target_search_filter.Search (m_breakpoint_locations); } bool show_fullpaths = true; bool show_module = true; bool show_inlined_frames = true; const bool show_function_arguments = true; const bool show_function_name = true; sc.DumpStopContext(&result.GetOutputStream(), m_exe_ctx.GetBestExecutionContextScope(), sc.line_entry.range.GetBaseAddress(), show_fullpaths, show_module, show_inlined_frames, show_function_arguments, show_function_name); result.GetOutputStream().EOL(); if (m_options.num_lines == 0) m_options.num_lines = 10; size_t lines_to_back_up = m_options.num_lines >= 10 ? 5 : m_options.num_lines/2; target->GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit, sc.line_entry.line, lines_to_back_up, m_options.num_lines - lines_to_back_up, "->", &result.GetOutputStream(), GetBreakpointLocations ()); result.SetStatus (eReturnStatusSuccessFinishResult); } } } else if (m_options.file_name.empty()) { // Last valid source manager context, or the current frame if no // valid last context in source manager. // One little trick here, if you type the exact same list command twice in a row, it is // more likely because you typed it once, then typed it again if (m_options.start_line == 0) { if (target->GetSourceManager().DisplayMoreWithLineNumbers (&result.GetOutputStream(), m_options.num_lines, m_options.reverse, GetBreakpointLocations ())) { result.SetStatus (eReturnStatusSuccessFinishResult); } } else { if (m_options.num_lines == 0) m_options.num_lines = 10; if (m_options.show_bp_locs) { SourceManager::FileSP last_file_sp (target->GetSourceManager().GetLastFile ()); if (last_file_sp) { const bool show_inlines = true; m_breakpoint_locations.Reset (last_file_sp->GetFileSpec(), 0, show_inlines); SearchFilterForUnconstrainedSearches target_search_filter (target->shared_from_this()); target_search_filter.Search (m_breakpoint_locations); } } else m_breakpoint_locations.Clear(); if (target->GetSourceManager().DisplaySourceLinesWithLineNumbersUsingLastFile( m_options.start_line, // Line to display m_options.num_lines, // Lines after line to UINT32_MAX, // Don't mark "line" "", // Don't mark "line" &result.GetOutputStream(), GetBreakpointLocations ())) { result.SetStatus (eReturnStatusSuccessFinishResult); } } } else { const char *filename = m_options.file_name.c_str(); bool check_inlines = false; SymbolContextList sc_list; size_t num_matches = 0; if (m_options.modules.size() > 0) { ModuleList matching_modules; for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) { FileSpec module_file_spec(m_options.modules[i].c_str(), false); if (module_file_spec) { ModuleSpec module_spec (module_file_spec); matching_modules.Clear(); target->GetImages().FindModules (module_spec, matching_modules); num_matches += matching_modules.ResolveSymbolContextForFilePath (filename, 0, check_inlines, eSymbolContextModule | eSymbolContextCompUnit, sc_list); } } } else { num_matches = target->GetImages().ResolveSymbolContextForFilePath (filename, 0, check_inlines, eSymbolContextModule | eSymbolContextCompUnit, sc_list); } if (num_matches == 0) { result.AppendErrorWithFormat("Could not find source file \"%s\".\n", m_options.file_name.c_str()); result.SetStatus (eReturnStatusFailed); return false; } if (num_matches > 1) { bool got_multiple = false; FileSpec *test_cu_spec = NULL; for (unsigned i = 0; i < num_matches; i++) { SymbolContext sc; sc_list.GetContextAtIndex(i, sc); if (sc.comp_unit) { if (test_cu_spec) { if (test_cu_spec != static_cast (sc.comp_unit)) got_multiple = true; break; } else test_cu_spec = sc.comp_unit; } } if (got_multiple) { result.AppendErrorWithFormat("Multiple source files found matching: \"%s.\"\n", m_options.file_name.c_str()); result.SetStatus (eReturnStatusFailed); return false; } } SymbolContext sc; if (sc_list.GetContextAtIndex(0, sc)) { if (sc.comp_unit) { if (m_options.show_bp_locs) { const bool show_inlines = true; m_breakpoint_locations.Reset (*sc.comp_unit, 0, show_inlines); SearchFilterForUnconstrainedSearches target_search_filter (target->shared_from_this()); target_search_filter.Search (m_breakpoint_locations); } else m_breakpoint_locations.Clear(); if (m_options.num_lines == 0) m_options.num_lines = 10; target->GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit, m_options.start_line, 0, m_options.num_lines, "", &result.GetOutputStream(), GetBreakpointLocations ()); result.SetStatus (eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n", m_options.file_name.c_str()); result.SetStatus (eReturnStatusFailed); return false; } } } return result.Succeeded(); } const SymbolContextList * GetBreakpointLocations () { if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0) return &m_breakpoint_locations.GetFileLineMatches(); return NULL; } CommandOptions m_options; FileLineResolver m_breakpoint_locations; std::string m_reverse_name; }; OptionDefinition CommandObjectSourceList::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "count", 'c', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeCount, "The number of source lines to display."}, { LLDB_OPT_SET_1 | LLDB_OPT_SET_2 , false, "shlib", 's', OptionParser::eRequiredArgument, NULL, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Look up the source file in the given shared library."}, { LLDB_OPT_SET_ALL, false, "show-breakpoints", 'b', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Show the line table locations from the debug information that indicate valid places to set source level breakpoints."}, { LLDB_OPT_SET_1 , false, "file", 'f', OptionParser::eRequiredArgument, NULL, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, { LLDB_OPT_SET_1 , false, "line", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, "The line number at which to start the display source."}, { LLDB_OPT_SET_2 , false, "name", 'n', OptionParser::eRequiredArgument, NULL, NULL, CommandCompletions::eSymbolCompletion, eArgTypeSymbol, "The name of a function whose source to display."}, { LLDB_OPT_SET_3 , false, "address",'a', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeAddressOrExpression, "Lookup the address and display the source information for the corresponding file and line."}, { LLDB_OPT_SET_4, false, "reverse", 'r', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Reverse the listing to look backwards from the last displayed block of source."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; #pragma mark CommandObjectMultiwordSource - //------------------------------------------------------------------------- // CommandObjectMultiwordSource //------------------------------------------------------------------------- CommandObjectMultiwordSource::CommandObjectMultiwordSource (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "source", "A set of commands for accessing source file information", "source []") { - // "source info" isn't implemented yet... - //LoadSubCommand ("info", CommandObjectSP (new CommandObjectSourceInfo (interpreter))); + LoadSubCommand ("info", CommandObjectSP (new CommandObjectSourceInfo (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectSourceList (interpreter))); } CommandObjectMultiwordSource::~CommandObjectMultiwordSource () { } Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp (revision 293283) @@ -1,5456 +1,5457 @@ //===-- CommandObjectTarget.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "CommandObjectTarget.h" // C Includes #include // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Interpreter/Args.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/IOHandler.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/Section.h" #include "lldb/Core/State.h" #include "lldb/Core/Timer.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/DataFormatters/ValueObjectPrinter.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/Symbols.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/OptionGroupArchitecture.h" #include "lldb/Interpreter/OptionGroupBoolean.h" #include "lldb/Interpreter/OptionGroupFile.h" #include "lldb/Interpreter/OptionGroupFormat.h" #include "lldb/Interpreter/OptionGroupVariable.h" #include "lldb/Interpreter/OptionGroupPlatform.h" #include "lldb/Interpreter/OptionGroupUInt64.h" #include "lldb/Interpreter/OptionGroupUUID.h" #include "lldb/Interpreter/OptionGroupString.h" #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/FuncUnwinders.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ABI.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadSpec.h" using namespace lldb; using namespace lldb_private; static void DumpTargetInfo (uint32_t target_idx, Target *target, const char *prefix_cstr, bool show_stopped_process_status, Stream &strm) { const ArchSpec &target_arch = target->GetArchitecture(); Module *exe_module = target->GetExecutableModulePointer(); char exe_path[PATH_MAX]; bool exe_valid = false; if (exe_module) exe_valid = exe_module->GetFileSpec().GetPath (exe_path, sizeof(exe_path)); if (!exe_valid) ::strcpy (exe_path, ""); strm.Printf ("%starget #%u: %s", prefix_cstr ? prefix_cstr : "", target_idx, exe_path); uint32_t properties = 0; if (target_arch.IsValid()) { strm.Printf ("%sarch=", properties++ > 0 ? ", " : " ( "); target_arch.DumpTriple (strm); properties++; } PlatformSP platform_sp (target->GetPlatform()); if (platform_sp) strm.Printf ("%splatform=%s", properties++ > 0 ? ", " : " ( ", platform_sp->GetName().GetCString()); ProcessSP process_sp (target->GetProcessSP()); bool show_process_status = false; if (process_sp) { lldb::pid_t pid = process_sp->GetID(); StateType state = process_sp->GetState(); if (show_stopped_process_status) show_process_status = StateIsStoppedState(state, true); const char *state_cstr = StateAsCString (state); if (pid != LLDB_INVALID_PROCESS_ID) strm.Printf ("%spid=%" PRIu64, properties++ > 0 ? ", " : " ( ", pid); strm.Printf ("%sstate=%s", properties++ > 0 ? ", " : " ( ", state_cstr); } if (properties > 0) strm.PutCString (" )\n"); else strm.EOL(); if (show_process_status) { const bool only_threads_with_stop_reason = true; const uint32_t start_frame = 0; const uint32_t num_frames = 1; const uint32_t num_frames_with_source = 1; process_sp->GetStatus (strm); process_sp->GetThreadStatus (strm, only_threads_with_stop_reason, start_frame, num_frames, num_frames_with_source); } } static uint32_t DumpTargetList (TargetList &target_list, bool show_stopped_process_status, Stream &strm) { const uint32_t num_targets = target_list.GetNumTargets(); if (num_targets) { TargetSP selected_target_sp (target_list.GetSelectedTarget()); strm.PutCString ("Current targets:\n"); for (uint32_t i=0; iGetPlatform(); if (remote_file) { if (platform_sp) { // I have a remote file.. two possible cases if (file_spec && file_spec.Exists()) { // if the remote file does not exist, push it there if (!platform_sp->GetFileExists (remote_file)) { Error err = platform_sp->PutFile(file_spec, remote_file); if (err.Fail()) { result.AppendError(err.AsCString()); result.SetStatus (eReturnStatusFailed); return false; } } } else { // there is no local file and we need one // in order to make the remote ---> local transfer we need a platform // TODO: if the user has passed in a --platform argument, use it to fetch the right platform if (!platform_sp) { result.AppendError("unable to perform remote debugging without a platform"); result.SetStatus (eReturnStatusFailed); return false; } if (file_path) { // copy the remote file to the local file Error err = platform_sp->GetFile(remote_file, file_spec); if (err.Fail()) { result.AppendError(err.AsCString()); result.SetStatus (eReturnStatusFailed); return false; } } else { // make up a local file result.AppendError("remote --> local transfer without local path is not implemented yet"); result.SetStatus (eReturnStatusFailed); return false; } } } else { result.AppendError("no platform found for target"); result.SetStatus (eReturnStatusFailed); return false; } } if (symfile || remote_file) { ModuleSP module_sp (target_sp->GetExecutableModule()); if (module_sp) { if (symfile) module_sp->SetSymbolFileFileSpec(symfile); if (remote_file) { std::string remote_path = remote_file.GetPath(); target_sp->SetArg0(remote_path.c_str()); module_sp->SetPlatformFileSpec(remote_file); } } } debugger.GetTargetList().SetSelectedTarget(target_sp.get()); if (must_set_platform_path) { ModuleSpec main_module_spec(file_spec); ModuleSP module_sp = target_sp->GetSharedModule(main_module_spec); if (module_sp) module_sp->SetPlatformFileSpec(remote_file); } if (core_file) { char core_path[PATH_MAX]; core_file.GetPath(core_path, sizeof(core_path)); if (core_file.Exists()) { if (!core_file.Readable()) { result.AppendMessageWithFormat ("Core file '%s' is not readable.\n", core_path); result.SetStatus (eReturnStatusFailed); return false; } FileSpec core_file_dir; core_file_dir.GetDirectory() = core_file.GetDirectory(); target_sp->GetExecutableSearchPaths ().Append (core_file_dir); ProcessSP process_sp (target_sp->CreateProcess (m_interpreter.GetDebugger().GetListener(), NULL, &core_file)); if (process_sp) { // Seems weird that we Launch a core file, but that is // what we do! error = process_sp->LoadCore(); if (error.Fail()) { result.AppendError(error.AsCString("can't find plug-in for core file")); result.SetStatus (eReturnStatusFailed); return false; } else { result.AppendMessageWithFormat ("Core file '%s' (%s) was loaded.\n", core_path, target_sp->GetArchitecture().GetArchitectureName()); result.SetStatus (eReturnStatusSuccessFinishNoResult); } } else { result.AppendErrorWithFormat ("Unable to find process plug-in for core file '%s'\n", core_path); result.SetStatus (eReturnStatusFailed); } } else { result.AppendErrorWithFormat ("Core file '%s' does not exist\n", core_path); result.SetStatus (eReturnStatusFailed); } } else { result.AppendMessageWithFormat ("Current executable set to '%s' (%s).\n", file_path, target_sp->GetArchitecture().GetArchitectureName()); result.SetStatus (eReturnStatusSuccessFinishNoResult); } } else { result.AppendError(error.AsCString()); result.SetStatus (eReturnStatusFailed); } } else { result.AppendErrorWithFormat("'%s' takes exactly one executable path argument, or use the --core option.\n", m_cmd_name.c_str()); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } private: OptionGroupOptions m_option_group; OptionGroupArchitecture m_arch_option; OptionGroupFile m_core_file; OptionGroupFile m_platform_path; OptionGroupFile m_symbol_file; OptionGroupFile m_remote_file; OptionGroupBoolean m_add_dependents; }; #pragma mark CommandObjectTargetList //---------------------------------------------------------------------- // "target list" //---------------------------------------------------------------------- class CommandObjectTargetList : public CommandObjectParsed { public: CommandObjectTargetList (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target list", "List all current targets in the current debug session.", NULL, 0) { } ~CommandObjectTargetList () override { } protected: bool DoExecute (Args& args, CommandReturnObject &result) override { if (args.GetArgumentCount() == 0) { Stream &strm = result.GetOutputStream(); bool show_stopped_process_status = false; if (DumpTargetList (m_interpreter.GetDebugger().GetTargetList(), show_stopped_process_status, strm) == 0) { strm.PutCString ("No targets.\n"); } result.SetStatus (eReturnStatusSuccessFinishResult); } else { result.AppendError ("the 'target list' command takes no arguments\n"); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetSelect //---------------------------------------------------------------------- // "target select" //---------------------------------------------------------------------- class CommandObjectTargetSelect : public CommandObjectParsed { public: CommandObjectTargetSelect (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target select", "Select a target as the current target by target index.", NULL, 0) { } ~CommandObjectTargetSelect () override { } protected: bool DoExecute (Args& args, CommandReturnObject &result) override { if (args.GetArgumentCount() == 1) { bool success = false; const char *target_idx_arg = args.GetArgumentAtIndex(0); uint32_t target_idx = StringConvert::ToUInt32 (target_idx_arg, UINT32_MAX, 0, &success); if (success) { TargetList &target_list = m_interpreter.GetDebugger().GetTargetList(); const uint32_t num_targets = target_list.GetNumTargets(); if (target_idx < num_targets) { TargetSP target_sp (target_list.GetTargetAtIndex (target_idx)); if (target_sp) { Stream &strm = result.GetOutputStream(); target_list.SetSelectedTarget (target_sp.get()); bool show_stopped_process_status = false; DumpTargetList (target_list, show_stopped_process_status, strm); result.SetStatus (eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat ("target #%u is NULL in target list\n", target_idx); result.SetStatus (eReturnStatusFailed); } } else { if (num_targets > 0) { result.AppendErrorWithFormat ("index %u is out of range, valid target indexes are 0 - %u\n", target_idx, num_targets - 1); } else { result.AppendErrorWithFormat ("index %u is out of range since there are no active targets\n", target_idx); } result.SetStatus (eReturnStatusFailed); } } else { result.AppendErrorWithFormat("invalid index string value '%s'\n", target_idx_arg); result.SetStatus (eReturnStatusFailed); } } else { result.AppendError ("'target select' takes a single argument: a target index\n"); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetSelect //---------------------------------------------------------------------- // "target delete" //---------------------------------------------------------------------- class CommandObjectTargetDelete : public CommandObjectParsed { public: CommandObjectTargetDelete (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target delete", "Delete one or more targets by target index.", NULL, 0), m_option_group(interpreter), m_all_option(LLDB_OPT_SET_1, false, "all", 'a', "Delete all targets.", false, true), m_cleanup_option( LLDB_OPT_SET_1, false, "clean", 'c', "Perform extra cleanup to minimize memory consumption after deleting the target. " "By default, LLDB will keep in memory any modules previously loaded by the target as well " "as all of its debug info. Specifying --clean will unload all of these shared modules and " "cause them to be reparsed again the next time the target is run", false, true) { m_option_group.Append(&m_all_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_cleanup_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectTargetDelete () override { } Options * GetOptions () override { return &m_option_group; } protected: bool DoExecute (Args& args, CommandReturnObject &result) override { const size_t argc = args.GetArgumentCount(); std::vector delete_target_list; TargetList &target_list = m_interpreter.GetDebugger().GetTargetList(); TargetSP target_sp; if (m_all_option.GetOptionValue()) { for (int i = 0; i < target_list.GetNumTargets(); ++i) delete_target_list.push_back(target_list.GetTargetAtIndex(i)); } else if (argc > 0) { const uint32_t num_targets = target_list.GetNumTargets(); // Bail out if don't have any targets. if (num_targets == 0) { result.AppendError("no targets to delete"); result.SetStatus(eReturnStatusFailed); return false; } for (uint32_t arg_idx = 0; arg_idx < argc; ++arg_idx) { const char *target_idx_arg = args.GetArgumentAtIndex(arg_idx); bool success = false; uint32_t target_idx = StringConvert::ToUInt32 (target_idx_arg, UINT32_MAX, 0, &success); if (!success) { result.AppendErrorWithFormat("invalid target index '%s'\n", target_idx_arg); result.SetStatus (eReturnStatusFailed); return false; } if (target_idx < num_targets) { target_sp = target_list.GetTargetAtIndex (target_idx); if (target_sp) { delete_target_list.push_back (target_sp); continue; } } if (num_targets > 1) result.AppendErrorWithFormat ("target index %u is out of range, valid target indexes are 0 - %u\n", target_idx, num_targets - 1); else result.AppendErrorWithFormat("target index %u is out of range, the only valid index is 0\n", target_idx); result.SetStatus (eReturnStatusFailed); return false; } } else { target_sp = target_list.GetSelectedTarget(); if (!target_sp) { result.AppendErrorWithFormat("no target is currently selected\n"); result.SetStatus (eReturnStatusFailed); return false; } delete_target_list.push_back (target_sp); } const size_t num_targets_to_delete = delete_target_list.size(); for (size_t idx = 0; idx < num_targets_to_delete; ++idx) { target_sp = delete_target_list[idx]; target_list.DeleteTarget(target_sp); target_sp->Destroy(); } // If "--clean" was specified, prune any orphaned shared modules from // the global shared module list if (m_cleanup_option.GetOptionValue ()) { const bool mandatory = true; ModuleList::RemoveOrphanSharedModules(mandatory); } result.GetOutputStream().Printf("%u targets deleted.\n", (uint32_t)num_targets_to_delete); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } OptionGroupOptions m_option_group; OptionGroupBoolean m_all_option; OptionGroupBoolean m_cleanup_option; }; #pragma mark CommandObjectTargetVariable //---------------------------------------------------------------------- // "target variable" //---------------------------------------------------------------------- class CommandObjectTargetVariable : public CommandObjectParsed { static const uint32_t SHORT_OPTION_FILE = 0x66696c65; // 'file' static const uint32_t SHORT_OPTION_SHLB = 0x73686c62; // 'shlb' public: CommandObjectTargetVariable (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target variable", "Read global variable(s) prior to, or while running your binary.", NULL, eCommandRequiresTarget), m_option_group (interpreter), m_option_variable (false), // Don't include frame options m_option_format (eFormatDefault), m_option_compile_units (LLDB_OPT_SET_1, false, "file", SHORT_OPTION_FILE, 0, eArgTypeFilename, "A basename or fullpath to a file that contains global variables. This option can be specified multiple times."), m_option_shared_libraries (LLDB_OPT_SET_1, false, "shlib", SHORT_OPTION_SHLB, 0, eArgTypeFilename, "A basename or fullpath to a shared library to use in the search for global variables. This option can be specified multiple times."), m_varobj_options() { CommandArgumentEntry arg; CommandArgumentData var_name_arg; // Define the first (and only) variant of this arg. var_name_arg.arg_type = eArgTypeVarName; var_name_arg.arg_repetition = eArgRepeatPlus; // There is only one variant this argument could be; put it into the argument entry. arg.push_back (var_name_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back (arg); m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_option_format, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_1); m_option_group.Append (&m_option_compile_units, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_option_shared_libraries, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectTargetVariable () override { } void DumpValueObject (Stream &s, VariableSP &var_sp, ValueObjectSP &valobj_sp, const char *root_name) { DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions()); if (false == valobj_sp->GetTargetSP()->GetDisplayRuntimeSupportValues() && true == valobj_sp->IsRuntimeSupportValue()) return; switch (var_sp->GetScope()) { case eValueTypeVariableGlobal: if (m_option_variable.show_scope) s.PutCString("GLOBAL: "); break; case eValueTypeVariableStatic: if (m_option_variable.show_scope) s.PutCString("STATIC: "); break; case eValueTypeVariableArgument: if (m_option_variable.show_scope) s.PutCString(" ARG: "); break; case eValueTypeVariableLocal: if (m_option_variable.show_scope) s.PutCString(" LOCAL: "); break; default: break; } if (m_option_variable.show_decl) { bool show_fullpaths = false; bool show_module = true; if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module)) s.PutCString (": "); } const Format format = m_option_format.GetFormat(); if (format != eFormatDefault) options.SetFormat(format); options.SetRootValueObjectName(root_name); valobj_sp->Dump(s,options); } static size_t GetVariableCallback (void *baton, const char *name, VariableList &variable_list) { Target *target = static_cast(baton); if (target) { return target->GetImages().FindGlobalVariables (ConstString(name), true, UINT32_MAX, variable_list); } return 0; } Options * GetOptions () override { return &m_option_group; } protected: void DumpGlobalVariableList(const ExecutionContext &exe_ctx, const SymbolContext &sc, const VariableList &variable_list, Stream &s) { size_t count = variable_list.GetSize(); if (count > 0) { if (sc.module_sp) { if (sc.comp_unit) { s.Printf ("Global variables for %s in %s:\n", sc.comp_unit->GetPath().c_str(), sc.module_sp->GetFileSpec().GetPath().c_str()); } else { s.Printf ("Global variables for %s\n", sc.module_sp->GetFileSpec().GetPath().c_str()); } } else if (sc.comp_unit) { s.Printf ("Global variables for %s\n", sc.comp_unit->GetPath().c_str()); } for (uint32_t i=0; iGetName().GetCString()); } } } } bool DoExecute (Args& args, CommandReturnObject &result) override { Target *target = m_exe_ctx.GetTargetPtr(); const size_t argc = args.GetArgumentCount(); Stream &s = result.GetOutputStream(); if (argc > 0) { for (size_t idx = 0; idx < argc; ++idx) { VariableList variable_list; ValueObjectList valobj_list; const char *arg = args.GetArgumentAtIndex(idx); size_t matches = 0; bool use_var_name = false; if (m_option_variable.use_regex) { RegularExpression regex(arg); if (!regex.IsValid ()) { result.GetErrorStream().Printf ("error: invalid regular expression: '%s'\n", arg); result.SetStatus (eReturnStatusFailed); return false; } use_var_name = true; matches = target->GetImages().FindGlobalVariables (regex, true, UINT32_MAX, variable_list); } else { Error error (Variable::GetValuesForVariableExpressionPath (arg, m_exe_ctx.GetBestExecutionContextScope(), GetVariableCallback, target, variable_list, valobj_list)); matches = variable_list.GetSize(); } if (matches == 0) { result.GetErrorStream().Printf ("error: can't find global variable '%s'\n", arg); result.SetStatus (eReturnStatusFailed); return false; } else { for (uint32_t global_idx=0; global_idxGetName().GetCString() : arg); } } } } } else { const FileSpecList &compile_units = m_option_compile_units.GetOptionValue().GetCurrentValue(); const FileSpecList &shlibs = m_option_shared_libraries.GetOptionValue().GetCurrentValue(); SymbolContextList sc_list; const size_t num_compile_units = compile_units.GetSize(); const size_t num_shlibs = shlibs.GetSize(); if (num_compile_units == 0 && num_shlibs == 0) { bool success = false; StackFrame *frame = m_exe_ctx.GetFramePtr(); CompileUnit *comp_unit = NULL; if (frame) { SymbolContext sc = frame->GetSymbolContext (eSymbolContextCompUnit); if (sc.comp_unit) { const bool can_create = true; VariableListSP comp_unit_varlist_sp (sc.comp_unit->GetVariableList(can_create)); if (comp_unit_varlist_sp) { size_t count = comp_unit_varlist_sp->GetSize(); if (count > 0) { DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp, s); success = true; } } } } if (!success) { if (frame) { if (comp_unit) result.AppendErrorWithFormat ("no global variables in current compile unit: %s\n", comp_unit->GetPath().c_str()); else result.AppendErrorWithFormat ("no debug information for frame %u\n", frame->GetFrameIndex()); } else result.AppendError ("'target variable' takes one or more global variable names as arguments\n"); result.SetStatus (eReturnStatusFailed); } } else { SymbolContextList sc_list; const bool append = true; // We have one or more compile unit or shlib if (num_shlibs > 0) { for (size_t shlib_idx=0; shlib_idxGetImages().FindFirstModule(module_spec)); if (module_sp) { if (num_compile_units > 0) { for (size_t cu_idx=0; cu_idxFindCompileUnits(compile_units.GetFileSpecAtIndex(cu_idx), append, sc_list); } else { SymbolContext sc; sc.module_sp = module_sp; sc_list.Append(sc); } } else { // Didn't find matching shlib/module in target... result.AppendErrorWithFormat ("target doesn't contain the specified shared library: %s\n", module_file.GetPath().c_str()); } } } else { // No shared libraries, we just want to find globals for the compile units files that were specified for (size_t cu_idx=0; cu_idxGetImages().FindCompileUnits(compile_units.GetFileSpecAtIndex(cu_idx), append, sc_list); } const uint32_t num_scs = sc_list.GetSize(); if (num_scs > 0) { SymbolContext sc; for (uint32_t sc_idx=0; sc_idxGetVariableList(can_create)); if (comp_unit_varlist_sp) DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp, s); } else if (sc.module_sp) { // Get all global variables for this module lldb_private::RegularExpression all_globals_regex("."); // Any global with at least one character VariableList variable_list; sc.module_sp->FindGlobalVariables(all_globals_regex, append, UINT32_MAX, variable_list); DumpGlobalVariableList(m_exe_ctx, sc, variable_list, s); } } } } } } if (m_interpreter.TruncationWarningNecessary()) { result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(), m_cmd_name.c_str()); m_interpreter.TruncationWarningGiven(); } return result.Succeeded(); } OptionGroupOptions m_option_group; OptionGroupVariable m_option_variable; OptionGroupFormat m_option_format; OptionGroupFileList m_option_compile_units; OptionGroupFileList m_option_shared_libraries; OptionGroupValueObjectDisplay m_varobj_options; }; #pragma mark CommandObjectTargetModulesSearchPathsAdd class CommandObjectTargetModulesSearchPathsAdd : public CommandObjectParsed { public: CommandObjectTargetModulesSearchPathsAdd (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target modules search-paths add", "Add new image search paths substitution pairs to the current target.", NULL) { CommandArgumentEntry arg; CommandArgumentData old_prefix_arg; CommandArgumentData new_prefix_arg; // Define the first variant of this arg pair. old_prefix_arg.arg_type = eArgTypeOldPathPrefix; old_prefix_arg.arg_repetition = eArgRepeatPairPlus; // Define the first variant of this arg pair. new_prefix_arg.arg_type = eArgTypeNewPathPrefix; new_prefix_arg.arg_repetition = eArgRepeatPairPlus; // There are two required arguments that must always occur together, i.e. an argument "pair". Because they // must always occur together, they are treated as two variants of one argument rather than two independent // arguments. Push them both into the first argument position for m_arguments... arg.push_back (old_prefix_arg); arg.push_back (new_prefix_arg); m_arguments.push_back (arg); } ~CommandObjectTargetModulesSearchPathsAdd () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) { const size_t argc = command.GetArgumentCount(); if (argc & 1) { result.AppendError ("add requires an even number of arguments\n"); result.SetStatus (eReturnStatusFailed); } else { for (size_t i=0; iPrintf ("target modules search path adding ImageSearchPath pair: '%s' -> '%s'", from, to); } bool last_pair = ((argc - i) == 2); target->GetImageSearchPathList().Append (ConstString(from), ConstString(to), last_pair); // Notify if this is the last pair result.SetStatus (eReturnStatusSuccessFinishNoResult); } else { if (from[0]) result.AppendError (" can't be empty\n"); else result.AppendError (" can't be empty\n"); result.SetStatus (eReturnStatusFailed); } } } } else { result.AppendError ("invalid target\n"); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesSearchPathsClear class CommandObjectTargetModulesSearchPathsClear : public CommandObjectParsed { public: CommandObjectTargetModulesSearchPathsClear (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target modules search-paths clear", "Clear all current image search path substitution pairs from the current target.", "target modules search-paths clear") { } ~CommandObjectTargetModulesSearchPathsClear () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) { bool notify = true; target->GetImageSearchPathList().Clear(notify); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else { result.AppendError ("invalid target\n"); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesSearchPathsInsert class CommandObjectTargetModulesSearchPathsInsert : public CommandObjectParsed { public: CommandObjectTargetModulesSearchPathsInsert (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target modules search-paths insert", "Insert a new image search path substitution pair into the current target at the specified index.", NULL) { CommandArgumentEntry arg1; CommandArgumentEntry arg2; CommandArgumentData index_arg; CommandArgumentData old_prefix_arg; CommandArgumentData new_prefix_arg; // Define the first and only variant of this arg. index_arg.arg_type = eArgTypeIndex; index_arg.arg_repetition = eArgRepeatPlain; // Put the one and only variant into the first arg for m_arguments: arg1.push_back (index_arg); // Define the first variant of this arg pair. old_prefix_arg.arg_type = eArgTypeOldPathPrefix; old_prefix_arg.arg_repetition = eArgRepeatPairPlus; // Define the first variant of this arg pair. new_prefix_arg.arg_type = eArgTypeNewPathPrefix; new_prefix_arg.arg_repetition = eArgRepeatPairPlus; // There are two required arguments that must always occur together, i.e. an argument "pair". Because they // must always occur together, they are treated as two variants of one argument rather than two independent // arguments. Push them both into the same argument position for m_arguments... arg2.push_back (old_prefix_arg); arg2.push_back (new_prefix_arg); // Add arguments to m_arguments. m_arguments.push_back (arg1); m_arguments.push_back (arg2); } ~CommandObjectTargetModulesSearchPathsInsert () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) { size_t argc = command.GetArgumentCount(); // check for at least 3 arguments and an odd number of parameters if (argc >= 3 && argc & 1) { bool success = false; uint32_t insert_idx = StringConvert::ToUInt32(command.GetArgumentAtIndex(0), UINT32_MAX, 0, &success); if (!success) { result.AppendErrorWithFormat(" parameter is not an integer: '%s'.\n", command.GetArgumentAtIndex(0)); result.SetStatus (eReturnStatusFailed); return result.Succeeded(); } // shift off the index command.Shift(); argc = command.GetArgumentCount(); for (uint32_t i=0; iGetImageSearchPathList().Insert (ConstString(from), ConstString(to), insert_idx, last_pair); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else { if (from[0]) result.AppendError (" can't be empty\n"); else result.AppendError (" can't be empty\n"); result.SetStatus (eReturnStatusFailed); return false; } } } else { result.AppendError ("insert requires at least three arguments\n"); result.SetStatus (eReturnStatusFailed); return result.Succeeded(); } } else { result.AppendError ("invalid target\n"); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesSearchPathsList class CommandObjectTargetModulesSearchPathsList : public CommandObjectParsed { public: CommandObjectTargetModulesSearchPathsList (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target modules search-paths list", "List all current image search path substitution pairs in the current target.", "target modules search-paths list") { } ~CommandObjectTargetModulesSearchPathsList () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) { if (command.GetArgumentCount() != 0) { result.AppendError ("list takes no arguments\n"); result.SetStatus (eReturnStatusFailed); return result.Succeeded(); } target->GetImageSearchPathList().Dump(&result.GetOutputStream()); result.SetStatus (eReturnStatusSuccessFinishResult); } else { result.AppendError ("invalid target\n"); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesSearchPathsQuery class CommandObjectTargetModulesSearchPathsQuery : public CommandObjectParsed { public: CommandObjectTargetModulesSearchPathsQuery (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target modules search-paths query", "Transform a path using the first applicable image search path.", NULL) { CommandArgumentEntry arg; CommandArgumentData path_arg; // Define the first (and only) variant of this arg. path_arg.arg_type = eArgTypeDirectoryName; path_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the argument entry. arg.push_back (path_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back (arg); } ~CommandObjectTargetModulesSearchPathsQuery () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) { if (command.GetArgumentCount() != 1) { result.AppendError ("query requires one argument\n"); result.SetStatus (eReturnStatusFailed); return result.Succeeded(); } ConstString orig(command.GetArgumentAtIndex(0)); ConstString transformed; if (target->GetImageSearchPathList().RemapPath(orig, transformed)) result.GetOutputStream().Printf("%s\n", transformed.GetCString()); else result.GetOutputStream().Printf("%s\n", orig.GetCString()); result.SetStatus (eReturnStatusSuccessFinishResult); } else { result.AppendError ("invalid target\n"); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } }; //---------------------------------------------------------------------- // Static Helper functions //---------------------------------------------------------------------- static void DumpModuleArchitecture (Stream &strm, Module *module, bool full_triple, uint32_t width) { if (module) { StreamString arch_strm; if (full_triple) module->GetArchitecture().DumpTriple(arch_strm); else arch_strm.PutCString(module->GetArchitecture().GetArchitectureName()); std::string arch_str = arch_strm.GetString(); if (width) strm.Printf("%-*s", width, arch_str.c_str()); else strm.PutCString(arch_str.c_str()); } } static void DumpModuleUUID (Stream &strm, Module *module) { if (module && module->GetUUID().IsValid()) module->GetUUID().Dump (&strm); else strm.PutCString(" "); } static uint32_t DumpCompileUnitLineTable (CommandInterpreter &interpreter, Stream &strm, Module *module, const FileSpec &file_spec, bool load_addresses) { uint32_t num_matches = 0; if (module) { SymbolContextList sc_list; num_matches = module->ResolveSymbolContextsForFileSpec (file_spec, 0, false, eSymbolContextCompUnit, sc_list); for (uint32_t i=0; i 0) strm << "\n\n"; strm << "Line table for " << *static_cast (sc.comp_unit) << " in `" << module->GetFileSpec().GetFilename() << "\n"; LineTable *line_table = sc.comp_unit->GetLineTable(); if (line_table) line_table->GetDescription (&strm, interpreter.GetExecutionContext().GetTargetPtr(), lldb::eDescriptionLevelBrief); else strm << "No line table"; } } } return num_matches; } static void DumpFullpath (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width) { if (file_spec_ptr) { if (width > 0) { std::string fullpath = file_spec_ptr->GetPath(); strm.Printf("%-*s", width, fullpath.c_str()); return; } else { file_spec_ptr->Dump(&strm); return; } } // Keep the width spacing correct if things go wrong... if (width > 0) strm.Printf("%-*s", width, ""); } static void DumpDirectory (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width) { if (file_spec_ptr) { if (width > 0) strm.Printf("%-*s", width, file_spec_ptr->GetDirectory().AsCString("")); else file_spec_ptr->GetDirectory().Dump(&strm); return; } // Keep the width spacing correct if things go wrong... if (width > 0) strm.Printf("%-*s", width, ""); } static void DumpBasename (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width) { if (file_spec_ptr) { if (width > 0) strm.Printf("%-*s", width, file_spec_ptr->GetFilename().AsCString("")); else file_spec_ptr->GetFilename().Dump(&strm); return; } // Keep the width spacing correct if things go wrong... if (width > 0) strm.Printf("%-*s", width, ""); } static void DumpModuleSymtab (CommandInterpreter &interpreter, Stream &strm, Module *module, SortOrder sort_order) { if (module) { SymbolVendor *sym_vendor = module->GetSymbolVendor (); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) symtab->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(), sort_order); } } } static void DumpModuleSections (CommandInterpreter &interpreter, Stream &strm, Module *module) { if (module) { SectionList *section_list = module->GetSectionList(); if (section_list) { strm.Printf ("Sections for '%s' (%s):\n", module->GetSpecificationDescription().c_str(), module->GetArchitecture().GetArchitectureName()); strm.IndentMore(); section_list->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(), true, UINT32_MAX); strm.IndentLess(); } } } static bool DumpModuleSymbolVendor (Stream &strm, Module *module) { if (module) { SymbolVendor *symbol_vendor = module->GetSymbolVendor(true); if (symbol_vendor) { symbol_vendor->Dump(&strm); return true; } } return false; } static void DumpAddress (ExecutionContextScope *exe_scope, const Address &so_addr, bool verbose, Stream &strm) { strm.IndentMore(); strm.Indent (" Address: "); so_addr.Dump (&strm, exe_scope, Address::DumpStyleModuleWithFileAddress); strm.PutCString (" ("); so_addr.Dump (&strm, exe_scope, Address::DumpStyleSectionNameOffset); strm.PutCString (")\n"); strm.Indent (" Summary: "); const uint32_t save_indent = strm.GetIndentLevel (); strm.SetIndentLevel (save_indent + 13); so_addr.Dump (&strm, exe_scope, Address::DumpStyleResolvedDescription); strm.SetIndentLevel (save_indent); // Print out detailed address information when verbose is enabled if (verbose) { strm.EOL(); so_addr.Dump (&strm, exe_scope, Address::DumpStyleDetailedSymbolContext); } strm.IndentLess(); } static bool LookupAddressInModule (CommandInterpreter &interpreter, Stream &strm, Module *module, uint32_t resolve_mask, lldb::addr_t raw_addr, lldb::addr_t offset, bool verbose) { if (module) { lldb::addr_t addr = raw_addr - offset; Address so_addr; SymbolContext sc; Target *target = interpreter.GetExecutionContext().GetTargetPtr(); if (target && !target->GetSectionLoadList().IsEmpty()) { if (!target->GetSectionLoadList().ResolveLoadAddress (addr, so_addr)) return false; else if (so_addr.GetModule().get() != module) return false; } else { if (!module->ResolveFileAddress (addr, so_addr)) return false; } ExecutionContextScope *exe_scope = interpreter.GetExecutionContext().GetBestExecutionContextScope(); DumpAddress (exe_scope, so_addr, verbose, strm); // strm.IndentMore(); // strm.Indent (" Address: "); // so_addr.Dump (&strm, exe_scope, Address::DumpStyleModuleWithFileAddress); // strm.PutCString (" ("); // so_addr.Dump (&strm, exe_scope, Address::DumpStyleSectionNameOffset); // strm.PutCString (")\n"); // strm.Indent (" Summary: "); // const uint32_t save_indent = strm.GetIndentLevel (); // strm.SetIndentLevel (save_indent + 13); // so_addr.Dump (&strm, exe_scope, Address::DumpStyleResolvedDescription); // strm.SetIndentLevel (save_indent); // // Print out detailed address information when verbose is enabled // if (verbose) // { // strm.EOL(); // so_addr.Dump (&strm, exe_scope, Address::DumpStyleDetailedSymbolContext); // } // strm.IndentLess(); return true; } return false; } static uint32_t LookupSymbolInModule (CommandInterpreter &interpreter, Stream &strm, Module *module, const char *name, bool name_is_regex, bool verbose) { if (module) { SymbolContext sc; SymbolVendor *sym_vendor = module->GetSymbolVendor (); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) { uint32_t i; std::vector match_indexes; ConstString symbol_name (name); uint32_t num_matches = 0; if (name_is_regex) { RegularExpression name_regexp(name); num_matches = symtab->AppendSymbolIndexesMatchingRegExAndType (name_regexp, eSymbolTypeAny, match_indexes); } else { num_matches = symtab->AppendSymbolIndexesWithName (symbol_name, match_indexes); } if (num_matches > 0) { strm.Indent (); strm.Printf("%u symbols match %s'%s' in ", num_matches, name_is_regex ? "the regular expression " : "", name); DumpFullpath (strm, &module->GetFileSpec(), 0); strm.PutCString(":\n"); strm.IndentMore (); for (i=0; i < num_matches; ++i) { Symbol *symbol = symtab->SymbolAtIndex(match_indexes[i]); if (symbol && symbol->ValueIsAddress()) { DumpAddress (interpreter.GetExecutionContext().GetBestExecutionContextScope(), symbol->GetAddressRef(), verbose, strm); } } strm.IndentLess (); return num_matches; } } } } return 0; } static void DumpSymbolContextList (ExecutionContextScope *exe_scope, Stream &strm, SymbolContextList &sc_list, bool verbose) { strm.IndentMore (); uint32_t i; const uint32_t num_matches = sc_list.GetSize(); for (i=0; iFindFunctions (function_name_regex, include_symbols, include_inlines, append, sc_list); } else { ConstString function_name (name); num_matches = module->FindFunctions (function_name, NULL, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); } if (num_matches) { strm.Indent (); strm.Printf("%" PRIu64 " match%s found in ", (uint64_t)num_matches, num_matches > 1 ? "es" : ""); DumpFullpath (strm, &module->GetFileSpec(), 0); strm.PutCString(":\n"); DumpSymbolContextList (interpreter.GetExecutionContext().GetBestExecutionContextScope(), strm, sc_list, verbose); } return num_matches; } return 0; } static size_t LookupTypeInModule (CommandInterpreter &interpreter, Stream &strm, Module *module, const char *name_cstr, bool name_is_regex) { if (module && name_cstr && name_cstr[0]) { TypeList type_list; const uint32_t max_num_matches = UINT32_MAX; size_t num_matches = 0; bool name_is_fully_qualified = false; SymbolContext sc; ConstString name(name_cstr); num_matches = module->FindTypes(sc, name, name_is_fully_qualified, max_num_matches, type_list); if (num_matches) { strm.Indent (); strm.Printf("%" PRIu64 " match%s found in ", (uint64_t)num_matches, num_matches > 1 ? "es" : ""); DumpFullpath (strm, &module->GetFileSpec(), 0); strm.PutCString(":\n"); for (TypeSP type_sp : type_list.Types()) { if (type_sp) { // Resolve the clang type so that any forward references // to types that haven't yet been parsed will get parsed. type_sp->GetFullCompilerType (); type_sp->GetDescription (&strm, eDescriptionLevelFull, true); // Print all typedef chains TypeSP typedef_type_sp (type_sp); TypeSP typedefed_type_sp (typedef_type_sp->GetTypedefType()); while (typedefed_type_sp) { strm.EOL(); strm.Printf(" typedef '%s': ", typedef_type_sp->GetName().GetCString()); typedefed_type_sp->GetFullCompilerType (); typedefed_type_sp->GetDescription (&strm, eDescriptionLevelFull, true); typedef_type_sp = typedefed_type_sp; typedefed_type_sp = typedef_type_sp->GetTypedefType(); } } strm.EOL(); } } return num_matches; } return 0; } static size_t LookupTypeHere (CommandInterpreter &interpreter, Stream &strm, const SymbolContext &sym_ctx, const char *name_cstr, bool name_is_regex) { if (!sym_ctx.module_sp) return 0; TypeList type_list; const uint32_t max_num_matches = UINT32_MAX; size_t num_matches = 1; bool name_is_fully_qualified = false; ConstString name(name_cstr); num_matches = sym_ctx.module_sp->FindTypes(sym_ctx, name, name_is_fully_qualified, max_num_matches, type_list); if (num_matches) { strm.Indent (); strm.PutCString("Best match found in "); DumpFullpath (strm, &sym_ctx.module_sp->GetFileSpec(), 0); strm.PutCString(":\n"); TypeSP type_sp (type_list.GetTypeAtIndex(0)); if (type_sp) { // Resolve the clang type so that any forward references // to types that haven't yet been parsed will get parsed. type_sp->GetFullCompilerType (); type_sp->GetDescription (&strm, eDescriptionLevelFull, true); // Print all typedef chains TypeSP typedef_type_sp (type_sp); TypeSP typedefed_type_sp (typedef_type_sp->GetTypedefType()); while (typedefed_type_sp) { strm.EOL(); strm.Printf(" typedef '%s': ", typedef_type_sp->GetName().GetCString()); typedefed_type_sp->GetFullCompilerType (); typedefed_type_sp->GetDescription (&strm, eDescriptionLevelFull, true); typedef_type_sp = typedefed_type_sp; typedefed_type_sp = typedef_type_sp->GetTypedefType(); } } strm.EOL(); } return num_matches; } static uint32_t LookupFileAndLineInModule (CommandInterpreter &interpreter, Stream &strm, Module *module, const FileSpec &file_spec, uint32_t line, bool check_inlines, bool verbose) { if (module && file_spec) { SymbolContextList sc_list; const uint32_t num_matches = module->ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines, eSymbolContextEverything, sc_list); if (num_matches > 0) { strm.Indent (); strm.Printf("%u match%s found in ", num_matches, num_matches > 1 ? "es" : ""); strm << file_spec; if (line > 0) strm.Printf (":%u", line); strm << " in "; DumpFullpath (strm, &module->GetFileSpec(), 0); strm.PutCString(":\n"); DumpSymbolContextList (interpreter.GetExecutionContext().GetBestExecutionContextScope(), strm, sc_list, verbose); return num_matches; } } return 0; } static size_t FindModulesByName (Target *target, const char *module_name, ModuleList &module_list, bool check_global_list) { // Dump specified images (by basename or fullpath) FileSpec module_file_spec(module_name, false); ModuleSpec module_spec (module_file_spec); const size_t initial_size = module_list.GetSize (); if (check_global_list) { // Check the global list Mutex::Locker locker(Module::GetAllocationModuleCollectionMutex()); const size_t num_modules = Module::GetNumberAllocatedModules(); ModuleSP module_sp; for (size_t image_idx = 0; image_idxMatchesModuleSpec (module_spec)) { module_sp = module->shared_from_this(); module_list.AppendIfNeeded(module_sp); } } } } else { if (target) { const size_t num_matches = target->GetImages().FindModules (module_spec, module_list); // Not found in our module list for our target, check the main // shared module list in case it is a extra file used somewhere // else if (num_matches == 0) { module_spec.GetArchitecture() = target->GetArchitecture(); ModuleList::FindSharedModules (module_spec, module_list); } } else { ModuleList::FindSharedModules (module_spec,module_list); } } return module_list.GetSize () - initial_size; } #pragma mark CommandObjectTargetModulesModuleAutoComplete //---------------------------------------------------------------------- // A base command object class that can auto complete with module file // paths //---------------------------------------------------------------------- class CommandObjectTargetModulesModuleAutoComplete : public CommandObjectParsed { public: CommandObjectTargetModulesModuleAutoComplete (CommandInterpreter &interpreter, const char *name, const char *help, const char *syntax) : CommandObjectParsed (interpreter, name, help, syntax) { CommandArgumentEntry arg; CommandArgumentData file_arg; // Define the first (and only) variant of this arg. file_arg.arg_type = eArgTypeFilename; file_arg.arg_repetition = eArgRepeatStar; // There is only one variant this argument could be; put it into the argument entry. arg.push_back (file_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back (arg); } ~CommandObjectTargetModulesModuleAutoComplete () override { } int HandleArgumentCompletion (Args &input, int &cursor_index, int &cursor_char_position, OptionElementVector &opt_element_vector, int match_start_point, int max_return_elements, bool &word_complete, StringList &matches) override { // Arguments are the standard module completer. std::string completion_str (input.GetArgumentAtIndex(cursor_index)); completion_str.erase (cursor_char_position); CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, CommandCompletions::eModuleCompletion, completion_str.c_str(), match_start_point, max_return_elements, NULL, word_complete, matches); return matches.GetSize(); } }; #pragma mark CommandObjectTargetModulesSourceFileAutoComplete //---------------------------------------------------------------------- // A base command object class that can auto complete with module source // file paths //---------------------------------------------------------------------- class CommandObjectTargetModulesSourceFileAutoComplete : public CommandObjectParsed { public: CommandObjectTargetModulesSourceFileAutoComplete (CommandInterpreter &interpreter, const char *name, const char *help, const char *syntax, uint32_t flags) : CommandObjectParsed (interpreter, name, help, syntax, flags) { CommandArgumentEntry arg; CommandArgumentData source_file_arg; // Define the first (and only) variant of this arg. source_file_arg.arg_type = eArgTypeSourceFile; source_file_arg.arg_repetition = eArgRepeatPlus; // There is only one variant this argument could be; put it into the argument entry. arg.push_back (source_file_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back (arg); } ~CommandObjectTargetModulesSourceFileAutoComplete () override { } int HandleArgumentCompletion (Args &input, int &cursor_index, int &cursor_char_position, OptionElementVector &opt_element_vector, int match_start_point, int max_return_elements, bool &word_complete, StringList &matches) override { // Arguments are the standard source file completer. std::string completion_str (input.GetArgumentAtIndex(cursor_index)); completion_str.erase (cursor_char_position); CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, CommandCompletions::eSourceFileCompletion, completion_str.c_str(), match_start_point, max_return_elements, NULL, word_complete, matches); return matches.GetSize(); } }; #pragma mark CommandObjectTargetModulesDumpSymtab class CommandObjectTargetModulesDumpSymtab : public CommandObjectTargetModulesModuleAutoComplete { public: CommandObjectTargetModulesDumpSymtab (CommandInterpreter &interpreter) : CommandObjectTargetModulesModuleAutoComplete (interpreter, "target modules dump symtab", "Dump the symbol table from one or more target modules.", NULL), m_options (interpreter) { } ~CommandObjectTargetModulesDumpSymtab () override { } Options * GetOptions () override { return &m_options; } class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options(interpreter), m_sort_order (eSortOrderNone) { } ~CommandOptions () override { } Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 's': m_sort_order = (SortOrder) Args::StringToOptionEnum (option_arg, g_option_table[option_idx].enum_values, eSortOrderNone, error); break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting () override { m_sort_order = eSortOrderNone; } const OptionDefinition* GetDefinitions () override { return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; SortOrder m_sort_order; }; protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == NULL) { result.AppendError ("invalid target, create a debug target using the 'target create' command"); result.SetStatus (eReturnStatusFailed); return false; } else { uint32_t num_dumped = 0; uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); if (command.GetArgumentCount() == 0) { // Dump all sections for all modules images Mutex::Locker modules_locker(target->GetImages().GetMutex()); const size_t num_modules = target->GetImages().GetSize(); if (num_modules > 0) { result.GetOutputStream().Printf("Dumping symbol table for %" PRIu64 " modules.\n", (uint64_t)num_modules); for (size_t image_idx = 0; image_idx 0) { result.GetOutputStream().EOL(); result.GetOutputStream().EOL(); } num_dumped++; DumpModuleSymtab (m_interpreter, result.GetOutputStream(), target->GetImages().GetModulePointerAtIndexUnlocked(image_idx), m_options.m_sort_order); } } else { result.AppendError ("the target has no associated executable images"); result.SetStatus (eReturnStatusFailed); return false; } } else { // Dump specified images (by basename or fullpath) const char *arg_cstr; for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx) { ModuleList module_list; const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, true); if (num_matches > 0) { for (size_t i=0; i 0) { result.GetOutputStream().EOL(); result.GetOutputStream().EOL(); } num_dumped++; DumpModuleSymtab (m_interpreter, result.GetOutputStream(), module, m_options.m_sort_order); } } } else result.AppendWarningWithFormat("Unable to find an image that matches '%s'.\n", arg_cstr); } } if (num_dumped > 0) result.SetStatus (eReturnStatusSuccessFinishResult); else { result.AppendError ("no matching executable images found"); result.SetStatus (eReturnStatusFailed); } } return result.Succeeded(); } CommandOptions m_options; }; static OptionEnumValueElement g_sort_option_enumeration[4] = { { eSortOrderNone, "none", "No sorting, use the original symbol table order."}, { eSortOrderByAddress, "address", "Sort output by symbol address."}, { eSortOrderByName, "name", "Sort output by symbol name."}, { 0, NULL, NULL } }; OptionDefinition CommandObjectTargetModulesDumpSymtab::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_1, false, "sort", 's', OptionParser::eRequiredArgument, NULL, g_sort_option_enumeration, 0, eArgTypeSortOrder, "Supply a sort order when dumping the symbol table."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; #pragma mark CommandObjectTargetModulesDumpSections //---------------------------------------------------------------------- // Image section dumping command //---------------------------------------------------------------------- class CommandObjectTargetModulesDumpSections : public CommandObjectTargetModulesModuleAutoComplete { public: CommandObjectTargetModulesDumpSections (CommandInterpreter &interpreter) : CommandObjectTargetModulesModuleAutoComplete (interpreter, "target modules dump sections", "Dump the sections from one or more target modules.", //"target modules dump sections [ ...]") NULL) { } ~CommandObjectTargetModulesDumpSections () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == NULL) { result.AppendError ("invalid target, create a debug target using the 'target create' command"); result.SetStatus (eReturnStatusFailed); return false; } else { uint32_t num_dumped = 0; uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); if (command.GetArgumentCount() == 0) { // Dump all sections for all modules images const size_t num_modules = target->GetImages().GetSize(); if (num_modules > 0) { result.GetOutputStream().Printf("Dumping sections for %" PRIu64 " modules.\n", (uint64_t)num_modules); for (size_t image_idx = 0; image_idxGetImages().GetModulePointerAtIndex(image_idx)); } } else { result.AppendError ("the target has no associated executable images"); result.SetStatus (eReturnStatusFailed); return false; } } else { // Dump specified images (by basename or fullpath) const char *arg_cstr; for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx) { ModuleList module_list; const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, true); if (num_matches > 0) { for (size_t i=0; i 0) result.SetStatus (eReturnStatusSuccessFinishResult); else { result.AppendError ("no matching executable images found"); result.SetStatus (eReturnStatusFailed); } } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesDumpSymfile //---------------------------------------------------------------------- // Image debug symbol dumping command //---------------------------------------------------------------------- class CommandObjectTargetModulesDumpSymfile : public CommandObjectTargetModulesModuleAutoComplete { public: CommandObjectTargetModulesDumpSymfile (CommandInterpreter &interpreter) : CommandObjectTargetModulesModuleAutoComplete (interpreter, "target modules dump symfile", "Dump the debug symbol file for one or more target modules.", //"target modules dump symfile [ ...]") NULL) { } ~CommandObjectTargetModulesDumpSymfile () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == NULL) { result.AppendError ("invalid target, create a debug target using the 'target create' command"); result.SetStatus (eReturnStatusFailed); return false; } else { uint32_t num_dumped = 0; uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); if (command.GetArgumentCount() == 0) { // Dump all sections for all modules images const ModuleList &target_modules = target->GetImages(); Mutex::Locker modules_locker (target_modules.GetMutex()); const size_t num_modules = target_modules.GetSize(); if (num_modules > 0) { result.GetOutputStream().Printf("Dumping debug symbols for %" PRIu64 " modules.\n", (uint64_t)num_modules); for (uint32_t image_idx = 0; image_idx 0) { for (size_t i=0; i 0) result.SetStatus (eReturnStatusSuccessFinishResult); else { result.AppendError ("no matching executable images found"); result.SetStatus (eReturnStatusFailed); } } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesDumpLineTable //---------------------------------------------------------------------- // Image debug line table dumping command //---------------------------------------------------------------------- class CommandObjectTargetModulesDumpLineTable : public CommandObjectTargetModulesSourceFileAutoComplete { public: CommandObjectTargetModulesDumpLineTable (CommandInterpreter &interpreter) : CommandObjectTargetModulesSourceFileAutoComplete (interpreter, "target modules dump line-table", "Dump the line table for one or more compilation units.", NULL, eCommandRequiresTarget) { } ~CommandObjectTargetModulesDumpLineTable () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = m_exe_ctx.GetTargetPtr(); uint32_t total_num_dumped = 0; uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); if (command.GetArgumentCount() == 0) { - result.AppendErrorWithFormat ("\nSyntax: %s\n", m_cmd_syntax.c_str()); + result.AppendError ("file option must be specified."); result.SetStatus (eReturnStatusFailed); + return result.Succeeded(); } else { // Dump specified images (by basename or fullpath) const char *arg_cstr; for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx) { FileSpec file_spec(arg_cstr, false); const ModuleList &target_modules = target->GetImages(); Mutex::Locker modules_locker(target_modules.GetMutex()); const size_t num_modules = target_modules.GetSize(); if (num_modules > 0) { uint32_t num_dumped = 0; for (uint32_t i = 0; i 0) result.SetStatus (eReturnStatusSuccessFinishResult); else { result.AppendError ("no source filenames matched any command arguments"); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesDump //---------------------------------------------------------------------- // Dump multi-word command for target modules //---------------------------------------------------------------------- class CommandObjectTargetModulesDump : public CommandObjectMultiword { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ CommandObjectTargetModulesDump(CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "target modules dump", "A set of commands for dumping information about one or more target modules.", "target modules dump [symtab|sections|symfile|line-table] [ ...]") { LoadSubCommand ("symtab", CommandObjectSP (new CommandObjectTargetModulesDumpSymtab (interpreter))); LoadSubCommand ("sections", CommandObjectSP (new CommandObjectTargetModulesDumpSections (interpreter))); LoadSubCommand ("symfile", CommandObjectSP (new CommandObjectTargetModulesDumpSymfile (interpreter))); LoadSubCommand ("line-table", CommandObjectSP (new CommandObjectTargetModulesDumpLineTable (interpreter))); } ~CommandObjectTargetModulesDump() override { } }; class CommandObjectTargetModulesAdd : public CommandObjectParsed { public: CommandObjectTargetModulesAdd (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target modules add", "Add a new module to the current target's modules.", "target modules add []"), m_option_group (interpreter), m_symbol_file (LLDB_OPT_SET_1, false, "symfile", 's', 0, eArgTypeFilename, "Fullpath to a stand alone debug symbols file for when debug symbols are not in the executable.") { m_option_group.Append (&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectTargetModulesAdd () override { } Options * GetOptions () override { return &m_option_group; } int HandleArgumentCompletion (Args &input, int &cursor_index, int &cursor_char_position, OptionElementVector &opt_element_vector, int match_start_point, int max_return_elements, bool &word_complete, StringList &matches) override { std::string completion_str (input.GetArgumentAtIndex(cursor_index)); completion_str.erase (cursor_char_position); CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, CommandCompletions::eDiskFileCompletion, completion_str.c_str(), match_start_point, max_return_elements, NULL, word_complete, matches); return matches.GetSize(); } protected: OptionGroupOptions m_option_group; OptionGroupUUID m_uuid_option_group; OptionGroupFile m_symbol_file; bool DoExecute (Args& args, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == NULL) { result.AppendError ("invalid target, create a debug target using the 'target create' command"); result.SetStatus (eReturnStatusFailed); return false; } else { bool flush = false; const size_t argc = args.GetArgumentCount(); if (argc == 0) { if (m_uuid_option_group.GetOptionValue ().OptionWasSet()) { // We are given a UUID only, go locate the file ModuleSpec module_spec; module_spec.GetUUID() = m_uuid_option_group.GetOptionValue ().GetCurrentValue(); if (m_symbol_file.GetOptionValue().OptionWasSet()) module_spec.GetSymbolFileSpec() = m_symbol_file.GetOptionValue().GetCurrentValue(); if (Symbols::DownloadObjectAndSymbolFile (module_spec)) { ModuleSP module_sp (target->GetSharedModule (module_spec)); if (module_sp) { result.SetStatus (eReturnStatusSuccessFinishResult); return true; } else { StreamString strm; module_spec.GetUUID().Dump (&strm); if (module_spec.GetFileSpec()) { if (module_spec.GetSymbolFileSpec()) { result.AppendErrorWithFormat ("Unable to create the executable or symbol file with UUID %s with path %s and symbol file %s", strm.GetString().c_str(), module_spec.GetFileSpec().GetPath().c_str(), module_spec.GetSymbolFileSpec().GetPath().c_str()); } else { result.AppendErrorWithFormat ("Unable to create the executable or symbol file with UUID %s with path %s", strm.GetString().c_str(), module_spec.GetFileSpec().GetPath().c_str()); } } else { result.AppendErrorWithFormat ("Unable to create the executable or symbol file with UUID %s", strm.GetString().c_str()); } result.SetStatus (eReturnStatusFailed); return false; } } else { StreamString strm; module_spec.GetUUID().Dump (&strm); result.AppendErrorWithFormat ("Unable to locate the executable or symbol file with UUID %s", strm.GetString().c_str()); result.SetStatus (eReturnStatusFailed); return false; } } else { result.AppendError ("one or more executable image paths must be specified"); result.SetStatus (eReturnStatusFailed); return false; } } else { for (size_t i=0; iGetArchitecture(); Error error; ModuleSP module_sp (target->GetSharedModule (module_spec, &error)); if (!module_sp) { const char *error_cstr = error.AsCString(); if (error_cstr) result.AppendError (error_cstr); else result.AppendErrorWithFormat ("unsupported module: %s", path); result.SetStatus (eReturnStatusFailed); return false; } else { flush = true; } result.SetStatus (eReturnStatusSuccessFinishResult); } else { char resolved_path[PATH_MAX]; result.SetStatus (eReturnStatusFailed); if (file_spec.GetPath (resolved_path, sizeof(resolved_path))) { if (strcmp (resolved_path, path) != 0) { result.AppendErrorWithFormat ("invalid module path '%s' with resolved path '%s'\n", path, resolved_path); break; } } result.AppendErrorWithFormat ("invalid module path '%s'\n", path); break; } } } } if (flush) { ProcessSP process = target->GetProcessSP(); if (process) process->Flush(); } } return result.Succeeded(); } }; class CommandObjectTargetModulesLoad : public CommandObjectTargetModulesModuleAutoComplete { public: CommandObjectTargetModulesLoad (CommandInterpreter &interpreter) : CommandObjectTargetModulesModuleAutoComplete (interpreter, "target modules load", "Set the load addresses for one or more sections in a target module.", "target modules load [--file --uuid ]
[
....]"), m_option_group (interpreter), m_file_option (LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypeName, "Fullpath or basename for module to load.", ""), m_slide_option(LLDB_OPT_SET_1, false, "slide", 's', 0, eArgTypeOffset, "Set the load address for all sections to be the virtual address in the file plus the offset.", 0) { m_option_group.Append (&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_slide_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectTargetModulesLoad () override { } Options * GetOptions () override { return &m_option_group; } protected: bool DoExecute (Args& args, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == NULL) { result.AppendError ("invalid target, create a debug target using the 'target create' command"); result.SetStatus (eReturnStatusFailed); return false; } else { const size_t argc = args.GetArgumentCount(); ModuleSpec module_spec; bool search_using_module_spec = false; if (m_file_option.GetOptionValue().OptionWasSet()) { search_using_module_spec = true; const char *arg_cstr = m_file_option.GetOptionValue().GetCurrentValue(); const bool use_global_module_list = true; ModuleList module_list; const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, use_global_module_list); if (num_matches == 1) { module_spec.GetFileSpec() = module_list.GetModuleAtIndex(0)->GetFileSpec(); } else if (num_matches > 1 ) { search_using_module_spec = false; result.AppendErrorWithFormat ("more than 1 module matched by name '%s'\n", arg_cstr); result.SetStatus (eReturnStatusFailed); } else { search_using_module_spec = false; result.AppendErrorWithFormat ("no object file for module '%s'\n", arg_cstr); result.SetStatus (eReturnStatusFailed); } } if (m_uuid_option_group.GetOptionValue().OptionWasSet()) { search_using_module_spec = true; module_spec.GetUUID() = m_uuid_option_group.GetOptionValue().GetCurrentValue(); } if (search_using_module_spec) { ModuleList matching_modules; const size_t num_matches = target->GetImages().FindModules (module_spec, matching_modules); char path[PATH_MAX]; if (num_matches == 1) { Module *module = matching_modules.GetModulePointerAtIndex(0); if (module) { ObjectFile *objfile = module->GetObjectFile(); if (objfile) { SectionList *section_list = module->GetSectionList(); if (section_list) { bool changed = false; if (argc == 0) { if (m_slide_option.GetOptionValue().OptionWasSet()) { const addr_t slide = m_slide_option.GetOptionValue().GetCurrentValue(); const bool slide_is_offset = true; module->SetLoadAddress (*target, slide, slide_is_offset, changed); } else { result.AppendError ("one or more section name + load address pair must be specified"); result.SetStatus (eReturnStatusFailed); return false; } } else { if (m_slide_option.GetOptionValue().OptionWasSet()) { result.AppendError ("The \"--slide \" option can't be used in conjunction with setting section load addresses.\n"); result.SetStatus (eReturnStatusFailed); return false; } for (size_t i=0; iFindSectionByName(const_sect_name)); if (section_sp) { if (section_sp->IsThreadSpecific()) { result.AppendErrorWithFormat ("thread specific sections are not yet supported (section '%s')\n", sect_name); result.SetStatus (eReturnStatusFailed); break; } else { if (target->GetSectionLoadList().SetSectionLoadAddress (section_sp, load_addr)) changed = true; result.AppendMessageWithFormat("section '%s' loaded at 0x%" PRIx64 "\n", sect_name, load_addr); } } else { result.AppendErrorWithFormat ("no section found that matches the section name '%s'\n", sect_name); result.SetStatus (eReturnStatusFailed); break; } } else { result.AppendErrorWithFormat ("invalid load address string '%s'\n", load_addr_cstr); result.SetStatus (eReturnStatusFailed); break; } } else { if (sect_name) result.AppendError ("section names must be followed by a load address.\n"); else result.AppendError ("one or more section name + load address pair must be specified.\n"); result.SetStatus (eReturnStatusFailed); break; } } } if (changed) { target->ModulesDidLoad (matching_modules); Process *process = m_exe_ctx.GetProcessPtr(); if (process) process->Flush(); } } else { module->GetFileSpec().GetPath (path, sizeof(path)); result.AppendErrorWithFormat ("no sections in object file '%s'\n", path); result.SetStatus (eReturnStatusFailed); } } else { module->GetFileSpec().GetPath (path, sizeof(path)); result.AppendErrorWithFormat ("no object file for module '%s'\n", path); result.SetStatus (eReturnStatusFailed); } } else { FileSpec *module_spec_file = module_spec.GetFileSpecPtr(); if (module_spec_file) { module_spec_file->GetPath (path, sizeof(path)); result.AppendErrorWithFormat ("invalid module '%s'.\n", path); } else result.AppendError ("no module spec"); result.SetStatus (eReturnStatusFailed); } } else { std::string uuid_str; if (module_spec.GetFileSpec()) module_spec.GetFileSpec().GetPath (path, sizeof(path)); else path[0] = '\0'; if (module_spec.GetUUIDPtr()) uuid_str = module_spec.GetUUID().GetAsString(); if (num_matches > 1) { result.AppendErrorWithFormat ("multiple modules match%s%s%s%s:\n", path[0] ? " file=" : "", path, !uuid_str.empty() ? " uuid=" : "", uuid_str.c_str()); for (size_t i=0; iGetFileSpec().GetPath (path, sizeof(path))) result.AppendMessageWithFormat("%s\n", path); } } else { result.AppendErrorWithFormat ("no modules were found that match%s%s%s%s.\n", path[0] ? " file=" : "", path, !uuid_str.empty() ? " uuid=" : "", uuid_str.c_str()); } result.SetStatus (eReturnStatusFailed); } } else { result.AppendError ("either the \"--file \" or the \"--uuid \" option must be specified.\n"); result.SetStatus (eReturnStatusFailed); return false; } } return result.Succeeded(); } OptionGroupOptions m_option_group; OptionGroupUUID m_uuid_option_group; OptionGroupString m_file_option; OptionGroupUInt64 m_slide_option; }; //---------------------------------------------------------------------- // List images with associated information //---------------------------------------------------------------------- class CommandObjectTargetModulesList : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options(interpreter), m_format_array(), m_use_global_module_list (false), m_module_addr (LLDB_INVALID_ADDRESS) { } ~CommandOptions () override { } Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; if (short_option == 'g') { m_use_global_module_list = true; } else if (short_option == 'a') { ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); m_module_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); } else { unsigned long width = 0; if (option_arg) width = strtoul (option_arg, NULL, 0); m_format_array.push_back(std::make_pair(short_option, width)); } return error; } void OptionParsingStarting () override { m_format_array.clear(); m_use_global_module_list = false; m_module_addr = LLDB_INVALID_ADDRESS; } const OptionDefinition* GetDefinitions () override { return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. typedef std::vector< std::pair > FormatWidthCollection; FormatWidthCollection m_format_array; bool m_use_global_module_list; lldb::addr_t m_module_addr; }; CommandObjectTargetModulesList (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target modules list", "List current executable and dependent shared library images.", "target modules list []"), m_options (interpreter) { } ~CommandObjectTargetModulesList () override { } Options * GetOptions () override { return &m_options; } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); const bool use_global_module_list = m_options.m_use_global_module_list; // Define a local module list here to ensure it lives longer than any "locker" // object which might lock its contents below (through the "module_list_ptr" // variable). ModuleList module_list; if (target == NULL && use_global_module_list == false) { result.AppendError ("invalid target, create a debug target using the 'target create' command"); result.SetStatus (eReturnStatusFailed); return false; } else { if (target) { uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); } // Dump all sections for all modules images Stream &strm = result.GetOutputStream(); if (m_options.m_module_addr != LLDB_INVALID_ADDRESS) { if (target) { Address module_address; if (module_address.SetLoadAddress(m_options.m_module_addr, target)) { ModuleSP module_sp (module_address.GetModule()); if (module_sp) { PrintModule (target, module_sp.get(), 0, strm); result.SetStatus (eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat ("Couldn't find module matching address: 0x%" PRIx64 ".", m_options.m_module_addr); result.SetStatus (eReturnStatusFailed); } } else { result.AppendErrorWithFormat ("Couldn't find module containing address: 0x%" PRIx64 ".", m_options.m_module_addr); result.SetStatus (eReturnStatusFailed); } } else { result.AppendError ("Can only look up modules by address with a valid target."); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } size_t num_modules = 0; Mutex::Locker locker; // This locker will be locked on the mutex in module_list_ptr if it is non-NULL. // Otherwise it will lock the AllocationModuleCollectionMutex when accessing // the global module list directly. const ModuleList *module_list_ptr = NULL; const size_t argc = command.GetArgumentCount(); if (argc == 0) { if (use_global_module_list) { locker.Lock (Module::GetAllocationModuleCollectionMutex()); num_modules = Module::GetNumberAllocatedModules(); } else { module_list_ptr = &target->GetImages(); } } else { for (size_t i=0; iGetMutex()); num_modules = module_list_ptr->GetSize(); } if (num_modules > 0) { for (uint32_t image_idx = 0; image_idxGetModuleAtIndexUnlocked(image_idx); module = module_sp.get(); } else { module = Module::GetAllocatedModuleAtIndex(image_idx); module_sp = module->shared_from_this(); } const size_t indent = strm.Printf("[%3u] ", image_idx); PrintModule (target, module, indent, strm); } result.SetStatus (eReturnStatusSuccessFinishResult); } else { if (argc) { if (use_global_module_list) result.AppendError ("the global module list has no matching modules"); else result.AppendError ("the target has no matching modules"); } else { if (use_global_module_list) result.AppendError ("the global module list is empty"); else result.AppendError ("the target has no associated executable images"); } result.SetStatus (eReturnStatusFailed); return false; } } return result.Succeeded(); } void PrintModule (Target *target, Module *module, int indent, Stream &strm) { if (module == NULL) { strm.PutCString("Null module"); return; } bool dump_object_name = false; if (m_options.m_format_array.empty()) { m_options.m_format_array.push_back(std::make_pair('u', 0)); m_options.m_format_array.push_back(std::make_pair('h', 0)); m_options.m_format_array.push_back(std::make_pair('f', 0)); m_options.m_format_array.push_back(std::make_pair('S', 0)); } const size_t num_entries = m_options.m_format_array.size(); bool print_space = false; for (size_t i=0; iGetFileSpec(), width); dump_object_name = true; break; case 'd': DumpDirectory (strm, &module->GetFileSpec(), width); break; case 'b': DumpBasename (strm, &module->GetFileSpec(), width); dump_object_name = true; break; case 'h': case 'o': // Image header address { uint32_t addr_nibble_width = target ? (target->GetArchitecture().GetAddressByteSize() * 2) : 16; ObjectFile *objfile = module->GetObjectFile (); if (objfile) { Address header_addr(objfile->GetHeaderAddress()); if (header_addr.IsValid()) { if (target && !target->GetSectionLoadList().IsEmpty()) { lldb::addr_t header_load_addr = header_addr.GetLoadAddress (target); if (header_load_addr == LLDB_INVALID_ADDRESS) { header_addr.Dump (&strm, target, Address::DumpStyleModuleWithFileAddress, Address::DumpStyleFileAddress); } else { if (format_char == 'o') { // Show the offset of slide for the image strm.Printf ("0x%*.*" PRIx64, addr_nibble_width, addr_nibble_width, header_load_addr - header_addr.GetFileAddress()); } else { // Show the load address of the image strm.Printf ("0x%*.*" PRIx64, addr_nibble_width, addr_nibble_width, header_load_addr); } } break; } // The address was valid, but the image isn't loaded, output the address in an appropriate format header_addr.Dump (&strm, target, Address::DumpStyleFileAddress); break; } } strm.Printf ("%*s", addr_nibble_width + 2, ""); } break; case 'r': { size_t ref_count = 0; ModuleSP module_sp (module->shared_from_this()); if (module_sp) { // Take one away to make sure we don't count our local "module_sp" ref_count = module_sp.use_count() - 1; } if (width) strm.Printf("{%*" PRIu64 "}", width, (uint64_t)ref_count); else strm.Printf("{%" PRIu64 "}", (uint64_t)ref_count); } break; case 's': case 'S': { const SymbolVendor *symbol_vendor = module->GetSymbolVendor(); if (symbol_vendor) { const FileSpec symfile_spec = symbol_vendor->GetMainFileSpec(); if (format_char == 'S') { // Dump symbol file only if different from module file if (!symfile_spec || symfile_spec == module->GetFileSpec()) { print_space = false; break; } // Add a newline and indent past the index strm.Printf ("\n%*s", indent, ""); } DumpFullpath (strm, &symfile_spec, width); dump_object_name = true; break; } strm.Printf("%.*s", width, ""); } break; case 'm': module->GetModificationTime().Dump(&strm, width); break; case 'p': strm.Printf("%p", static_cast(module)); break; case 'u': DumpModuleUUID(strm, module); break; default: break; } } if (dump_object_name) { const char *object_name = module->GetObjectName().GetCString(); if (object_name) strm.Printf ("(%s)", object_name); } strm.EOL(); } CommandOptions m_options; }; OptionDefinition CommandObjectTargetModulesList::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_1, false, "address", 'a', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeAddressOrExpression, "Display the image at this address."}, { LLDB_OPT_SET_1, false, "arch", 'A', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeWidth, "Display the architecture when listing images."}, { LLDB_OPT_SET_1, false, "triple", 't', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeWidth, "Display the triple when listing images."}, { LLDB_OPT_SET_1, false, "header", 'h', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Display the image header address as a load address if debugging, a file address otherwise."}, { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Display the image header address offset from the header file address (the slide amount)."}, { LLDB_OPT_SET_1, false, "uuid", 'u', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Display the UUID when listing images."}, { LLDB_OPT_SET_1, false, "fullpath", 'f', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeWidth, "Display the fullpath to the image object file."}, { LLDB_OPT_SET_1, false, "directory", 'd', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeWidth, "Display the directory with optional width for the image object file."}, { LLDB_OPT_SET_1, false, "basename", 'b', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeWidth, "Display the basename with optional width for the image object file."}, { LLDB_OPT_SET_1, false, "symfile", 's', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeWidth, "Display the fullpath to the image symbol file with optional width."}, { LLDB_OPT_SET_1, false, "symfile-unique", 'S', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeWidth, "Display the symbol file with optional width only if it is different from the executable object file."}, { LLDB_OPT_SET_1, false, "mod-time", 'm', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeWidth, "Display the modification time with optional width of the module."}, { LLDB_OPT_SET_1, false, "ref-count", 'r', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeWidth, "Display the reference count if the module is still in the shared module cache."}, { LLDB_OPT_SET_1, false, "pointer", 'p', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeNone, "Display the module pointer."}, { LLDB_OPT_SET_1, false, "global", 'g', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Display the modules from the global module list, not just the current target."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; #pragma mark CommandObjectTargetModulesShowUnwind //---------------------------------------------------------------------- // Lookup unwind information in images //---------------------------------------------------------------------- class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed { public: enum { eLookupTypeInvalid = -1, eLookupTypeAddress = 0, eLookupTypeSymbol, eLookupTypeFunction, eLookupTypeFunctionOrSymbol, kNumLookupTypes }; class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options(interpreter), m_type(eLookupTypeInvalid), m_str(), m_addr(LLDB_INVALID_ADDRESS) { } ~CommandOptions () override { } Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'a': { ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); m_str = option_arg; m_type = eLookupTypeAddress; m_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); if (m_addr == LLDB_INVALID_ADDRESS) error.SetErrorStringWithFormat ("invalid address string '%s'", option_arg); break; } case 'n': { m_str = option_arg; m_type = eLookupTypeFunctionOrSymbol; break; } default: error.SetErrorStringWithFormat ("unrecognized option %c.", short_option); break; } return error; } void OptionParsingStarting () override { m_type = eLookupTypeInvalid; m_str.clear(); m_addr = LLDB_INVALID_ADDRESS; } const OptionDefinition* GetDefinitions () override { return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. int m_type; // Should be a eLookupTypeXXX enum after parsing options std::string m_str; // Holds name lookup lldb::addr_t m_addr; // Holds the address to lookup }; CommandObjectTargetModulesShowUnwind (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target modules show-unwind", "Show synthesized unwind instructions for a function.", NULL, eCommandRequiresTarget | eCommandRequiresProcess | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused ), m_options (interpreter) { } ~CommandObjectTargetModulesShowUnwind () override { } Options * GetOptions () override { return &m_options; } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = m_exe_ctx.GetTargetPtr(); Process *process = m_exe_ctx.GetProcessPtr(); ABI *abi = NULL; if (process) abi = process->GetABI().get(); if (process == NULL) { result.AppendError ("You must have a process running to use this command."); result.SetStatus (eReturnStatusFailed); return false; } ThreadList threads(process->GetThreadList()); if (threads.GetSize() == 0) { result.AppendError ("The process must be paused to use this command."); result.SetStatus (eReturnStatusFailed); return false; } ThreadSP thread(threads.GetThreadAtIndex(0)); if (thread.get() == NULL) { result.AppendError ("The process must be paused to use this command."); result.SetStatus (eReturnStatusFailed); return false; } SymbolContextList sc_list; if (m_options.m_type == eLookupTypeFunctionOrSymbol) { ConstString function_name (m_options.m_str.c_str()); target->GetImages().FindFunctions (function_name, eFunctionNameTypeAuto, true, false, true, sc_list); } else if (m_options.m_type == eLookupTypeAddress && target) { Address addr; if (target->GetSectionLoadList().ResolveLoadAddress (m_options.m_addr, addr)) { SymbolContext sc; ModuleSP module_sp (addr.GetModule()); module_sp->ResolveSymbolContextForAddress (addr, eSymbolContextEverything, sc); if (sc.function || sc.symbol) { sc_list.Append(sc); } } } else { result.AppendError ("address-expression or function name option must be specified."); result.SetStatus (eReturnStatusFailed); return false; } size_t num_matches = sc_list.GetSize(); if (num_matches == 0) { result.AppendErrorWithFormat ("no unwind data found that matches '%s'.", m_options.m_str.c_str()); result.SetStatus (eReturnStatusFailed); return false; } for (uint32_t idx = 0; idx < num_matches; idx++) { SymbolContext sc; sc_list.GetContextAtIndex(idx, sc); if (sc.symbol == NULL && sc.function == NULL) continue; if (sc.module_sp.get() == NULL || sc.module_sp->GetObjectFile() == NULL) continue; AddressRange range; if (!sc.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, range)) continue; if (!range.GetBaseAddress().IsValid()) continue; ConstString funcname(sc.GetFunctionName()); if (funcname.IsEmpty()) continue; addr_t start_addr = range.GetBaseAddress().GetLoadAddress(target); if (abi) start_addr = abi->FixCodeAddress(start_addr); FuncUnwindersSP func_unwinders_sp (sc.module_sp->GetObjectFile()->GetUnwindTable().GetUncachedFuncUnwindersContainingAddress(start_addr, sc)); if (func_unwinders_sp.get() == NULL) continue; result.GetOutputStream().Printf("UNWIND PLANS for %s`%s (start addr 0x%" PRIx64 ")\n\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); UnwindPlanSP non_callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtNonCallSite(*target, *thread.get(), -1); if (non_callsite_unwind_plan.get()) { result.GetOutputStream().Printf("Asynchronous (not restricted to call-sites) UnwindPlan is '%s'\n", non_callsite_unwind_plan->GetSourceName().AsCString()); } UnwindPlanSP callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(*target, -1); if (callsite_unwind_plan.get()) { result.GetOutputStream().Printf("Synchronous (restricted to call-sites) UnwindPlan is '%s'\n", callsite_unwind_plan->GetSourceName().AsCString()); } UnwindPlanSP fast_unwind_plan = func_unwinders_sp->GetUnwindPlanFastUnwind(*target, *thread.get()); if (fast_unwind_plan.get()) { result.GetOutputStream().Printf("Fast UnwindPlan is '%s'\n", fast_unwind_plan->GetSourceName().AsCString()); } result.GetOutputStream().Printf("\n"); UnwindPlanSP assembly_sp = func_unwinders_sp->GetAssemblyUnwindPlan(*target, *thread.get(), 0); if (assembly_sp) { result.GetOutputStream().Printf("Assembly language inspection UnwindPlan:\n"); assembly_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } UnwindPlanSP ehframe_sp = func_unwinders_sp->GetEHFrameUnwindPlan(*target, 0); if (ehframe_sp) { result.GetOutputStream().Printf("eh_frame UnwindPlan:\n"); ehframe_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } UnwindPlanSP ehframe_augmented_sp = func_unwinders_sp->GetEHFrameAugmentedUnwindPlan(*target, *thread.get(), 0); if (ehframe_augmented_sp) { result.GetOutputStream().Printf("eh_frame augmented UnwindPlan:\n"); ehframe_augmented_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } UnwindPlanSP arm_unwind_sp = func_unwinders_sp->GetArmUnwindUnwindPlan(*target, 0); if (arm_unwind_sp) { result.GetOutputStream().Printf("ARM.exidx unwind UnwindPlan:\n"); arm_unwind_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } UnwindPlanSP compact_unwind_sp = func_unwinders_sp->GetCompactUnwindUnwindPlan(*target, 0); if (compact_unwind_sp) { result.GetOutputStream().Printf("Compact unwind UnwindPlan:\n"); compact_unwind_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } if (fast_unwind_plan) { result.GetOutputStream().Printf("Fast UnwindPlan:\n"); fast_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } ABISP abi_sp = process->GetABI(); if (abi_sp) { UnwindPlan arch_default(lldb::eRegisterKindGeneric); if (abi_sp->CreateDefaultUnwindPlan (arch_default)) { result.GetOutputStream().Printf("Arch default UnwindPlan:\n"); arch_default.Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } UnwindPlan arch_entry(lldb::eRegisterKindGeneric); if (abi_sp->CreateFunctionEntryUnwindPlan (arch_entry)) { result.GetOutputStream().Printf("Arch default at entry point UnwindPlan:\n"); arch_entry.Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } } result.GetOutputStream().Printf ("\n"); } return result.Succeeded(); } CommandOptions m_options; }; OptionDefinition CommandObjectTargetModulesShowUnwind::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_1, false, "name", 'n', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFunctionName, "Show unwind instructions for a function or symbol name."}, { LLDB_OPT_SET_2, false, "address", 'a', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeAddressOrExpression, "Show unwind instructions for a function or symbol containing an address"}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; //---------------------------------------------------------------------- // Lookup information in images //---------------------------------------------------------------------- class CommandObjectTargetModulesLookup : public CommandObjectParsed { public: enum { eLookupTypeInvalid = -1, eLookupTypeAddress = 0, eLookupTypeSymbol, eLookupTypeFileLine, // Line is optional eLookupTypeFunction, eLookupTypeFunctionOrSymbol, eLookupTypeType, kNumLookupTypes }; class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options(interpreter) { OptionParsingStarting(); } ~CommandOptions () override { } Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'a': { m_type = eLookupTypeAddress; ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); m_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); } break; case 'o': m_offset = StringConvert::ToUInt64(option_arg, LLDB_INVALID_ADDRESS); if (m_offset == LLDB_INVALID_ADDRESS) error.SetErrorStringWithFormat ("invalid offset string '%s'", option_arg); break; case 's': m_str = option_arg; m_type = eLookupTypeSymbol; break; case 'f': m_file.SetFile (option_arg, false); m_type = eLookupTypeFileLine; break; case 'i': m_include_inlines = false; break; case 'l': m_line_number = StringConvert::ToUInt32(option_arg, UINT32_MAX); if (m_line_number == UINT32_MAX) error.SetErrorStringWithFormat ("invalid line number string '%s'", option_arg); else if (m_line_number == 0) error.SetErrorString ("zero is an invalid line number"); m_type = eLookupTypeFileLine; break; case 'F': m_str = option_arg; m_type = eLookupTypeFunction; break; case 'n': m_str = option_arg; m_type = eLookupTypeFunctionOrSymbol; break; case 't': m_str = option_arg; m_type = eLookupTypeType; break; case 'v': m_verbose = 1; break; case 'A': m_print_all = true; break; case 'r': m_use_regex = true; break; } return error; } void OptionParsingStarting () override { m_type = eLookupTypeInvalid; m_str.clear(); m_file.Clear(); m_addr = LLDB_INVALID_ADDRESS; m_offset = 0; m_line_number = 0; m_use_regex = false; m_include_inlines = true; m_verbose = false; m_print_all = false; } const OptionDefinition* GetDefinitions () override { return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; int m_type; // Should be a eLookupTypeXXX enum after parsing options std::string m_str; // Holds name lookup FileSpec m_file; // Files for file lookups lldb::addr_t m_addr; // Holds the address to lookup lldb::addr_t m_offset; // Subtract this offset from m_addr before doing lookups. uint32_t m_line_number; // Line number for file+line lookups bool m_use_regex; // Name lookups in m_str are regular expressions. bool m_include_inlines;// Check for inline entries when looking up by file/line. bool m_verbose; // Enable verbose lookup info bool m_print_all; // Print all matches, even in cases where there's a best match. }; CommandObjectTargetModulesLookup (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target modules lookup", "Look up information within executable and dependent shared library images.", NULL, eCommandRequiresTarget), m_options (interpreter) { CommandArgumentEntry arg; CommandArgumentData file_arg; // Define the first (and only) variant of this arg. file_arg.arg_type = eArgTypeFilename; file_arg.arg_repetition = eArgRepeatStar; // There is only one variant this argument could be; put it into the argument entry. arg.push_back (file_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back (arg); } ~CommandObjectTargetModulesLookup () override { } Options * GetOptions () override { return &m_options; } bool LookupHere (CommandInterpreter &interpreter, CommandReturnObject &result, bool &syntax_error) { switch (m_options.m_type) { case eLookupTypeAddress: case eLookupTypeFileLine: case eLookupTypeFunction: case eLookupTypeFunctionOrSymbol: case eLookupTypeSymbol: default: return false; case eLookupTypeType: break; } StackFrameSP frame = m_exe_ctx.GetFrameSP(); if (!frame) return false; const SymbolContext &sym_ctx(frame->GetSymbolContext(eSymbolContextModule)); if (!sym_ctx.module_sp) return false; switch (m_options.m_type) { default: return false; case eLookupTypeType: if (!m_options.m_str.empty()) { if (LookupTypeHere (m_interpreter, result.GetOutputStream(), sym_ctx, m_options.m_str.c_str(), m_options.m_use_regex)) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } break; } return true; } bool LookupInModule (CommandInterpreter &interpreter, Module *module, CommandReturnObject &result, bool &syntax_error) { switch (m_options.m_type) { case eLookupTypeAddress: if (m_options.m_addr != LLDB_INVALID_ADDRESS) { if (LookupAddressInModule (m_interpreter, result.GetOutputStream(), module, eSymbolContextEverything | (m_options.m_verbose ? eSymbolContextVariable : 0), m_options.m_addr, m_options.m_offset, m_options.m_verbose)) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } break; case eLookupTypeSymbol: if (!m_options.m_str.empty()) { if (LookupSymbolInModule (m_interpreter, result.GetOutputStream(), module, m_options.m_str.c_str(), m_options.m_use_regex, m_options.m_verbose)) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } break; case eLookupTypeFileLine: if (m_options.m_file) { if (LookupFileAndLineInModule (m_interpreter, result.GetOutputStream(), module, m_options.m_file, m_options.m_line_number, m_options.m_include_inlines, m_options.m_verbose)) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } break; case eLookupTypeFunctionOrSymbol: case eLookupTypeFunction: if (!m_options.m_str.empty()) { if (LookupFunctionInModule (m_interpreter, result.GetOutputStream(), module, m_options.m_str.c_str(), m_options.m_use_regex, m_options.m_include_inlines, m_options.m_type == eLookupTypeFunctionOrSymbol, // include symbols m_options.m_verbose)) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } break; case eLookupTypeType: if (!m_options.m_str.empty()) { if (LookupTypeInModule (m_interpreter, result.GetOutputStream(), module, m_options.m_str.c_str(), m_options.m_use_regex)) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } break; default: m_options.GenerateOptionUsage (result.GetErrorStream(), this); syntax_error = true; break; } result.SetStatus (eReturnStatusFailed); return false; } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == NULL) { result.AppendError ("invalid target, create a debug target using the 'target create' command"); result.SetStatus (eReturnStatusFailed); return false; } else { bool syntax_error = false; uint32_t i; uint32_t num_successful_lookups = 0; uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); // Dump all sections for all modules images if (command.GetArgumentCount() == 0) { ModuleSP current_module; // Where it is possible to look in the current symbol context // first, try that. If this search was successful and --all // was not passed, don't print anything else. if (LookupHere (m_interpreter, result, syntax_error)) { result.GetOutputStream().EOL(); num_successful_lookups++; if (!m_options.m_print_all) { result.SetStatus (eReturnStatusSuccessFinishResult); return result.Succeeded(); } } // Dump all sections for all other modules const ModuleList &target_modules = target->GetImages(); Mutex::Locker modules_locker(target_modules.GetMutex()); const size_t num_modules = target_modules.GetSize(); if (num_modules > 0) { for (i = 0; i 0) { for (size_t j=0; j 0) result.SetStatus (eReturnStatusSuccessFinishResult); else result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } CommandOptions m_options; }; OptionDefinition CommandObjectTargetModulesLookup::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_1, true, "address", 'a', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeAddressOrExpression, "Lookup an address in one or more target modules."}, { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeOffset, "When looking up an address subtract from any addresses before doing the lookup."}, { LLDB_OPT_SET_2| LLDB_OPT_SET_4 | LLDB_OPT_SET_5 /* FIXME: re-enable this for types when the LookupTypeInModule actually uses the regex option: | LLDB_OPT_SET_6 */ , false, "regex", 'r', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "The argument for name lookups are regular expressions."}, { LLDB_OPT_SET_2, true, "symbol", 's', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeSymbol, "Lookup a symbol by name in the symbol tables in one or more target modules."}, { LLDB_OPT_SET_3, true, "file", 'f', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFilename, "Lookup a file by fullpath or basename in one or more target modules."}, { LLDB_OPT_SET_3, false, "line", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, "Lookup a line number in a file (must be used in conjunction with --file)."}, { LLDB_OPT_SET_FROM_TO(3,5), false, "no-inlines", 'i', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Ignore inline entries (must be used in conjunction with --file or --function)."}, { LLDB_OPT_SET_4, true, "function", 'F', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFunctionName, "Lookup a function by name in the debug symbols in one or more target modules."}, { LLDB_OPT_SET_5, true, "name", 'n', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFunctionOrSymbol, "Lookup a function or symbol by name in one or more target modules."}, { LLDB_OPT_SET_6, true, "type", 't', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeName, "Lookup a type by name in the debug symbols in one or more target modules."}, { LLDB_OPT_SET_ALL, false, "verbose", 'v', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Enable verbose lookup information."}, { LLDB_OPT_SET_ALL, false, "all", 'A', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Print all matches, not just the best match, if a best match is available."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; #pragma mark CommandObjectMultiwordImageSearchPaths //------------------------------------------------------------------------- // CommandObjectMultiwordImageSearchPaths //------------------------------------------------------------------------- class CommandObjectTargetModulesImageSearchPaths : public CommandObjectMultiword { public: CommandObjectTargetModulesImageSearchPaths (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "target modules search-paths", "A set of commands for operating on debugger target image search paths.", "target modules search-paths []") { LoadSubCommand ("add", CommandObjectSP (new CommandObjectTargetModulesSearchPathsAdd (interpreter))); LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTargetModulesSearchPathsClear (interpreter))); LoadSubCommand ("insert", CommandObjectSP (new CommandObjectTargetModulesSearchPathsInsert (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectTargetModulesSearchPathsList (interpreter))); LoadSubCommand ("query", CommandObjectSP (new CommandObjectTargetModulesSearchPathsQuery (interpreter))); } ~CommandObjectTargetModulesImageSearchPaths() override { } }; #pragma mark CommandObjectTargetModules //------------------------------------------------------------------------- // CommandObjectTargetModules //------------------------------------------------------------------------- class CommandObjectTargetModules : public CommandObjectMultiword { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ CommandObjectTargetModules(CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "target modules", "A set of commands for accessing information for one or more target modules.", "target modules ...") { LoadSubCommand ("add", CommandObjectSP (new CommandObjectTargetModulesAdd (interpreter))); LoadSubCommand ("load", CommandObjectSP (new CommandObjectTargetModulesLoad (interpreter))); LoadSubCommand ("dump", CommandObjectSP (new CommandObjectTargetModulesDump (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectTargetModulesList (interpreter))); LoadSubCommand ("lookup", CommandObjectSP (new CommandObjectTargetModulesLookup (interpreter))); LoadSubCommand ("search-paths", CommandObjectSP (new CommandObjectTargetModulesImageSearchPaths (interpreter))); LoadSubCommand ("show-unwind", CommandObjectSP (new CommandObjectTargetModulesShowUnwind (interpreter))); } ~CommandObjectTargetModules() override { } private: //------------------------------------------------------------------ // For CommandObjectTargetModules only //------------------------------------------------------------------ DISALLOW_COPY_AND_ASSIGN (CommandObjectTargetModules); }; class CommandObjectTargetSymbolsAdd : public CommandObjectParsed { public: CommandObjectTargetSymbolsAdd (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target symbols add", "Add a debug symbol file to one of the target's current modules by specifying a path to a debug symbols file, or using the options to specify a module to download symbols for.", "target symbols add []", eCommandRequiresTarget), m_option_group (interpreter), m_file_option (LLDB_OPT_SET_1, false, "shlib", 's', CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Fullpath or basename for module to find debug symbols for."), m_current_frame_option (LLDB_OPT_SET_2, false, "frame", 'F', "Locate the debug symbols the currently selected frame.", false, true) { m_option_group.Append (&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_current_frame_option, LLDB_OPT_SET_2, LLDB_OPT_SET_2); m_option_group.Finalize(); } ~CommandObjectTargetSymbolsAdd () override { } int HandleArgumentCompletion (Args &input, int &cursor_index, int &cursor_char_position, OptionElementVector &opt_element_vector, int match_start_point, int max_return_elements, bool &word_complete, StringList &matches) override { std::string completion_str (input.GetArgumentAtIndex(cursor_index)); completion_str.erase (cursor_char_position); CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, CommandCompletions::eDiskFileCompletion, completion_str.c_str(), match_start_point, max_return_elements, NULL, word_complete, matches); return matches.GetSize(); } Options * GetOptions () override { return &m_option_group; } protected: bool AddModuleSymbols (Target *target, ModuleSpec &module_spec, bool &flush, CommandReturnObject &result) { const FileSpec &symbol_fspec = module_spec.GetSymbolFileSpec(); if (symbol_fspec) { char symfile_path[PATH_MAX]; symbol_fspec.GetPath (symfile_path, sizeof(symfile_path)); if (!module_spec.GetUUID().IsValid()) { if (!module_spec.GetFileSpec() && !module_spec.GetPlatformFileSpec()) module_spec.GetFileSpec().GetFilename() = symbol_fspec.GetFilename(); } // We now have a module that represents a symbol file // that can be used for a module that might exist in the // current target, so we need to find that module in the // target ModuleList matching_module_list; size_t num_matches = 0; // First extract all module specs from the symbol file lldb_private::ModuleSpecList symfile_module_specs; if (ObjectFile::GetModuleSpecifications(module_spec.GetSymbolFileSpec(), 0, 0, symfile_module_specs)) { // Now extract the module spec that matches the target architecture ModuleSpec target_arch_module_spec; ModuleSpec symfile_module_spec; target_arch_module_spec.GetArchitecture() = target->GetArchitecture(); if (symfile_module_specs.FindMatchingModuleSpec(target_arch_module_spec, symfile_module_spec)) { // See if it has a UUID? if (symfile_module_spec.GetUUID().IsValid()) { // It has a UUID, look for this UUID in the target modules ModuleSpec symfile_uuid_module_spec; symfile_uuid_module_spec.GetUUID() = symfile_module_spec.GetUUID(); num_matches = target->GetImages().FindModules (symfile_uuid_module_spec, matching_module_list); } } if (num_matches == 0) { // No matches yet, iterate through the module specs to find a UUID value that // we can match up to an image in our target const size_t num_symfile_module_specs = symfile_module_specs.GetSize(); for (size_t i=0; iGetImages().FindModules (symfile_uuid_module_spec, matching_module_list); } } } } } // Just try to match up the file by basename if we have no matches at this point if (num_matches == 0) num_matches = target->GetImages().FindModules (module_spec, matching_module_list); while (num_matches == 0) { ConstString filename_no_extension(module_spec.GetFileSpec().GetFileNameStrippingExtension()); // Empty string returned, lets bail if (!filename_no_extension) break; // Check if there was no extension to strip and the basename is the same if (filename_no_extension == module_spec.GetFileSpec().GetFilename()) break; // Replace basename with one less extension module_spec.GetFileSpec().GetFilename() = filename_no_extension; num_matches = target->GetImages().FindModules (module_spec, matching_module_list); } if (num_matches > 1) { result.AppendErrorWithFormat ("multiple modules match symbol file '%s', use the --uuid option to resolve the ambiguity.\n", symfile_path); } else if (num_matches == 1) { ModuleSP module_sp (matching_module_list.GetModuleAtIndex(0)); // The module has not yet created its symbol vendor, we can just // give the existing target module the symfile path to use for // when it decides to create it! module_sp->SetSymbolFileFileSpec (symbol_fspec); SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(true, &result.GetErrorStream()); if (symbol_vendor) { SymbolFile *symbol_file = symbol_vendor->GetSymbolFile(); if (symbol_file) { ObjectFile *object_file = symbol_file->GetObjectFile(); if (object_file && object_file->GetFileSpec() == symbol_fspec) { // Provide feedback that the symfile has been successfully added. const FileSpec &module_fs = module_sp->GetFileSpec(); result.AppendMessageWithFormat("symbol file '%s' has been added to '%s'\n", symfile_path, module_fs.GetPath().c_str()); // Let clients know something changed in the module // if it is currently loaded ModuleList module_list; module_list.Append (module_sp); target->SymbolsDidLoad (module_list); // Make sure we load any scripting resources that may be embedded // in the debug info files in case the platform supports that. Error error; StreamString feedback_stream; module_sp->LoadScriptingResourceInTarget (target, error,&feedback_stream); if (error.Fail() && error.AsCString()) result.AppendWarningWithFormat("unable to load scripting data for module %s - error reported was %s", module_sp->GetFileSpec().GetFileNameStrippingExtension().GetCString(), error.AsCString()); else if (feedback_stream.GetSize()) result.AppendWarningWithFormat("%s",feedback_stream.GetData()); flush = true; result.SetStatus (eReturnStatusSuccessFinishResult); return true; } } } // Clear the symbol file spec if anything went wrong module_sp->SetSymbolFileFileSpec (FileSpec()); } if (module_spec.GetUUID().IsValid()) { StreamString ss_symfile_uuid; module_spec.GetUUID().Dump(&ss_symfile_uuid); result.AppendErrorWithFormat ("symbol file '%s' (%s) does not match any existing module%s\n", symfile_path, ss_symfile_uuid.GetData(), (symbol_fspec.GetFileType() != FileSpec::eFileTypeRegular) ? "\n please specify the full path to the symbol file" : ""); } else { result.AppendErrorWithFormat ("symbol file '%s' does not match any existing module%s\n", symfile_path, (symbol_fspec.GetFileType() != FileSpec::eFileTypeRegular) ? "\n please specify the full path to the symbol file" : ""); } } else { result.AppendError ("one or more executable image paths must be specified"); } result.SetStatus (eReturnStatusFailed); return false; } bool DoExecute (Args& args, CommandReturnObject &result) override { Target *target = m_exe_ctx.GetTargetPtr(); result.SetStatus (eReturnStatusFailed); bool flush = false; ModuleSpec module_spec; const bool uuid_option_set = m_uuid_option_group.GetOptionValue().OptionWasSet(); const bool file_option_set = m_file_option.GetOptionValue().OptionWasSet(); const bool frame_option_set = m_current_frame_option.GetOptionValue().OptionWasSet(); const size_t argc = args.GetArgumentCount(); if (argc == 0) { if (uuid_option_set || file_option_set || frame_option_set) { bool success = false; bool error_set = false; if (frame_option_set) { Process *process = m_exe_ctx.GetProcessPtr(); if (process) { const StateType process_state = process->GetState(); if (StateIsStoppedState (process_state, true)) { StackFrame *frame = m_exe_ctx.GetFramePtr(); if (frame) { ModuleSP frame_module_sp (frame->GetSymbolContext(eSymbolContextModule).module_sp); if (frame_module_sp) { if (frame_module_sp->GetPlatformFileSpec().Exists()) { module_spec.GetArchitecture() = frame_module_sp->GetArchitecture(); module_spec.GetFileSpec() = frame_module_sp->GetPlatformFileSpec(); } module_spec.GetUUID() = frame_module_sp->GetUUID(); success = module_spec.GetUUID().IsValid() || module_spec.GetFileSpec(); } else { result.AppendError ("frame has no module"); error_set = true; } } else { result.AppendError ("invalid current frame"); error_set = true; } } else { result.AppendErrorWithFormat ("process is not stopped: %s", StateAsCString(process_state)); error_set = true; } } else { result.AppendError ("a process must exist in order to use the --frame option"); error_set = true; } } else { if (uuid_option_set) { module_spec.GetUUID() = m_uuid_option_group.GetOptionValue().GetCurrentValue(); success |= module_spec.GetUUID().IsValid(); } else if (file_option_set) { module_spec.GetFileSpec() = m_file_option.GetOptionValue().GetCurrentValue(); ModuleSP module_sp (target->GetImages().FindFirstModule(module_spec)); if (module_sp) { module_spec.GetFileSpec() = module_sp->GetFileSpec(); module_spec.GetPlatformFileSpec() = module_sp->GetPlatformFileSpec(); module_spec.GetUUID() = module_sp->GetUUID(); module_spec.GetArchitecture() = module_sp->GetArchitecture(); } else { module_spec.GetArchitecture() = target->GetArchitecture(); } success |= module_spec.GetFileSpec().Exists(); } } if (success) { if (Symbols::DownloadObjectAndSymbolFile (module_spec)) { if (module_spec.GetSymbolFileSpec()) success = AddModuleSymbols (target, module_spec, flush, result); } } if (!success && !error_set) { StreamString error_strm; if (uuid_option_set) { error_strm.PutCString("unable to find debug symbols for UUID "); module_spec.GetUUID().Dump (&error_strm); } else if (file_option_set) { error_strm.PutCString("unable to find debug symbols for the executable file "); error_strm << module_spec.GetFileSpec(); } else if (frame_option_set) { error_strm.PutCString("unable to find debug symbols for the current frame"); } result.AppendError (error_strm.GetData()); } } else { result.AppendError ("one or more symbol file paths must be specified, or options must be specified"); } } else { if (uuid_option_set) { result.AppendError ("specify either one or more paths to symbol files or use the --uuid option without arguments"); } else if (file_option_set) { result.AppendError ("specify either one or more paths to symbol files or use the --file option without arguments"); } else if (frame_option_set) { result.AppendError ("specify either one or more paths to symbol files or use the --frame option without arguments"); } else { PlatformSP platform_sp (target->GetPlatform()); for (size_t i=0; iResolveSymbolFile(*target, module_spec, symfile_spec).Success()) module_spec.GetSymbolFileSpec() = symfile_spec; } ArchSpec arch; bool symfile_exists = module_spec.GetSymbolFileSpec().Exists(); if (symfile_exists) { if (!AddModuleSymbols (target, module_spec, flush, result)) break; } else { char resolved_symfile_path[PATH_MAX]; if (module_spec.GetSymbolFileSpec().GetPath (resolved_symfile_path, sizeof(resolved_symfile_path))) { if (strcmp (resolved_symfile_path, symfile_path) != 0) { result.AppendErrorWithFormat ("invalid module path '%s' with resolved path '%s'\n", symfile_path, resolved_symfile_path); break; } } result.AppendErrorWithFormat ("invalid module path '%s'\n", symfile_path); break; } } } } } if (flush) { Process *process = m_exe_ctx.GetProcessPtr(); if (process) process->Flush(); } return result.Succeeded(); } OptionGroupOptions m_option_group; OptionGroupUUID m_uuid_option_group; OptionGroupFile m_file_option; OptionGroupBoolean m_current_frame_option; }; #pragma mark CommandObjectTargetSymbols //------------------------------------------------------------------------- // CommandObjectTargetSymbols //------------------------------------------------------------------------- class CommandObjectTargetSymbols : public CommandObjectMultiword { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ CommandObjectTargetSymbols(CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "target symbols", "A set of commands for adding and managing debug symbol files.", "target symbols ...") { LoadSubCommand ("add", CommandObjectSP (new CommandObjectTargetSymbolsAdd (interpreter))); } ~CommandObjectTargetSymbols() override { } private: //------------------------------------------------------------------ // For CommandObjectTargetModules only //------------------------------------------------------------------ DISALLOW_COPY_AND_ASSIGN (CommandObjectTargetSymbols); }; #pragma mark CommandObjectTargetStopHookAdd //------------------------------------------------------------------------- // CommandObjectTargetStopHookAdd //------------------------------------------------------------------------- class CommandObjectTargetStopHookAdd : public CommandObjectParsed, public IOHandlerDelegateMultiline { public: class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options(interpreter), m_line_start(0), m_line_end (UINT_MAX), m_func_name_type_mask (eFunctionNameTypeAuto), m_sym_ctx_specified (false), m_thread_specified (false), m_use_one_liner (false), m_one_liner() { } ~CommandOptions () override {} const OptionDefinition* GetDefinitions () override { return g_option_table; } Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; bool success; switch (short_option) { case 'c': m_class_name = option_arg; m_sym_ctx_specified = true; break; case 'e': m_line_end = StringConvert::ToUInt32 (option_arg, UINT_MAX, 0, &success); if (!success) { error.SetErrorStringWithFormat ("invalid end line number: \"%s\"", option_arg); break; } m_sym_ctx_specified = true; break; case 'l': m_line_start = StringConvert::ToUInt32 (option_arg, 0, 0, &success); if (!success) { error.SetErrorStringWithFormat ("invalid start line number: \"%s\"", option_arg); break; } m_sym_ctx_specified = true; break; case 'i': m_no_inlines = true; break; case 'n': m_function_name = option_arg; m_func_name_type_mask |= eFunctionNameTypeAuto; m_sym_ctx_specified = true; break; case 'f': m_file_name = option_arg; m_sym_ctx_specified = true; break; case 's': m_module_name = option_arg; m_sym_ctx_specified = true; break; case 't' : { m_thread_id = StringConvert::ToUInt64(option_arg, LLDB_INVALID_THREAD_ID, 0); if (m_thread_id == LLDB_INVALID_THREAD_ID) error.SetErrorStringWithFormat ("invalid thread id string '%s'", option_arg); m_thread_specified = true; } break; case 'T': m_thread_name = option_arg; m_thread_specified = true; break; case 'q': m_queue_name = option_arg; m_thread_specified = true; break; case 'x': { m_thread_index = StringConvert::ToUInt32(option_arg, UINT32_MAX, 0); if (m_thread_id == UINT32_MAX) error.SetErrorStringWithFormat ("invalid thread index string '%s'", option_arg); m_thread_specified = true; } break; case 'o': m_use_one_liner = true; m_one_liner = option_arg; break; default: error.SetErrorStringWithFormat ("unrecognized option %c.", short_option); break; } return error; } void OptionParsingStarting () override { m_class_name.clear(); m_function_name.clear(); m_line_start = 0; m_line_end = UINT_MAX; m_file_name.clear(); m_module_name.clear(); m_func_name_type_mask = eFunctionNameTypeAuto; m_thread_id = LLDB_INVALID_THREAD_ID; m_thread_index = UINT32_MAX; m_thread_name.clear(); m_queue_name.clear(); m_no_inlines = false; m_sym_ctx_specified = false; m_thread_specified = false; m_use_one_liner = false; m_one_liner.clear(); } static OptionDefinition g_option_table[]; std::string m_class_name; std::string m_function_name; uint32_t m_line_start; uint32_t m_line_end; std::string m_file_name; std::string m_module_name; uint32_t m_func_name_type_mask; // A pick from lldb::FunctionNameType. lldb::tid_t m_thread_id; uint32_t m_thread_index; std::string m_thread_name; std::string m_queue_name; bool m_sym_ctx_specified; bool m_no_inlines; bool m_thread_specified; // Instance variables to hold the values for one_liner options. bool m_use_one_liner; std::string m_one_liner; }; Options * GetOptions () override { return &m_options; } CommandObjectTargetStopHookAdd (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target stop-hook add", "Add a hook to be executed when the target stops.", "target stop-hook add"), IOHandlerDelegateMultiline ("DONE", IOHandlerDelegate::Completion::LLDBCommand), m_options (interpreter) { } ~CommandObjectTargetStopHookAdd () override { } protected: void IOHandlerActivated (IOHandler &io_handler) override { StreamFileSP output_sp(io_handler.GetOutputStreamFile()); if (output_sp) { output_sp->PutCString("Enter your stop hook command(s). Type 'DONE' to end.\n"); output_sp->Flush(); } } void IOHandlerInputComplete (IOHandler &io_handler, std::string &line) override { if (m_stop_hook_sp) { if (line.empty()) { StreamFileSP error_sp(io_handler.GetErrorStreamFile()); if (error_sp) { error_sp->Printf("error: stop hook #%" PRIu64 " aborted, no commands.\n", m_stop_hook_sp->GetID()); error_sp->Flush(); } Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) target->RemoveStopHookByID(m_stop_hook_sp->GetID()); } else { m_stop_hook_sp->GetCommandPointer()->SplitIntoLines(line); StreamFileSP output_sp(io_handler.GetOutputStreamFile()); if (output_sp) { output_sp->Printf("Stop hook #%" PRIu64 " added.\n", m_stop_hook_sp->GetID()); output_sp->Flush(); } } m_stop_hook_sp.reset(); } io_handler.SetIsDone(true); } bool DoExecute (Args& command, CommandReturnObject &result) override { m_stop_hook_sp.reset(); Target *target = GetSelectedOrDummyTarget(); if (target) { Target::StopHookSP new_hook_sp = target->CreateStopHook(); // First step, make the specifier. std::unique_ptr specifier_ap; if (m_options.m_sym_ctx_specified) { specifier_ap.reset(new SymbolContextSpecifier(m_interpreter.GetDebugger().GetSelectedTarget())); if (!m_options.m_module_name.empty()) { specifier_ap->AddSpecification (m_options.m_module_name.c_str(), SymbolContextSpecifier::eModuleSpecified); } if (!m_options.m_class_name.empty()) { specifier_ap->AddSpecification (m_options.m_class_name.c_str(), SymbolContextSpecifier::eClassOrNamespaceSpecified); } if (!m_options.m_file_name.empty()) { specifier_ap->AddSpecification (m_options.m_file_name.c_str(), SymbolContextSpecifier::eFileSpecified); } if (m_options.m_line_start != 0) { specifier_ap->AddLineSpecification (m_options.m_line_start, SymbolContextSpecifier::eLineStartSpecified); } if (m_options.m_line_end != UINT_MAX) { specifier_ap->AddLineSpecification (m_options.m_line_end, SymbolContextSpecifier::eLineEndSpecified); } if (!m_options.m_function_name.empty()) { specifier_ap->AddSpecification (m_options.m_function_name.c_str(), SymbolContextSpecifier::eFunctionSpecified); } } if (specifier_ap.get()) new_hook_sp->SetSpecifier (specifier_ap.release()); // Next see if any of the thread options have been entered: if (m_options.m_thread_specified) { ThreadSpec *thread_spec = new ThreadSpec(); if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID) { thread_spec->SetTID (m_options.m_thread_id); } if (m_options.m_thread_index != UINT32_MAX) thread_spec->SetIndex (m_options.m_thread_index); if (!m_options.m_thread_name.empty()) thread_spec->SetName (m_options.m_thread_name.c_str()); if (!m_options.m_queue_name.empty()) thread_spec->SetQueueName (m_options.m_queue_name.c_str()); new_hook_sp->SetThreadSpecifier (thread_spec); } if (m_options.m_use_one_liner) { // Use one-liner. new_hook_sp->GetCommandPointer()->AppendString (m_options.m_one_liner.c_str()); result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n", new_hook_sp->GetID()); } else { m_stop_hook_sp = new_hook_sp; m_interpreter.GetLLDBCommandsFromIOHandler ("> ", // Prompt *this, // IOHandlerDelegate true, // Run IOHandler in async mode NULL); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions } result.SetStatus (eReturnStatusSuccessFinishNoResult); } else { result.AppendError ("invalid target\n"); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } private: CommandOptions m_options; Target::StopHookSP m_stop_hook_sp; }; OptionDefinition CommandObjectTargetStopHookAdd::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "one-liner", 'o', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeOneLiner, "Specify a one-line breakpoint command inline. Be sure to surround it with quotes." }, { LLDB_OPT_SET_ALL, false, "shlib", 's', OptionParser::eRequiredArgument, NULL, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Set the module within which the stop-hook is to be run."}, { LLDB_OPT_SET_ALL, false, "thread-index", 'x', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeThreadIndex, "The stop hook is run only for the thread whose index matches this argument."}, { LLDB_OPT_SET_ALL, false, "thread-id", 't', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeThreadID, "The stop hook is run only for the thread whose TID matches this argument."}, { LLDB_OPT_SET_ALL, false, "thread-name", 'T', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeThreadName, "The stop hook is run only for the thread whose thread name matches this argument."}, { LLDB_OPT_SET_ALL, false, "queue-name", 'q', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeQueueName, "The stop hook is run only for threads in the queue whose name is given by this argument."}, { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, NULL, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "Specify the source file within which the stop-hook is to be run." }, { LLDB_OPT_SET_1, false, "start-line", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, "Set the start of the line range for which the stop-hook is to be run."}, { LLDB_OPT_SET_1, false, "end-line", 'e', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, "Set the end of the line range for which the stop-hook is to be run."}, { LLDB_OPT_SET_2, false, "classname", 'c', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeClassName, "Specify the class within which the stop-hook is to be run." }, { LLDB_OPT_SET_3, false, "name", 'n', OptionParser::eRequiredArgument, NULL, NULL, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, "Set the function name within which the stop hook will be run." }, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; #pragma mark CommandObjectTargetStopHookDelete //------------------------------------------------------------------------- // CommandObjectTargetStopHookDelete //------------------------------------------------------------------------- class CommandObjectTargetStopHookDelete : public CommandObjectParsed { public: CommandObjectTargetStopHookDelete (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target stop-hook delete", "Delete a stop-hook.", "target stop-hook delete []") { } ~CommandObjectTargetStopHookDelete () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = GetSelectedOrDummyTarget(); if (target) { // FIXME: see if we can use the breakpoint id style parser? size_t num_args = command.GetArgumentCount(); if (num_args == 0) { if (!m_interpreter.Confirm ("Delete all stop hooks?", true)) { result.SetStatus (eReturnStatusFailed); return false; } else { target->RemoveAllStopHooks(); } } else { bool success; for (size_t i = 0; i < num_args; i++) { lldb::user_id_t user_id = StringConvert::ToUInt32 (command.GetArgumentAtIndex(i), 0, 0, &success); if (!success) { result.AppendErrorWithFormat ("invalid stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } success = target->RemoveStopHookByID (user_id); if (!success) { result.AppendErrorWithFormat ("unknown stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } } } result.SetStatus (eReturnStatusSuccessFinishNoResult); } else { result.AppendError ("invalid target\n"); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetStopHookEnableDisable //------------------------------------------------------------------------- // CommandObjectTargetStopHookEnableDisable //------------------------------------------------------------------------- class CommandObjectTargetStopHookEnableDisable : public CommandObjectParsed { public: CommandObjectTargetStopHookEnableDisable (CommandInterpreter &interpreter, bool enable, const char *name, const char *help, const char *syntax) : CommandObjectParsed (interpreter, name, help, syntax), m_enable (enable) { } ~CommandObjectTargetStopHookEnableDisable () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = GetSelectedOrDummyTarget(); if (target) { // FIXME: see if we can use the breakpoint id style parser? size_t num_args = command.GetArgumentCount(); bool success; if (num_args == 0) { target->SetAllStopHooksActiveState (m_enable); } else { for (size_t i = 0; i < num_args; i++) { lldb::user_id_t user_id = StringConvert::ToUInt32 (command.GetArgumentAtIndex(i), 0, 0, &success); if (!success) { result.AppendErrorWithFormat ("invalid stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } success = target->SetStopHookActiveStateByID (user_id, m_enable); if (!success) { result.AppendErrorWithFormat ("unknown stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } } } result.SetStatus (eReturnStatusSuccessFinishNoResult); } else { result.AppendError ("invalid target\n"); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } private: bool m_enable; }; #pragma mark CommandObjectTargetStopHookList //------------------------------------------------------------------------- // CommandObjectTargetStopHookList //------------------------------------------------------------------------- class CommandObjectTargetStopHookList : public CommandObjectParsed { public: CommandObjectTargetStopHookList (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "target stop-hook list", "List all stop-hooks.", "target stop-hook list []") { } ~CommandObjectTargetStopHookList () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { Target *target = GetSelectedOrDummyTarget(); if (!target) { result.AppendError ("invalid target\n"); result.SetStatus (eReturnStatusFailed); return result.Succeeded(); } size_t num_hooks = target->GetNumStopHooks (); if (num_hooks == 0) { result.GetOutputStream().PutCString ("No stop hooks.\n"); } else { for (size_t i = 0; i < num_hooks; i++) { Target::StopHookSP this_hook = target->GetStopHookAtIndex (i); if (i > 0) result.GetOutputStream().PutCString ("\n"); this_hook->GetDescription (&(result.GetOutputStream()), eDescriptionLevelFull); } } result.SetStatus (eReturnStatusSuccessFinishResult); return result.Succeeded(); } }; #pragma mark CommandObjectMultiwordTargetStopHooks //------------------------------------------------------------------------- // CommandObjectMultiwordTargetStopHooks //------------------------------------------------------------------------- class CommandObjectMultiwordTargetStopHooks : public CommandObjectMultiword { public: CommandObjectMultiwordTargetStopHooks (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "target stop-hook", "A set of commands for operating on debugger target stop-hooks.", "target stop-hook []") { LoadSubCommand ("add", CommandObjectSP (new CommandObjectTargetStopHookAdd (interpreter))); LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTargetStopHookDelete (interpreter))); LoadSubCommand ("disable", CommandObjectSP (new CommandObjectTargetStopHookEnableDisable (interpreter, false, "target stop-hook disable []", "Disable a stop-hook.", "target stop-hook disable"))); LoadSubCommand ("enable", CommandObjectSP (new CommandObjectTargetStopHookEnableDisable (interpreter, true, "target stop-hook enable []", "Enable a stop-hook.", "target stop-hook enable"))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectTargetStopHookList (interpreter))); } ~CommandObjectMultiwordTargetStopHooks() override { } }; #pragma mark CommandObjectMultiwordTarget //------------------------------------------------------------------------- // CommandObjectMultiwordTarget //------------------------------------------------------------------------- CommandObjectMultiwordTarget::CommandObjectMultiwordTarget (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "target", "A set of commands for operating on debugger targets.", "target []") { LoadSubCommand ("create", CommandObjectSP (new CommandObjectTargetCreate (interpreter))); LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTargetDelete (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectTargetList (interpreter))); LoadSubCommand ("select", CommandObjectSP (new CommandObjectTargetSelect (interpreter))); LoadSubCommand ("stop-hook", CommandObjectSP (new CommandObjectMultiwordTargetStopHooks (interpreter))); LoadSubCommand ("modules", CommandObjectSP (new CommandObjectTargetModules (interpreter))); LoadSubCommand ("symbols", CommandObjectSP (new CommandObjectTargetSymbols (interpreter))); LoadSubCommand ("variable", CommandObjectSP (new CommandObjectTargetVariable (interpreter))); } CommandObjectMultiwordTarget::~CommandObjectMultiwordTarget () { } Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectType.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectType.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectType.cpp (revision 293283) @@ -1,3724 +1,3719 @@ //===-- CommandObjectType.cpp ----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "CommandObjectType.h" // C Includes #include // C++ Includes #include #include "llvm/ADT/StringRef.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/IOHandler.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/State.h" #include "lldb/Core/StringList.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/OptionGroupFormat.h" #include "lldb/Interpreter/OptionValueBoolean.h" #include "lldb/Interpreter/OptionValueLanguage.h" #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Target/Language.h" #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadList.h" using namespace lldb; using namespace lldb_private; class ScriptAddOptions { public: TypeSummaryImpl::Flags m_flags; StringList m_target_types; bool m_regex; ConstString m_name; std::string m_category; ScriptAddOptions(const TypeSummaryImpl::Flags& flags, bool regx, const ConstString& name, std::string catg) : m_flags(flags), m_regex(regx), m_name(name), m_category(catg) { } typedef std::shared_ptr SharedPointer; }; class SynthAddOptions { public: bool m_skip_pointers; bool m_skip_references; bool m_cascade; bool m_regex; StringList m_target_types; std::string m_category; SynthAddOptions(bool sptr, bool sref, bool casc, bool regx, std::string catg) : m_skip_pointers(sptr), m_skip_references(sref), m_cascade(casc), m_regex(regx), m_target_types(), m_category(catg) { } typedef std::shared_ptr SharedPointer; }; static bool WarnOnPotentialUnquotedUnsignedType (Args& command, CommandReturnObject &result) { for (unsigned idx = 0; idx < command.GetArgumentCount(); idx++) { const char* arg = command.GetArgumentAtIndex(idx); if (idx+1 < command.GetArgumentCount()) { if (arg && 0 == strcmp(arg,"unsigned")) { const char* next = command.GetArgumentAtIndex(idx+1); if (next && (0 == strcmp(next, "int") || 0 == strcmp(next, "short") || 0 == strcmp(next, "char") || 0 == strcmp(next, "long"))) { result.AppendWarningWithFormat("%s %s being treated as two types. if you meant the combined type name use quotes, as in \"%s %s\"\n", arg,next,arg,next); return true; } } } } return false; } class CommandObjectTypeSummaryAdd : public CommandObjectParsed, public IOHandlerDelegateMultiline { private: class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options (interpreter) { } ~CommandOptions () override {} Error SetOptionValue (uint32_t option_idx, const char *option_arg) override; void OptionParsingStarting () override; const OptionDefinition* GetDefinitions () override { return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. TypeSummaryImpl::Flags m_flags; bool m_regex; std::string m_format_string; ConstString m_name; std::string m_python_script; std::string m_python_function; bool m_is_add_script; std::string m_category; }; CommandOptions m_options; Options * GetOptions () override { return &m_options; } bool Execute_ScriptSummary (Args& command, CommandReturnObject &result); bool Execute_StringSummary (Args& command, CommandReturnObject &result); public: enum SummaryFormatType { eRegularSummary, eRegexSummary, eNamedSummary }; CommandObjectTypeSummaryAdd (CommandInterpreter &interpreter); ~CommandObjectTypeSummaryAdd () override { } void IOHandlerActivated (IOHandler &io_handler) override { static const char *g_summary_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" "def function (valobj,internal_dict):\n" " \"\"\"valobj: an SBValue which you want to provide a summary for\n" " internal_dict: an LLDB support object not to be used\"\"\"\n"; StreamFileSP output_sp(io_handler.GetOutputStreamFile()); if (output_sp) { output_sp->PutCString(g_summary_addreader_instructions); output_sp->Flush(); } } void IOHandlerInputComplete (IOHandler &io_handler, std::string &data) override { StreamFileSP error_sp = io_handler.GetErrorStreamFile(); #ifndef LLDB_DISABLE_PYTHON ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); if (interpreter) { StringList lines; lines.SplitIntoLines(data); if (lines.GetSize() > 0) { ScriptAddOptions *options_ptr = ((ScriptAddOptions*)io_handler.GetUserData()); if (options_ptr) { ScriptAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); if (interpreter) { std::string funct_name_str; if (interpreter->GenerateTypeScriptFunction (lines, funct_name_str)) { if (funct_name_str.empty()) { error_sp->Printf ("unable to obtain a valid function name from the script interpreter.\n"); error_sp->Flush(); } else { // now I have a valid function name, let's add this as script for every type in the list TypeSummaryImplSP script_format; script_format.reset(new ScriptSummaryFormat(options->m_flags, funct_name_str.c_str(), lines.CopyList(" ").c_str())); Error error; for (size_t i = 0; i < options->m_target_types.GetSize(); i++) { const char *type_name = options->m_target_types.GetStringAtIndex(i); CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), script_format, (options->m_regex ? CommandObjectTypeSummaryAdd::eRegexSummary : CommandObjectTypeSummaryAdd::eRegularSummary), options->m_category, &error); if (error.Fail()) { error_sp->Printf ("error: %s", error.AsCString()); error_sp->Flush(); } } if (options->m_name) { CommandObjectTypeSummaryAdd::AddSummary (options->m_name, script_format, CommandObjectTypeSummaryAdd::eNamedSummary, options->m_category, &error); if (error.Fail()) { CommandObjectTypeSummaryAdd::AddSummary (options->m_name, script_format, CommandObjectTypeSummaryAdd::eNamedSummary, options->m_category, &error); if (error.Fail()) { error_sp->Printf ("error: %s", error.AsCString()); error_sp->Flush(); } } else { error_sp->Printf ("error: %s", error.AsCString()); error_sp->Flush(); } } else { if (error.AsCString()) { error_sp->Printf ("error: %s", error.AsCString()); error_sp->Flush(); } } } } else { error_sp->Printf ("error: unable to generate a function.\n"); error_sp->Flush(); } } else { error_sp->Printf ("error: no script interpreter.\n"); error_sp->Flush(); } } else { error_sp->Printf ("error: internal synchronization information missing or invalid.\n"); error_sp->Flush(); } } else { error_sp->Printf ("error: empty function, didn't add python command.\n"); error_sp->Flush(); } } else { error_sp->Printf ("error: script interpreter missing, didn't add python command.\n"); error_sp->Flush(); } #endif // #ifndef LLDB_DISABLE_PYTHON io_handler.SetIsDone(true); } static bool AddSummary(ConstString type_name, lldb::TypeSummaryImplSP entry, SummaryFormatType type, std::string category, Error* error = NULL); protected: bool DoExecute (Args& command, CommandReturnObject &result) override; }; static const char *g_synth_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" "You must define a Python class with these methods:\n" " def __init__(self, valobj, dict):\n" " def num_children(self):\n" " def get_child_at_index(self, index):\n" " def get_child_index(self, name):\n" " def update(self):\n" " '''Optional'''\n" "class synthProvider:\n"; class CommandObjectTypeSynthAdd : public CommandObjectParsed, public IOHandlerDelegateMultiline { private: class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options (interpreter) { } ~CommandOptions () override {} Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; bool success; switch (short_option) { case 'C': m_cascade = Args::StringToBoolean(option_arg, true, &success); if (!success) error.SetErrorStringWithFormat("invalid value for cascade: %s", option_arg); break; case 'P': handwrite_python = true; break; case 'l': m_class_name = std::string(option_arg); is_class_based = true; break; case 'p': m_skip_pointers = true; break; case 'r': m_skip_references = true; break; case 'w': m_category = std::string(option_arg); break; case 'x': m_regex = true; break; default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting () override { m_cascade = true; m_class_name = ""; m_skip_pointers = false; m_skip_references = false; m_category = "default"; is_class_based = false; handwrite_python = false; m_regex = false; } const OptionDefinition* GetDefinitions () override { return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. bool m_cascade; bool m_skip_references; bool m_skip_pointers; std::string m_class_name; bool m_input_python; std::string m_category; bool is_class_based; bool handwrite_python; bool m_regex; }; CommandOptions m_options; Options * GetOptions () override { return &m_options; } bool Execute_HandwritePython (Args& command, CommandReturnObject &result); bool Execute_PythonClass (Args& command, CommandReturnObject &result); protected: bool DoExecute (Args& command, CommandReturnObject &result) override { WarnOnPotentialUnquotedUnsignedType(command, result); if (m_options.handwrite_python) return Execute_HandwritePython(command, result); else if (m_options.is_class_based) return Execute_PythonClass(command, result); else { result.AppendError("must either provide a children list, a Python class name, or use -P and type a Python class line-by-line"); result.SetStatus(eReturnStatusFailed); return false; } } void IOHandlerActivated (IOHandler &io_handler) override { StreamFileSP output_sp(io_handler.GetOutputStreamFile()); if (output_sp) { output_sp->PutCString(g_synth_addreader_instructions); output_sp->Flush(); } } void IOHandlerInputComplete (IOHandler &io_handler, std::string &data) override { StreamFileSP error_sp = io_handler.GetErrorStreamFile(); #ifndef LLDB_DISABLE_PYTHON ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); if (interpreter) { StringList lines; lines.SplitIntoLines(data); if (lines.GetSize() > 0) { SynthAddOptions *options_ptr = ((SynthAddOptions*)io_handler.GetUserData()); if (options_ptr) { SynthAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); if (interpreter) { std::string class_name_str; if (interpreter->GenerateTypeSynthClass (lines, class_name_str)) { if (class_name_str.empty()) { error_sp->Printf ("error: unable to obtain a proper name for the class.\n"); error_sp->Flush(); } else { // everything should be fine now, let's add the synth provider class SyntheticChildrenSP synth_provider; synth_provider.reset(new ScriptedSyntheticChildren(SyntheticChildren::Flags().SetCascades(options->m_cascade). SetSkipPointers(options->m_skip_pointers). SetSkipReferences(options->m_skip_references), class_name_str.c_str())); lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(options->m_category.c_str()), category); Error error; for (size_t i = 0; i < options->m_target_types.GetSize(); i++) { const char *type_name = options->m_target_types.GetStringAtIndex(i); ConstString const_type_name(type_name); if (const_type_name) { if (!CommandObjectTypeSynthAdd::AddSynth(const_type_name, synth_provider, options->m_regex ? CommandObjectTypeSynthAdd::eRegexSynth : CommandObjectTypeSynthAdd::eRegularSynth, options->m_category, &error)) { error_sp->Printf("error: %s\n", error.AsCString()); error_sp->Flush(); break; } } else { error_sp->Printf ("error: invalid type name.\n"); error_sp->Flush(); break; } } } } else { error_sp->Printf ("error: unable to generate a class.\n"); error_sp->Flush(); } } else { error_sp->Printf ("error: no script interpreter.\n"); error_sp->Flush(); } } else { error_sp->Printf ("error: internal synchronization data missing.\n"); error_sp->Flush(); } } else { error_sp->Printf ("error: empty function, didn't add python command.\n"); error_sp->Flush(); } } else { error_sp->Printf ("error: script interpreter missing, didn't add python command.\n"); error_sp->Flush(); } #endif // #ifndef LLDB_DISABLE_PYTHON io_handler.SetIsDone(true); } public: enum SynthFormatType { eRegularSynth, eRegexSynth }; CommandObjectTypeSynthAdd (CommandInterpreter &interpreter); ~CommandObjectTypeSynthAdd () override { } static bool AddSynth(ConstString type_name, lldb::SyntheticChildrenSP entry, SynthFormatType type, std::string category_name, Error* error); }; //------------------------------------------------------------------------- // CommandObjectTypeFormatAdd //------------------------------------------------------------------------- class CommandObjectTypeFormatAdd : public CommandObjectParsed { private: class CommandOptions : public OptionGroup { public: CommandOptions () : OptionGroup() { } ~CommandOptions () override { } uint32_t GetNumDefinitions () override; const OptionDefinition* GetDefinitions () override { return g_option_table; } void OptionParsingStarting (CommandInterpreter &interpreter) override { m_cascade = true; m_skip_pointers = false; m_skip_references = false; m_regex = false; m_category.assign("default"); m_custom_type_name.clear(); } Error SetOptionValue (CommandInterpreter &interpreter, uint32_t option_idx, const char *option_value) override { Error error; const int short_option = g_option_table[option_idx].short_option; bool success; switch (short_option) { case 'C': m_cascade = Args::StringToBoolean(option_value, true, &success); if (!success) error.SetErrorStringWithFormat("invalid value for cascade: %s", option_value); break; case 'p': m_skip_pointers = true; break; case 'w': m_category.assign(option_value); break; case 'r': m_skip_references = true; break; case 'x': m_regex = true; break; case 't': m_custom_type_name.assign(option_value); break; default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; } return error; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. bool m_cascade; bool m_skip_references; bool m_skip_pointers; bool m_regex; std::string m_category; std::string m_custom_type_name; }; OptionGroupOptions m_option_group; OptionGroupFormat m_format_options; CommandOptions m_command_options; Options * GetOptions () override { return &m_option_group; } public: CommandObjectTypeFormatAdd (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "type format add", "Add a new formatting style for a type.", NULL), m_option_group (interpreter), m_format_options (eFormatInvalid), m_command_options () { CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatPlus; type_arg.push_back (type_style_arg); m_arguments.push_back (type_arg); SetHelpLong( R"( The following examples of 'type format add' refer to this code snippet for context: typedef int Aint; typedef float Afloat; typedef Aint Bint; typedef Afloat Bfloat; Aint ix = 5; Bint iy = 5; Afloat fx = 3.14; BFloat fy = 3.14; Adding default formatting: (lldb) type format add -f hex AInt (lldb) frame variable iy )" " Produces hexidecimal display of iy, because no formatter is available for Bint and \ the one for Aint is used instead." R"( To prevent this use the cascade option '-C no' to prevent evaluation of typedef chains: (lldb) type format add -f hex -C no AInt Similar reasoning applies to this: (lldb) type format add -f hex -C no float -p )" " All float values and float references are now formatted as hexadecimal, but not \ pointers to floats. Nor will it change the default display for Afloat and Bfloat objects." ); // Add the "--format" to all options groups m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT, LLDB_OPT_SET_1); m_option_group.Append (&m_command_options); m_option_group.Finalize(); } ~CommandObjectTypeFormatAdd () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc < 1) { result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } const Format format = m_format_options.GetFormat(); if (format == eFormatInvalid && m_command_options.m_custom_type_name.empty()) { result.AppendErrorWithFormat ("%s needs a valid format.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } TypeFormatImplSP entry; if (m_command_options.m_custom_type_name.empty()) entry.reset(new TypeFormatImpl_Format(format, TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade). SetSkipPointers(m_command_options.m_skip_pointers). SetSkipReferences(m_command_options.m_skip_references))); else entry.reset(new TypeFormatImpl_EnumType(ConstString(m_command_options.m_custom_type_name.c_str()), TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade). SetSkipPointers(m_command_options.m_skip_pointers). SetSkipReferences(m_command_options.m_skip_references))); // now I have a valid format, let's add it to every type TypeCategoryImplSP category_sp; DataVisualization::Categories::GetCategory(ConstString(m_command_options.m_category), category_sp); if (!category_sp) return false; WarnOnPotentialUnquotedUnsignedType(command, result); for (size_t i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); ConstString typeCS(typeA); if (typeCS) { if (m_command_options.m_regex) { RegularExpressionSP typeRX(new RegularExpression()); if (!typeRX->Compile(typeCS.GetCString())) { result.AppendError("regex format error (maybe this is not really a regex?)"); result.SetStatus(eReturnStatusFailed); return false; } category_sp->GetRegexTypeSummariesContainer()->Delete(typeCS); category_sp->GetRegexTypeFormatsContainer()->Add(typeRX, entry); } else category_sp->GetTypeFormatsContainer()->Add(typeCS, entry); } else { result.AppendError("empty typenames not allowed"); result.SetStatus(eReturnStatusFailed); return false; } } result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } }; OptionDefinition CommandObjectTypeFormatAdd::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, { LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, { LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, { LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."}, { LLDB_OPT_SET_2, false, "type", 't', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeName, "Format variables as if they were of this type."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; uint32_t CommandObjectTypeFormatAdd::CommandOptions::GetNumDefinitions () { return sizeof(g_option_table) / sizeof (OptionDefinition); } class CommandObjectTypeFormatterDelete : public CommandObjectParsed { protected: class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options (interpreter) { } ~CommandOptions () override {} Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'a': m_delete_all = true; break; case 'w': m_category = std::string(option_arg); break; case 'l': m_language = Language::GetLanguageTypeFromString(option_arg); break; default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting () override { m_delete_all = false; m_category = "default"; m_language = lldb::eLanguageTypeUnknown; } const OptionDefinition* GetDefinitions () override { return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. bool m_delete_all; std::string m_category; lldb::LanguageType m_language; }; CommandOptions m_options; uint32_t m_formatter_kind_mask; Options * GetOptions () override { return &m_options; } public: CommandObjectTypeFormatterDelete (CommandInterpreter &interpreter, uint32_t formatter_kind_mask, const char* name, const char* help) : CommandObjectParsed (interpreter, name, help, NULL), m_options(interpreter), m_formatter_kind_mask(formatter_kind_mask) { CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatPlain; type_arg.push_back (type_style_arg); m_arguments.push_back (type_arg); } ~CommandObjectTypeFormatterDelete () override = default; protected: virtual bool FormatterSpecificDeletion (ConstString typeCS) { return false; } bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc != 1) { result.AppendErrorWithFormat ("%s takes 1 arg.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } const char* typeA = command.GetArgumentAtIndex(0); ConstString typeCS(typeA); if (!typeCS) { result.AppendError("empty typenames not allowed"); result.SetStatus(eReturnStatusFailed); return false; } if (m_options.m_delete_all) { DataVisualization::Categories::ForEach( [this, typeCS] (const lldb::TypeCategoryImplSP& category_sp) -> bool { category_sp->Delete(typeCS, m_formatter_kind_mask); return true; }); result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } bool delete_category = false; bool extra_deletion = false; if (m_options.m_language != lldb::eLanguageTypeUnknown) { lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(m_options.m_language, category); if (category) delete_category = category->Delete(typeCS, m_formatter_kind_mask); extra_deletion = FormatterSpecificDeletion(typeCS); } else { lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); if (category) delete_category = category->Delete(typeCS, m_formatter_kind_mask); extra_deletion = FormatterSpecificDeletion(typeCS); } if (delete_category || extra_deletion) { result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } else { result.AppendErrorWithFormat ("no custom formatter for %s.\n", typeA); result.SetStatus(eReturnStatusFailed); return false; } } }; OptionDefinition CommandObjectTypeFormatterDelete::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_1, false, "all", 'a', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Delete from every category."}, { LLDB_OPT_SET_2, false, "category", 'w', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeName, "Delete from given category."}, { LLDB_OPT_SET_3, false, "language", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLanguage, "Delete from given language's category."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; class CommandObjectTypeFormatterClear : public CommandObjectParsed { private: class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options (interpreter) { } ~CommandOptions () override {} Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'a': m_delete_all = true; break; default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting () override { m_delete_all = false; } const OptionDefinition* GetDefinitions () override { return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. bool m_delete_all; }; CommandOptions m_options; uint32_t m_formatter_kind_mask; Options * GetOptions () override { return &m_options; } public: CommandObjectTypeFormatterClear (CommandInterpreter &interpreter, uint32_t formatter_kind_mask, const char* name, const char* help) : CommandObjectParsed (interpreter, name, help, NULL), m_options(interpreter), m_formatter_kind_mask(formatter_kind_mask) { } ~CommandObjectTypeFormatterClear () override { } protected: virtual void FormatterSpecificDeletion () { } bool DoExecute (Args& command, CommandReturnObject &result) override { if (m_options.m_delete_all) { DataVisualization::Categories::ForEach( [this] (const TypeCategoryImplSP& category_sp) -> bool { category_sp->Clear(m_formatter_kind_mask); return true; }); } else { lldb::TypeCategoryImplSP category; if (command.GetArgumentCount() > 0) { const char* cat_name = command.GetArgumentAtIndex(0); ConstString cat_nameCS(cat_name); DataVisualization::Categories::GetCategory(cat_nameCS, category); } else { DataVisualization::Categories::GetCategory(ConstString(NULL), category); } category->Clear(m_formatter_kind_mask); } FormatterSpecificDeletion(); result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } }; OptionDefinition CommandObjectTypeFormatterClear::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "all", 'a', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Clear every category."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; //------------------------------------------------------------------------- // CommandObjectTypeFormatDelete //------------------------------------------------------------------------- class CommandObjectTypeFormatDelete : public CommandObjectTypeFormatterDelete { public: CommandObjectTypeFormatDelete (CommandInterpreter &interpreter) : CommandObjectTypeFormatterDelete (interpreter, eFormatCategoryItemValue | eFormatCategoryItemRegexValue, "type format delete", "Delete an existing formatting style for a type.") { } ~CommandObjectTypeFormatDelete () override { } }; //------------------------------------------------------------------------- // CommandObjectTypeFormatClear //------------------------------------------------------------------------- class CommandObjectTypeFormatClear : public CommandObjectTypeFormatterClear { public: CommandObjectTypeFormatClear (CommandInterpreter &interpreter) : CommandObjectTypeFormatterClear (interpreter, eFormatCategoryItemValue | eFormatCategoryItemRegexValue, "type format clear", "Delete all existing format styles.") { } }; template class CommandObjectTypeFormatterList : public CommandObjectParsed { typedef typename FormatterType::SharedPointer FormatterSharedPointer; class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options (interpreter), m_category_regex("",""), m_category_language(lldb::eLanguageTypeUnknown, lldb::eLanguageTypeUnknown) { } ~CommandOptions () override {} Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'w': m_category_regex.SetCurrentValue(option_arg); break; case 'l': error = m_category_language.SetValueFromString(option_arg); break; default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting () override { m_category_regex.Clear(); m_category_language.Clear(); } const OptionDefinition* GetDefinitions () override { static OptionDefinition g_option_table[] = { { LLDB_OPT_SET_1, false, "category-regex", 'w', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeName, "Only show categories matching this filter."}, { LLDB_OPT_SET_2, false, "language", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLanguage, "Only show the category for a specific language."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. OptionValueString m_category_regex; OptionValueLanguage m_category_language; }; CommandOptions m_options; Options * GetOptions () override { return &m_options; } public: CommandObjectTypeFormatterList (CommandInterpreter &interpreter, const char* name, const char* help) : CommandObjectParsed (interpreter, name, help, NULL), m_options(interpreter) { CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatOptional; type_arg.push_back (type_style_arg); m_arguments.push_back (type_arg); } ~CommandObjectTypeFormatterList () override { } protected: virtual void FormatterSpecificList (CommandReturnObject &result) { } bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); std::unique_ptr category_regex; std::unique_ptr formatter_regex; if (m_options.m_category_regex.OptionWasSet()) { category_regex.reset(new RegularExpression()); if (!category_regex->Compile(m_options.m_category_regex.GetCurrentValue())) { result.AppendErrorWithFormat("syntax error in category regular expression '%s'", m_options.m_category_regex.GetCurrentValue()); result.SetStatus(eReturnStatusFailed); return false; } } if (argc == 1) { const char* arg = command.GetArgumentAtIndex(1); formatter_regex.reset(new RegularExpression()); if (!formatter_regex->Compile(arg)) { result.AppendErrorWithFormat("syntax error in regular expression '%s'", arg); result.SetStatus(eReturnStatusFailed); return false; } } auto category_closure = [&result, &formatter_regex] (const lldb::TypeCategoryImplSP& category) -> void { result.GetOutputStream().Printf("-----------------------\nCategory: %s\n-----------------------\n", category->GetName()); - - typedef const std::shared_ptr Bar; - typedef std::function Func1Type; - typedef std::function Func2Type; - TypeCategoryImpl::ForEachCallbacks foreach; foreach.SetExact([&result, &formatter_regex] (ConstString name, const FormatterSharedPointer& format_sp) -> bool { if (formatter_regex) { bool escape = true; if (0 == strcmp(name.AsCString(), formatter_regex->GetText())) { escape = false; } else if (formatter_regex->Execute(name.AsCString())) { escape = false; } if (escape) return true; } result.GetOutputStream().Printf ("%s: %s\n", name.AsCString(), format_sp->GetDescription().c_str()); return true; }); foreach.SetWithRegex( [&result, &formatter_regex] (RegularExpressionSP regex_sp, const FormatterSharedPointer& format_sp) -> bool { if (formatter_regex) { bool escape = true; if (0 == strcmp(regex_sp->GetText(), formatter_regex->GetText())) { escape = false; } else if (formatter_regex->Execute(regex_sp->GetText())) { escape = false; } if (escape) return true; } result.GetOutputStream().Printf ("%s: %s\n", regex_sp->GetText(), format_sp->GetDescription().c_str()); return true; }); category->ForEach(foreach); }; if (m_options.m_category_language.OptionWasSet()) { lldb::TypeCategoryImplSP category_sp; DataVisualization::Categories::GetCategory(m_options.m_category_language.GetCurrentValue(), category_sp); if (category_sp) category_closure(category_sp); } else { DataVisualization::Categories::ForEach( [this, &command, &result, &category_regex, &formatter_regex, &category_closure] (const lldb::TypeCategoryImplSP& category) -> bool { if (category_regex) { bool escape = true; if (0 == strcmp(category->GetName(), category_regex->GetText())) { escape = false; } else if (category_regex->Execute(category->GetName())) { escape = false; } if (escape) return true; } category_closure(category); return true; }); FormatterSpecificList(result); } result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectTypeFormatList //------------------------------------------------------------------------- class CommandObjectTypeFormatList : public CommandObjectTypeFormatterList { public: CommandObjectTypeFormatList (CommandInterpreter &interpreter) : CommandObjectTypeFormatterList(interpreter, "type format list", "Show a list of current formats.") { } }; #ifndef LLDB_DISABLE_PYTHON //------------------------------------------------------------------------- // CommandObjectTypeSummaryAdd //------------------------------------------------------------------------- #endif // #ifndef LLDB_DISABLE_PYTHON Error CommandObjectTypeSummaryAdd::CommandOptions::SetOptionValue (uint32_t option_idx, const char *option_arg) { Error error; const int short_option = m_getopt_table[option_idx].val; bool success; switch (short_option) { case 'C': m_flags.SetCascades(Args::StringToBoolean(option_arg, true, &success)); if (!success) error.SetErrorStringWithFormat("invalid value for cascade: %s", option_arg); break; case 'e': m_flags.SetDontShowChildren(false); break; case 'h': m_flags.SetHideEmptyAggregates(true); break; case 'v': m_flags.SetDontShowValue(true); break; case 'c': m_flags.SetShowMembersOneLiner(true); break; case 's': m_format_string = std::string(option_arg); break; case 'p': m_flags.SetSkipPointers(true); break; case 'r': m_flags.SetSkipReferences(true); break; case 'x': m_regex = true; break; case 'n': m_name.SetCString(option_arg); break; case 'o': m_python_script = std::string(option_arg); m_is_add_script = true; break; case 'F': m_python_function = std::string(option_arg); m_is_add_script = true; break; case 'P': m_is_add_script = true; break; case 'w': m_category = std::string(option_arg); break; case 'O': m_flags.SetHideItemNames(true); break; default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; } return error; } void CommandObjectTypeSummaryAdd::CommandOptions::OptionParsingStarting () { m_flags.Clear().SetCascades().SetDontShowChildren().SetDontShowValue(false); m_flags.SetShowMembersOneLiner(false).SetSkipPointers(false).SetSkipReferences(false).SetHideItemNames(false); m_regex = false; m_name.Clear(); m_python_script = ""; m_python_function = ""; m_format_string = ""; m_is_add_script = false; m_category = "default"; } #ifndef LLDB_DISABLE_PYTHON bool CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturnObject &result) { const size_t argc = command.GetArgumentCount(); if (argc < 1 && !m_options.m_name) { result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } TypeSummaryImplSP script_format; if (!m_options.m_python_function.empty()) // we have a Python function ready to use { const char *funct_name = m_options.m_python_function.c_str(); if (!funct_name || !funct_name[0]) { result.AppendError ("function name empty.\n"); result.SetStatus (eReturnStatusFailed); return false; } std::string code = (" " + m_options.m_python_function + "(valobj,internal_dict)"); script_format.reset(new ScriptSummaryFormat(m_options.m_flags, funct_name, code.c_str())); ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); if (interpreter && interpreter->CheckObjectExists(funct_name) == false) result.AppendWarningWithFormat("The provided function \"%s\" does not exist - " "please define it before attempting to use this summary.\n", funct_name); } else if (!m_options.m_python_script.empty()) // we have a quick 1-line script, just use it { ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); if (!interpreter) { result.AppendError ("script interpreter missing - unable to generate function wrapper.\n"); result.SetStatus (eReturnStatusFailed); return false; } StringList funct_sl; funct_sl << m_options.m_python_script.c_str(); std::string funct_name_str; if (!interpreter->GenerateTypeScriptFunction (funct_sl, funct_name_str)) { result.AppendError ("unable to generate function wrapper.\n"); result.SetStatus (eReturnStatusFailed); return false; } if (funct_name_str.empty()) { result.AppendError ("script interpreter failed to generate a valid function name.\n"); result.SetStatus (eReturnStatusFailed); return false; } std::string code = " " + m_options.m_python_script; script_format.reset(new ScriptSummaryFormat(m_options.m_flags, funct_name_str.c_str(), code.c_str())); } else { // Use an IOHandler to grab Python code from the user ScriptAddOptions *options = new ScriptAddOptions(m_options.m_flags, m_options.m_regex, m_options.m_name, m_options.m_category); for (size_t i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); if (typeA && *typeA) options->m_target_types << typeA; else { result.AppendError("empty typenames not allowed"); result.SetStatus(eReturnStatusFailed); return false; } } m_interpreter.GetPythonCommandsFromIOHandler (" ", // Prompt *this, // IOHandlerDelegate true, // Run IOHandler in async mode options); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } // if I am here, script_format must point to something good, so I can add that // as a script summary to all interested parties Error error; for (size_t i = 0; i < command.GetArgumentCount(); i++) { const char *type_name = command.GetArgumentAtIndex(i); CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), script_format, (m_options.m_regex ? eRegexSummary : eRegularSummary), m_options.m_category, &error); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } } if (m_options.m_name) { AddSummary(m_options.m_name, script_format, eNamedSummary, m_options.m_category, &error); if (error.Fail()) { result.AppendError(error.AsCString()); result.AppendError("added to types, but not given a name"); result.SetStatus(eReturnStatusFailed); return false; } } return result.Succeeded(); } #endif bool CommandObjectTypeSummaryAdd::Execute_StringSummary (Args& command, CommandReturnObject &result) { const size_t argc = command.GetArgumentCount(); if (argc < 1 && !m_options.m_name) { result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } if (!m_options.m_flags.GetShowMembersOneLiner() && m_options.m_format_string.empty()) { result.AppendError("empty summary strings not allowed"); result.SetStatus(eReturnStatusFailed); return false; } const char* format_cstr = (m_options.m_flags.GetShowMembersOneLiner() ? "" : m_options.m_format_string.c_str()); // ${var%S} is an endless recursion, prevent it if (strcmp(format_cstr, "${var%S}") == 0) { result.AppendError("recursive summary not allowed"); result.SetStatus(eReturnStatusFailed); return false; } Error error; lldb::TypeSummaryImplSP entry(new StringSummaryFormat(m_options.m_flags, format_cstr)); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } // now I have a valid format, let's add it to every type for (size_t i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); if (!typeA || typeA[0] == '\0') { result.AppendError("empty typenames not allowed"); result.SetStatus(eReturnStatusFailed); return false; } ConstString typeCS(typeA); AddSummary(typeCS, entry, (m_options.m_regex ? eRegexSummary : eRegularSummary), m_options.m_category, &error); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } } if (m_options.m_name) { AddSummary(m_options.m_name, entry, eNamedSummary, m_options.m_category, &error); if (error.Fail()) { result.AppendError(error.AsCString()); result.AppendError("added to types, but not given a name"); result.SetStatus(eReturnStatusFailed); return false; } } result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } CommandObjectTypeSummaryAdd::CommandObjectTypeSummaryAdd (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "type summary add", "Add a new summary style for a type.", NULL), IOHandlerDelegateMultiline ("DONE"), m_options (interpreter) { CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatPlus; type_arg.push_back (type_style_arg); m_arguments.push_back (type_arg); SetHelpLong( R"( The following examples of 'type summary add' refer to this code snippet for context: struct JustADemo { int* ptr; float value; JustADemo(int p = 1, float v = 0.1) : ptr(new int(p)), value(v) {} }; JustADemo demo_instance(42, 3.14); typedef JustADemo NewDemo; NewDemo new_demo_instance(42, 3.14); (lldb) type summary add --summary-string "the answer is ${*var.ptr}" JustADemo Subsequently displaying demo_instance with 'frame variable' or 'expression' will display "the answer is 42" (lldb) type summary add --summary-string "the answer is ${*var.ptr}, and the question is ${var.value}" JustADemo Subsequently displaying demo_instance with 'frame variable' or 'expression' will display "the answer is 42 and the question is 3.14" )" "Alternatively, you could define formatting for all pointers to integers and \ rely on that when formatting JustADemo to obtain the same result:" R"( (lldb) type summary add --summary-string "${var%V} -> ${*var}" "int *" (lldb) type summary add --summary-string "the answer is ${var.ptr}, and the question is ${var.value}" JustADemo )" "Type summaries are automatically applied to derived typedefs, so the examples \ above apply to both JustADemo and NewDemo. The cascade option can be used to \ suppress this behavior:" R"( (lldb) type summary add --summary-string "${var.ptr}, ${var.value},{${var.byte}}" JustADemo -C no The summary will now be used for values of JustADemo but not NewDemo. )" "By default summaries are shown for pointers and references to values of the \ specified type. To suppress formatting for pointers use the -p option, or apply \ the corresponding -r option to suppress formatting for references:" R"( (lldb) type summary add -p -r --summary-string "${var.ptr}, ${var.value},{${var.byte}}" JustADemo )" "One-line summaries including all fields in a type can be inferred without supplying an \ explicit summary string by passing the -c option:" R"( (lldb) type summary add -c JustADemo (lldb) frame variable demo_instance (ptr=
, value=3.14) )" "Type summaries normally suppress the nested display of individual fields. To \ supply a summary to supplement the default structure add the -e option:" R"( (lldb) type summary add -e --summary-string "*ptr = ${*var.ptr}" JustADemo )" "Now when displaying JustADemo values the int* is displayed, followed by the \ standard LLDB sequence of children, one per line:" R"( *ptr = 42 { ptr =
value = 3.14 } )" "You can also add summaries written in Python. These scripts use lldb public API to \ gather information from your variables and produce a meaningful summary. To start a \ multi-line script use the -P option. The function declaration will be displayed along with \ a comment describing the two arguments. End your script with the word 'DONE' on a line by \ itself:" R"( (lldb) type summary add JustADemo -P def function (valobj,internal_dict): """valobj: an SBValue which you want to provide a summary for internal_dict: an LLDB support object not to be used""" value = valobj.GetChildMemberWithName('value'); return 'My value is ' + value.GetValue(); DONE Alternatively, the -o option can be used when providing a simple one-line Python script: (lldb) type summary add JustADemo -o "value = valobj.GetChildMemberWithName('value'); return 'My value is ' + value.GetValue();")" ); } bool CommandObjectTypeSummaryAdd::DoExecute (Args& command, CommandReturnObject &result) { WarnOnPotentialUnquotedUnsignedType(command, result); if (m_options.m_is_add_script) { #ifndef LLDB_DISABLE_PYTHON return Execute_ScriptSummary(command, result); #else result.AppendError ("python is disabled"); result.SetStatus(eReturnStatusFailed); return false; #endif } return Execute_StringSummary(command, result); } static bool FixArrayTypeNameWithRegex (ConstString &type_name) { llvm::StringRef type_name_ref(type_name.GetStringRef()); if (type_name_ref.endswith("[]")) { std::string type_name_str(type_name.GetCString()); type_name_str.resize(type_name_str.length()-2); if (type_name_str.back() != ' ') type_name_str.append(" \\[[0-9]+\\]"); else type_name_str.append("\\[[0-9]+\\]"); type_name.SetCString(type_name_str.c_str()); return true; } return false; } bool CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name, TypeSummaryImplSP entry, SummaryFormatType type, std::string category_name, Error* error) { lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); if (type == eRegularSummary) { if (FixArrayTypeNameWithRegex (type_name)) type = eRegexSummary; } if (type == eRegexSummary) { RegularExpressionSP typeRX(new RegularExpression()); if (!typeRX->Compile(type_name.GetCString())) { if (error) error->SetErrorString("regex format error (maybe this is not really a regex?)"); return false; } category->GetRegexTypeSummariesContainer()->Delete(type_name); category->GetRegexTypeSummariesContainer()->Add(typeRX, entry); return true; } else if (type == eNamedSummary) { // system named summaries do not exist (yet?) DataVisualization::NamedSummaryFormats::Add(type_name,entry); return true; } else { category->GetTypeSummariesContainer()->Add(type_name, entry); return true; } } OptionDefinition CommandObjectTypeSummaryAdd::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, { LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, { LLDB_OPT_SET_ALL, false, "no-value", 'v', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Don't show the value, just show the summary, for this type."}, { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, { LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, { LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."}, { LLDB_OPT_SET_1 , true, "inline-children", 'c', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "If true, inline all child values into summary string."}, { LLDB_OPT_SET_1 , false, "omit-names", 'O', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "If true, omit value names in the summary display."}, { LLDB_OPT_SET_2 , true, "summary-string", 's', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeSummaryString, "Summary string used to display text and object contents."}, { LLDB_OPT_SET_3, false, "python-script", 'o', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypePythonScript, "Give a one-liner Python script as part of the command."}, { LLDB_OPT_SET_3, false, "python-function", 'F', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypePythonFunction, "Give the name of a Python function to use for this type."}, { LLDB_OPT_SET_3, false, "input-python", 'P', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Input Python code to use for this type manually."}, { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "expand", 'e', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Expand aggregate data types to show children on separate lines."}, { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "hide-empty", 'h', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Do not expand aggregate data types with no children."}, { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "name", 'n', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeName, "A name for this summary string."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; //------------------------------------------------------------------------- // CommandObjectTypeSummaryDelete //------------------------------------------------------------------------- class CommandObjectTypeSummaryDelete : public CommandObjectTypeFormatterDelete { public: CommandObjectTypeSummaryDelete (CommandInterpreter &interpreter) : CommandObjectTypeFormatterDelete (interpreter, eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary, "type summary delete", "Delete an existing summary for a type.") { } ~CommandObjectTypeSummaryDelete () override { } protected: bool FormatterSpecificDeletion (ConstString typeCS) override { if (m_options.m_language != lldb::eLanguageTypeUnknown) return false; return DataVisualization::NamedSummaryFormats::Delete(typeCS); } }; class CommandObjectTypeSummaryClear : public CommandObjectTypeFormatterClear { public: CommandObjectTypeSummaryClear (CommandInterpreter &interpreter) : CommandObjectTypeFormatterClear (interpreter, eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary, "type summary clear", "Delete all existing summaries.") { } protected: void FormatterSpecificDeletion () override { DataVisualization::NamedSummaryFormats::Clear(); } }; //------------------------------------------------------------------------- // CommandObjectTypeSummaryList //------------------------------------------------------------------------- class CommandObjectTypeSummaryList : public CommandObjectTypeFormatterList { public: CommandObjectTypeSummaryList (CommandInterpreter &interpreter) : CommandObjectTypeFormatterList(interpreter, "type summary list", "Show a list of current summaries.") { } protected: void FormatterSpecificList (CommandReturnObject &result) override { if (DataVisualization::NamedSummaryFormats::GetCount() > 0) { result.GetOutputStream().Printf("Named summaries:\n"); DataVisualization::NamedSummaryFormats::ForEach( [&result] (ConstString name, const TypeSummaryImplSP& summary_sp) -> bool { result.GetOutputStream().Printf ("%s: %s\n", name.AsCString(), summary_sp->GetDescription().c_str()); return true; }); } } }; //------------------------------------------------------------------------- // CommandObjectTypeCategoryDefine //------------------------------------------------------------------------- class CommandObjectTypeCategoryDefine : public CommandObjectParsed { class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options (interpreter), m_define_enabled(false,false), m_cate_language(eLanguageTypeUnknown,eLanguageTypeUnknown) { } ~CommandOptions () override {} Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'e': m_define_enabled.SetValueFromString("true"); break; case 'l': error = m_cate_language.SetValueFromString(option_arg); break; default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting () override { m_define_enabled.Clear(); m_cate_language.Clear(); } const OptionDefinition* GetDefinitions () override { return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. OptionValueBoolean m_define_enabled; OptionValueLanguage m_cate_language; }; CommandOptions m_options; Options * GetOptions () override { return &m_options; } public: CommandObjectTypeCategoryDefine (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "type category define", "Define a new category as a source of formatters.", NULL), m_options(interpreter) { CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatPlus; type_arg.push_back (type_style_arg); m_arguments.push_back (type_arg); } ~CommandObjectTypeCategoryDefine () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc < 1) { result.AppendErrorWithFormat ("%s takes 1 or more args.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } for (size_t i = 0; i < argc; i++) { const char* cateName = command.GetArgumentAtIndex(i); TypeCategoryImplSP category_sp; if (DataVisualization::Categories::GetCategory(ConstString(cateName), category_sp) && category_sp) { category_sp->AddLanguage(m_options.m_cate_language.GetCurrentValue()); if (m_options.m_define_enabled.GetCurrentValue()) DataVisualization::Categories::Enable(category_sp, TypeCategoryMap::Default); } } result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } }; OptionDefinition CommandObjectTypeCategoryDefine::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "enabled", 'e', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "If specified, this category will be created enabled."}, { LLDB_OPT_SET_ALL, false, "language", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLanguage, "Specify the language that this category is supported for."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; //------------------------------------------------------------------------- // CommandObjectTypeCategoryEnable //------------------------------------------------------------------------- class CommandObjectTypeCategoryEnable : public CommandObjectParsed { class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options (interpreter) { } ~CommandOptions () override {} Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'l': if (option_arg) { m_language = Language::GetLanguageTypeFromString(option_arg); if (m_language == lldb::eLanguageTypeUnknown) error.SetErrorStringWithFormat ("unrecognized language '%s'", option_arg); } break; default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting () override { m_language = lldb::eLanguageTypeUnknown; } const OptionDefinition* GetDefinitions () override { return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. lldb::LanguageType m_language; }; CommandOptions m_options; Options * GetOptions () override { return &m_options; } public: CommandObjectTypeCategoryEnable (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "type category enable", "Enable a category as a source of formatters.", NULL), m_options(interpreter) { CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatPlus; type_arg.push_back (type_style_arg); m_arguments.push_back (type_arg); } ~CommandObjectTypeCategoryEnable () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc < 1 && m_options.m_language == lldb::eLanguageTypeUnknown) { result.AppendErrorWithFormat ("%s takes arguments and/or a language", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } if (argc == 1 && strcmp(command.GetArgumentAtIndex(0),"*") == 0) { DataVisualization::Categories::EnableStar(); } else if (argc > 0) { for (int i = argc - 1; i >= 0; i--) { const char* typeA = command.GetArgumentAtIndex(i); ConstString typeCS(typeA); if (!typeCS) { result.AppendError("empty category name not allowed"); result.SetStatus(eReturnStatusFailed); return false; } DataVisualization::Categories::Enable(typeCS); lldb::TypeCategoryImplSP cate; if (DataVisualization::Categories::GetCategory(typeCS, cate) && cate.get()) { if (cate->GetCount() == 0) { result.AppendWarning("empty category enabled (typo?)"); } } } } if (m_options.m_language != lldb::eLanguageTypeUnknown) DataVisualization::Categories::Enable(m_options.m_language); result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } }; OptionDefinition CommandObjectTypeCategoryEnable::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "language", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLanguage, "Enable the category for this language."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; //------------------------------------------------------------------------- // CommandObjectTypeCategoryDelete //------------------------------------------------------------------------- class CommandObjectTypeCategoryDelete : public CommandObjectParsed { public: CommandObjectTypeCategoryDelete (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "type category delete", "Delete a category and all associated formatters.", NULL) { CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatPlus; type_arg.push_back (type_style_arg); m_arguments.push_back (type_arg); } ~CommandObjectTypeCategoryDelete () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc < 1) { result.AppendErrorWithFormat ("%s takes 1 or more arg.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } bool success = true; // the order is not relevant here for (int i = argc - 1; i >= 0; i--) { const char* typeA = command.GetArgumentAtIndex(i); ConstString typeCS(typeA); if (!typeCS) { result.AppendError("empty category name not allowed"); result.SetStatus(eReturnStatusFailed); return false; } if (!DataVisualization::Categories::Delete(typeCS)) success = false; // keep deleting even if we hit an error } if (success) { result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } else { result.AppendError("cannot delete one or more categories\n"); result.SetStatus(eReturnStatusFailed); return false; } } }; //------------------------------------------------------------------------- // CommandObjectTypeCategoryDisable //------------------------------------------------------------------------- class CommandObjectTypeCategoryDisable : public CommandObjectParsed { class CommandOptions : public Options { public: CommandOptions (CommandInterpreter &interpreter) : Options (interpreter) { } ~CommandOptions () override {} Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'l': if (option_arg) { m_language = Language::GetLanguageTypeFromString(option_arg); if (m_language == lldb::eLanguageTypeUnknown) error.SetErrorStringWithFormat ("unrecognized language '%s'", option_arg); } break; default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting () override { m_language = lldb::eLanguageTypeUnknown; } const OptionDefinition* GetDefinitions () override { return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. lldb::LanguageType m_language; }; CommandOptions m_options; Options * GetOptions () override { return &m_options; } public: CommandObjectTypeCategoryDisable (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "type category disable", "Disable a category as a source of formatters.", NULL), m_options(interpreter) { CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatPlus; type_arg.push_back (type_style_arg); m_arguments.push_back (type_arg); } ~CommandObjectTypeCategoryDisable () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc < 1 && m_options.m_language == lldb::eLanguageTypeUnknown) { result.AppendErrorWithFormat ("%s takes arguments and/or a language", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } if (argc == 1 && strcmp(command.GetArgumentAtIndex(0),"*") == 0) { DataVisualization::Categories::DisableStar(); } else if (argc > 0) { // the order is not relevant here for (int i = argc - 1; i >= 0; i--) { const char* typeA = command.GetArgumentAtIndex(i); ConstString typeCS(typeA); if (!typeCS) { result.AppendError("empty category name not allowed"); result.SetStatus(eReturnStatusFailed); return false; } DataVisualization::Categories::Disable(typeCS); } } if (m_options.m_language != lldb::eLanguageTypeUnknown) DataVisualization::Categories::Disable(m_options.m_language); result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } }; OptionDefinition CommandObjectTypeCategoryDisable::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "language", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLanguage, "Enable the category for this language."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; //------------------------------------------------------------------------- // CommandObjectTypeCategoryList //------------------------------------------------------------------------- class CommandObjectTypeCategoryList : public CommandObjectParsed { public: CommandObjectTypeCategoryList (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "type category list", "Provide a list of all existing categories.", NULL) { CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatOptional; type_arg.push_back (type_style_arg); m_arguments.push_back (type_arg); } ~CommandObjectTypeCategoryList () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); std::unique_ptr regex; if (argc == 1) { regex.reset(new RegularExpression()); const char* arg = command.GetArgumentAtIndex(0); if (!regex->Compile(arg)) { result.AppendErrorWithFormat("syntax error in category regular expression '%s'", arg); result.SetStatus(eReturnStatusFailed); return false; } } else if (argc != 0) { result.AppendErrorWithFormat ("%s takes 0 or one arg.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } DataVisualization::Categories::ForEach( [®ex, &result] (const lldb::TypeCategoryImplSP& category_sp) -> bool { if (regex) { bool escape = true; if (0 == strcmp(category_sp->GetName(), regex->GetText())) { escape = false; } else if (regex->Execute(category_sp->GetName())) { escape = false; } if (escape) return true; } result.GetOutputStream().Printf("Category: %s\n", category_sp->GetDescription().c_str()); return true; }); result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectTypeFilterList //------------------------------------------------------------------------- class CommandObjectTypeFilterList : public CommandObjectTypeFormatterList { public: CommandObjectTypeFilterList (CommandInterpreter &interpreter) : CommandObjectTypeFormatterList(interpreter, "type filter list", "Show a list of current filters.") { } }; #ifndef LLDB_DISABLE_PYTHON //------------------------------------------------------------------------- // CommandObjectTypeSynthList //------------------------------------------------------------------------- class CommandObjectTypeSynthList : public CommandObjectTypeFormatterList { public: CommandObjectTypeSynthList (CommandInterpreter &interpreter) : CommandObjectTypeFormatterList(interpreter, "type synthetic list", "Show a list of current synthetic providers.") { } }; #endif // #ifndef LLDB_DISABLE_PYTHON //------------------------------------------------------------------------- // CommandObjectTypeFilterDelete //------------------------------------------------------------------------- class CommandObjectTypeFilterDelete : public CommandObjectTypeFormatterDelete { public: CommandObjectTypeFilterDelete (CommandInterpreter &interpreter) : CommandObjectTypeFormatterDelete (interpreter, eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter, "type filter delete", "Delete an existing filter for a type.") { } ~CommandObjectTypeFilterDelete () override { } }; #ifndef LLDB_DISABLE_PYTHON //------------------------------------------------------------------------- // CommandObjectTypeSynthDelete //------------------------------------------------------------------------- class CommandObjectTypeSynthDelete : public CommandObjectTypeFormatterDelete { public: CommandObjectTypeSynthDelete (CommandInterpreter &interpreter) : CommandObjectTypeFormatterDelete (interpreter, eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth, "type synthetic delete", "Delete an existing synthetic provider for a type.") { } ~CommandObjectTypeSynthDelete () override { } }; #endif // #ifndef LLDB_DISABLE_PYTHON //------------------------------------------------------------------------- // CommandObjectTypeFilterClear //------------------------------------------------------------------------- class CommandObjectTypeFilterClear : public CommandObjectTypeFormatterClear { public: CommandObjectTypeFilterClear (CommandInterpreter &interpreter) : CommandObjectTypeFormatterClear (interpreter, eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter, "type filter clear", "Delete all existing filter.") { } }; #ifndef LLDB_DISABLE_PYTHON //------------------------------------------------------------------------- // CommandObjectTypeSynthClear //------------------------------------------------------------------------- class CommandObjectTypeSynthClear : public CommandObjectTypeFormatterClear { public: CommandObjectTypeSynthClear (CommandInterpreter &interpreter) : CommandObjectTypeFormatterClear (interpreter, eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth, "type synthetic clear", "Delete all existing synthetic providers.") { } }; bool CommandObjectTypeSynthAdd::Execute_HandwritePython (Args& command, CommandReturnObject &result) { SynthAddOptions *options = new SynthAddOptions ( m_options.m_skip_pointers, m_options.m_skip_references, m_options.m_cascade, m_options.m_regex, m_options.m_category); const size_t argc = command.GetArgumentCount(); for (size_t i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); if (typeA && *typeA) options->m_target_types << typeA; else { result.AppendError("empty typenames not allowed"); result.SetStatus(eReturnStatusFailed); return false; } } m_interpreter.GetPythonCommandsFromIOHandler (" ", // Prompt *this, // IOHandlerDelegate true, // Run IOHandler in async mode options); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } bool CommandObjectTypeSynthAdd::Execute_PythonClass (Args& command, CommandReturnObject &result) { const size_t argc = command.GetArgumentCount(); if (argc < 1) { result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } if (m_options.m_class_name.empty() && !m_options.m_input_python) { result.AppendErrorWithFormat ("%s needs either a Python class name or -P to directly input Python code.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } SyntheticChildrenSP entry; ScriptedSyntheticChildren* impl = new ScriptedSyntheticChildren(SyntheticChildren::Flags(). SetCascades(m_options.m_cascade). SetSkipPointers(m_options.m_skip_pointers). SetSkipReferences(m_options.m_skip_references), m_options.m_class_name.c_str()); entry.reset(impl); ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); if (interpreter && interpreter->CheckObjectExists(impl->GetPythonClassName()) == false) result.AppendWarning("The provided class does not exist - please define it before attempting to use this synthetic provider"); // now I have a valid provider, let's add it to every type lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); Error error; for (size_t i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); ConstString typeCS(typeA); if (typeCS) { if (!AddSynth(typeCS, entry, m_options.m_regex ? eRegexSynth : eRegularSynth, m_options.m_category, &error)) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } } else { result.AppendError("empty typenames not allowed"); result.SetStatus(eReturnStatusFailed); return false; } } result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } CommandObjectTypeSynthAdd::CommandObjectTypeSynthAdd (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "type synthetic add", "Add a new synthetic provider for a type.", NULL), IOHandlerDelegateMultiline ("DONE"), m_options (interpreter) { CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatPlus; type_arg.push_back (type_style_arg); m_arguments.push_back (type_arg); } bool CommandObjectTypeSynthAdd::AddSynth(ConstString type_name, SyntheticChildrenSP entry, SynthFormatType type, std::string category_name, Error* error) { lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); if (type == eRegularSynth) { if (FixArrayTypeNameWithRegex (type_name)) type = eRegexSynth; } if (category->AnyMatches(type_name, eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter, false)) { if (error) error->SetErrorStringWithFormat("cannot add synthetic for type %s when filter is defined in same category!", type_name.AsCString()); return false; } if (type == eRegexSynth) { RegularExpressionSP typeRX(new RegularExpression()); if (!typeRX->Compile(type_name.GetCString())) { if (error) error->SetErrorString("regex format error (maybe this is not really a regex?)"); return false; } category->GetRegexTypeSyntheticsContainer()->Delete(type_name); category->GetRegexTypeSyntheticsContainer()->Add(typeRX, entry); return true; } else { category->GetTypeSyntheticsContainer()->Add(type_name, entry); return true; } } OptionDefinition CommandObjectTypeSynthAdd::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, { LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, { LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, { LLDB_OPT_SET_2, false, "python-class", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypePythonClass, "Use this Python class to produce synthetic children."}, { LLDB_OPT_SET_3, false, "input-python", 'P', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Type Python code to generate a class that provides synthetic children."}, { LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; #endif // #ifndef LLDB_DISABLE_PYTHON class CommandObjectTypeFilterAdd : public CommandObjectParsed { private: class CommandOptions : public Options { typedef std::vector option_vector; public: CommandOptions (CommandInterpreter &interpreter) : Options (interpreter) { } ~CommandOptions () override {} Error SetOptionValue (uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; bool success; switch (short_option) { case 'C': m_cascade = Args::StringToBoolean(option_arg, true, &success); if (!success) error.SetErrorStringWithFormat("invalid value for cascade: %s", option_arg); break; case 'c': m_expr_paths.push_back(option_arg); has_child_list = true; break; case 'p': m_skip_pointers = true; break; case 'r': m_skip_references = true; break; case 'w': m_category = std::string(option_arg); break; case 'x': m_regex = true; break; default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting () override { m_cascade = true; m_skip_pointers = false; m_skip_references = false; m_category = "default"; m_expr_paths.clear(); has_child_list = false; m_regex = false; } const OptionDefinition* GetDefinitions () override { return g_option_table; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. bool m_cascade; bool m_skip_references; bool m_skip_pointers; bool m_input_python; option_vector m_expr_paths; std::string m_category; bool has_child_list; bool m_regex; typedef option_vector::iterator ExpressionPathsIterator; }; CommandOptions m_options; Options * GetOptions () override { return &m_options; } enum FilterFormatType { eRegularFilter, eRegexFilter }; bool AddFilter(ConstString type_name, TypeFilterImplSP entry, FilterFormatType type, std::string category_name, Error* error) { lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); if (type == eRegularFilter) { if (FixArrayTypeNameWithRegex (type_name)) type = eRegexFilter; } if (category->AnyMatches(type_name, eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth, false)) { if (error) error->SetErrorStringWithFormat("cannot add filter for type %s when synthetic is defined in same category!", type_name.AsCString()); return false; } if (type == eRegexFilter) { RegularExpressionSP typeRX(new RegularExpression()); if (!typeRX->Compile(type_name.GetCString())) { if (error) error->SetErrorString("regex format error (maybe this is not really a regex?)"); return false; } category->GetRegexTypeFiltersContainer()->Delete(type_name); category->GetRegexTypeFiltersContainer()->Add(typeRX, entry); return true; } else { category->GetTypeFiltersContainer()->Add(type_name, entry); return true; } } public: CommandObjectTypeFilterAdd (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "type filter add", "Add a new filter for a type.", NULL), m_options (interpreter) { CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatPlus; type_arg.push_back (type_style_arg); m_arguments.push_back (type_arg); SetHelpLong( R"( The following examples of 'type filter add' refer to this code snippet for context: class Foo { int a; int b; int c; int d; int e; int f; int g; int h; int i; } Foo my_foo; Adding a simple filter: (lldb) type filter add --child a --child g Foo (lldb) frame variable my_foo )" "Produces output where only a and g are displayed. Other children of my_foo \ (b, c, d, e, f, h and i) are available by asking for them explicitly:" R"( (lldb) frame variable my_foo.b my_foo.c my_foo.i )" "The formatting option --raw on frame variable bypasses the filter, showing \ all children of my_foo as if no filter was defined:" R"( (lldb) frame variable my_foo --raw)" ); } ~CommandObjectTypeFilterAdd () override { } protected: bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc < 1) { result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } if (m_options.m_expr_paths.size() == 0) { result.AppendErrorWithFormat ("%s needs one or more children.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } TypeFilterImplSP entry(new TypeFilterImpl(SyntheticChildren::Flags().SetCascades(m_options.m_cascade). SetSkipPointers(m_options.m_skip_pointers). SetSkipReferences(m_options.m_skip_references))); // go through the expression paths CommandOptions::ExpressionPathsIterator begin, end = m_options.m_expr_paths.end(); for (begin = m_options.m_expr_paths.begin(); begin != end; begin++) entry->AddExpressionPath(*begin); // now I have a valid provider, let's add it to every type lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); Error error; WarnOnPotentialUnquotedUnsignedType(command, result); for (size_t i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); ConstString typeCS(typeA); if (typeCS) { if (!AddFilter(typeCS, entry, m_options.m_regex ? eRegexFilter : eRegularFilter, m_options.m_category, &error)) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } } else { result.AppendError("empty typenames not allowed"); result.SetStatus(eReturnStatusFailed); return false; } } result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } }; OptionDefinition CommandObjectTypeFilterAdd::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, { LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, { LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, { LLDB_OPT_SET_ALL, false, "child", 'c', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeExpressionPath, "Include this expression path in the synthetic view."}, { LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; //---------------------------------------------------------------------- // "type lookup" //---------------------------------------------------------------------- class CommandObjectTypeLookup : public CommandObjectRaw { protected: class CommandOptions : public OptionGroup { public: CommandOptions () : OptionGroup(), m_show_help(false), m_language(eLanguageTypeUnknown) {} ~CommandOptions () override {} uint32_t GetNumDefinitions () override { return 3; } const OptionDefinition* GetDefinitions () override { return g_option_table; } Error SetOptionValue (CommandInterpreter &interpreter, uint32_t option_idx, const char *option_value) override { Error error; const int short_option = g_option_table[option_idx].short_option; switch (short_option) { case 'h': m_show_help = true; break; case 'l': m_language = Language::GetLanguageTypeFromString(option_value); break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting (CommandInterpreter &interpreter) override { m_show_help = false; m_language = eLanguageTypeUnknown; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; bool m_show_help; lldb::LanguageType m_language; }; OptionGroupOptions m_option_group; CommandOptions m_command_options; public: CommandObjectTypeLookup (CommandInterpreter &interpreter) : CommandObjectRaw (interpreter, "type lookup", "Lookup a type by name in the select target.", "type lookup ", eCommandRequiresTarget), m_option_group(interpreter), m_command_options() { m_option_group.Append(&m_command_options); m_option_group.Finalize(); } ~CommandObjectTypeLookup () override { } Options * GetOptions () override { return &m_option_group; } bool DoExecute (const char *raw_command_line, CommandReturnObject &result) override { if (!raw_command_line || !raw_command_line[0]) { result.SetError("type lookup cannot be invoked without a type name as argument"); return false; } m_option_group.NotifyOptionParsingStarting(); const char * name_of_type = NULL; if (raw_command_line[0] == '-') { // We have some options and these options MUST end with --. const char *end_options = NULL; const char *s = raw_command_line; while (s && s[0]) { end_options = ::strstr (s, "--"); if (end_options) { end_options += 2; // Get past the "--" if (::isspace (end_options[0])) { name_of_type = end_options; while (::isspace (*name_of_type)) ++name_of_type; break; } } s = end_options; } if (end_options) { Args args (llvm::StringRef(raw_command_line, end_options - raw_command_line)); if (!ParseOptions (args, result)) return false; Error error (m_option_group.NotifyOptionParsingFinished()); if (error.Fail()) { result.AppendError (error.AsCString()); result.SetStatus (eReturnStatusFailed); return false; } } } if (nullptr == name_of_type) name_of_type = raw_command_line; TargetSP target_sp(GetCommandInterpreter().GetDebugger().GetSelectedTarget()); const bool fill_all_in = true; ExecutionContext exe_ctx(target_sp.get(), fill_all_in); ExecutionContextScope *best_scope = exe_ctx.GetBestExecutionContextScope(); bool any_found = false; std::vector languages; if (m_command_options.m_language == eLanguageTypeUnknown) { // FIXME: hardcoding languages is not good languages.push_back(Language::FindPlugin(eLanguageTypeObjC)); languages.push_back(Language::FindPlugin(eLanguageTypeC_plus_plus)); } else { languages.push_back(Language::FindPlugin(m_command_options.m_language)); } for (Language* language : languages) { if (!language) continue; if (auto scavenger = language->GetTypeScavenger()) { Language::TypeScavenger::ResultSet search_results; if (scavenger->Find(best_scope, name_of_type, search_results) > 0) { for (const auto& search_result : search_results) { if (search_result && search_result->IsValid()) { any_found = true; search_result->DumpToStream(result.GetOutputStream(), this->m_command_options.m_show_help); } } } } } result.SetStatus (any_found ? lldb::eReturnStatusSuccessFinishResult : lldb::eReturnStatusSuccessFinishNoResult); return true; } }; OptionDefinition CommandObjectTypeLookup::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "show-help", 'h', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Display available help for types"}, { LLDB_OPT_SET_ALL, false, "language", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLanguage, "Which language's types should the search scope be"}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; template class CommandObjectFormatterInfo : public CommandObjectRaw { public: typedef std::function DiscoveryFunction; CommandObjectFormatterInfo (CommandInterpreter &interpreter, const char* formatter_name, DiscoveryFunction discovery_func) : CommandObjectRaw(interpreter, nullptr, nullptr, nullptr, eCommandRequiresFrame), m_formatter_name(formatter_name ? formatter_name : ""), m_discovery_function(discovery_func) { StreamString name; name.Printf("type %s info", formatter_name); SetCommandName(name.GetData()); StreamString help; help.Printf("This command evaluates the provided expression and shows which %s is applied to the resulting value (if any).", formatter_name); SetHelp(help.GetData()); StreamString syntax; syntax.Printf("type %s info ", formatter_name); SetSyntax(syntax.GetData()); } ~CommandObjectFormatterInfo () override { } protected: bool DoExecute (const char *command, CommandReturnObject &result) override { auto target_sp = m_interpreter.GetDebugger().GetSelectedTarget(); auto frame_sp = target_sp->GetProcessSP()->GetThreadList().GetSelectedThread()->GetSelectedFrame(); ValueObjectSP result_valobj_sp; EvaluateExpressionOptions options; lldb::ExpressionResults expr_result = target_sp->EvaluateExpression(command, frame_sp.get(), result_valobj_sp, options); if (expr_result == eExpressionCompleted && result_valobj_sp) { result_valobj_sp = result_valobj_sp->GetQualifiedRepresentationIfAvailable(target_sp->GetPreferDynamicValue(), target_sp->GetEnableSyntheticValue()); typename FormatterType::SharedPointer formatter_sp = m_discovery_function(*result_valobj_sp); if (formatter_sp) { std::string description(formatter_sp->GetDescription()); result.AppendMessageWithFormat("%s applied to (%s) %s is: %s\n", m_formatter_name.c_str(), result_valobj_sp->GetDisplayTypeName().AsCString(""), command, description.c_str()); result.SetStatus(lldb::eReturnStatusSuccessFinishResult); } else { result.AppendMessageWithFormat("no %s applies to (%s) %s\n", m_formatter_name.c_str(), result_valobj_sp->GetDisplayTypeName().AsCString(""), command); result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); } return true; } else { result.AppendError("failed to evaluate expression"); result.SetStatus(lldb::eReturnStatusFailed); return false; } } private: std::string m_formatter_name; DiscoveryFunction m_discovery_function; }; class CommandObjectTypeFormat : public CommandObjectMultiword { public: CommandObjectTypeFormat (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "type format", "A set of commands for editing variable value display options", "type format [] ") { LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeFormatAdd (interpreter))); LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeFormatClear (interpreter))); LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeFormatDelete (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeFormatList (interpreter))); LoadSubCommand ("info", CommandObjectSP (new CommandObjectFormatterInfo(interpreter, "format", [](ValueObject& valobj) -> TypeFormatImpl::SharedPointer { return valobj.GetValueFormat(); }))); } ~CommandObjectTypeFormat () override { } }; #ifndef LLDB_DISABLE_PYTHON class CommandObjectTypeSynth : public CommandObjectMultiword { public: CommandObjectTypeSynth (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "type synthetic", "A set of commands for operating on synthetic type representations", "type synthetic [] ") { LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeSynthAdd (interpreter))); LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSynthClear (interpreter))); LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSynthDelete (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSynthList (interpreter))); LoadSubCommand ("info", CommandObjectSP (new CommandObjectFormatterInfo(interpreter, "synthetic", [](ValueObject& valobj) -> SyntheticChildren::SharedPointer { return valobj.GetSyntheticChildren(); }))); } ~CommandObjectTypeSynth () override { } }; #endif // #ifndef LLDB_DISABLE_PYTHON class CommandObjectTypeFilter : public CommandObjectMultiword { public: CommandObjectTypeFilter (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "type filter", "A set of commands for operating on type filters", "type synthetic [] ") { LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeFilterAdd (interpreter))); LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeFilterClear (interpreter))); LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeFilterDelete (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeFilterList (interpreter))); } ~CommandObjectTypeFilter () override { } }; class CommandObjectTypeCategory : public CommandObjectMultiword { public: CommandObjectTypeCategory (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "type category", "A set of commands for operating on categories", "type category [] ") { LoadSubCommand ("define", CommandObjectSP (new CommandObjectTypeCategoryDefine (interpreter))); LoadSubCommand ("enable", CommandObjectSP (new CommandObjectTypeCategoryEnable (interpreter))); LoadSubCommand ("disable", CommandObjectSP (new CommandObjectTypeCategoryDisable (interpreter))); LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeCategoryDelete (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeCategoryList (interpreter))); } ~CommandObjectTypeCategory () override { } }; class CommandObjectTypeSummary : public CommandObjectMultiword { public: CommandObjectTypeSummary (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "type summary", "A set of commands for editing variable summary display options", "type summary [] ") { LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeSummaryAdd (interpreter))); LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSummaryClear (interpreter))); LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSummaryDelete (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSummaryList (interpreter))); LoadSubCommand ("info", CommandObjectSP (new CommandObjectFormatterInfo(interpreter, "summary", [](ValueObject& valobj) -> TypeSummaryImpl::SharedPointer { return valobj.GetSummaryFormat(); }))); } ~CommandObjectTypeSummary () override { } }; //------------------------------------------------------------------------- // CommandObjectType //------------------------------------------------------------------------- CommandObjectType::CommandObjectType (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "type", "A set of commands for operating on the type system", "type []") { LoadSubCommand ("category", CommandObjectSP (new CommandObjectTypeCategory (interpreter))); LoadSubCommand ("filter", CommandObjectSP (new CommandObjectTypeFilter (interpreter))); LoadSubCommand ("format", CommandObjectSP (new CommandObjectTypeFormat (interpreter))); LoadSubCommand ("summary", CommandObjectSP (new CommandObjectTypeSummary (interpreter))); #ifndef LLDB_DISABLE_PYTHON LoadSubCommand ("synthetic", CommandObjectSP (new CommandObjectTypeSynth (interpreter))); #endif LoadSubCommand ("lookup", CommandObjectSP (new CommandObjectTypeLookup (interpreter))); } CommandObjectType::~CommandObjectType () { } Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Core/StringList.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Core/StringList.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Core/StringList.cpp (revision 293283) @@ -1,341 +1,378 @@ //===-- StringList.cpp ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Core/StringList.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/FileSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" #include using namespace lldb_private; StringList::StringList () : m_strings () { } StringList::StringList (const char *str) : m_strings () { if (str) m_strings.push_back (str); } StringList::StringList (const char **strv, int strc) : m_strings () { for (int i = 0; i < strc; ++i) { if (strv[i]) m_strings.push_back (strv[i]); } } StringList::~StringList () { } void StringList::AppendString (const char *str) { if (str) m_strings.push_back (str); } void StringList::AppendString (const std::string &s) { m_strings.push_back (s); } void StringList::AppendString (std::string &&s) { m_strings.push_back (s); } void StringList::AppendString (const char *str, size_t str_len) { if (str) m_strings.push_back (std::string (str, str_len)); } void StringList::AppendString(llvm::StringRef str) { m_strings.push_back(str.str()); } void StringList::AppendList (const char **strv, int strc) { for (int i = 0; i < strc; ++i) { if (strv[i]) m_strings.push_back (strv[i]); } } void StringList::AppendList (StringList strings) { size_t len = strings.GetSize(); for (size_t i = 0; i < len; ++i) m_strings.push_back (strings.GetStringAtIndex(i)); } bool StringList::ReadFileLines (FileSpec &input_file) { return input_file.ReadFileLines (m_strings); } size_t StringList::GetSize () const { return m_strings.size(); } size_t StringList::GetMaxStringLength () const { size_t max_length = 0; for (const auto &s : m_strings) { const size_t len = s.size(); if (max_length < len) max_length = len; } return max_length; } const char * StringList::GetStringAtIndex (size_t idx) const { if (idx < m_strings.size()) return m_strings[idx].c_str(); return NULL; } void StringList::Join (const char *separator, Stream &strm) { size_t size = GetSize(); if (size == 0) return; for (uint32_t i = 0; i < size; ++i) { if (i > 0) strm.PutCString(separator); strm.PutCString(GetStringAtIndex(i)); } } void StringList::Clear () { m_strings.clear(); } void StringList::LongestCommonPrefix (std::string &common_prefix) { const size_t num_strings = m_strings.size(); if (num_strings == 0) { common_prefix.clear(); } else { common_prefix = m_strings.front(); for (size_t idx = 1; idx < num_strings; ++idx) { std::string &curr_string = m_strings[idx]; size_t new_size = curr_string.size(); // First trim common_prefix if it is longer than the current element: if (common_prefix.size() > new_size) common_prefix.erase (new_size); // Then trim it at the first disparity: for (size_t i = 0; i < common_prefix.size(); i++) { if (curr_string[i] != common_prefix[i]) { common_prefix.erase(i); break; } } // If we've emptied the common prefix, we're done. if (common_prefix.empty()) break; } } } void StringList::InsertStringAtIndex (size_t idx, const char *str) { if (str) { if (idx < m_strings.size()) m_strings.insert (m_strings.begin() + idx, str); else m_strings.push_back (str); } } void StringList::InsertStringAtIndex (size_t idx, const std::string &str) { if (idx < m_strings.size()) m_strings.insert (m_strings.begin() + idx, str); else m_strings.push_back (str); } void StringList::InsertStringAtIndex (size_t idx, std::string &&str) { if (idx < m_strings.size()) m_strings.insert (m_strings.begin() + idx, str); else m_strings.push_back (str); } void StringList::DeleteStringAtIndex (size_t idx) { if (idx < m_strings.size()) m_strings.erase (m_strings.begin() + idx); } size_t StringList::SplitIntoLines (const std::string &lines) { return SplitIntoLines (lines.c_str(), lines.size()); } size_t StringList::SplitIntoLines (const char *lines, size_t len) { const size_t orig_size = m_strings.size(); if (len == 0) return 0; const char *k_newline_chars = "\r\n"; const char *p = lines; const char *end = lines + len; while (p < end) { size_t count = strcspn (p, k_newline_chars); if (count == 0) { if (p[count] == '\r' || p[count] == '\n') m_strings.push_back(std::string()); else break; } else { if (p + count > end) count = end - p; m_strings.push_back(std::string(p, count)); } if (p[count] == '\r' && p[count+1] == '\n') count++; // Skip an extra newline char for the DOS newline count++; // Skip the newline character p += count; } return m_strings.size() - orig_size; } void StringList::RemoveBlankLines () { if (GetSize() == 0) return; size_t idx = 0; while (idx < m_strings.size()) { if (m_strings[idx].empty()) DeleteStringAtIndex(idx); else idx++; } } std::string StringList::CopyList(const char* item_preamble, const char* items_sep) const { StreamString strm; for (size_t i = 0; i < GetSize(); i++) { if (i && items_sep && items_sep[0]) strm << items_sep; if (item_preamble) strm << item_preamble; strm << GetStringAtIndex(i); } return std::string(strm.GetData()); } StringList& StringList::operator << (const char* str) { AppendString(str); return *this; } StringList& +StringList::operator << (const std::string& str) +{ + AppendString(str); + return *this; +} + +StringList& StringList::operator << (StringList strings) { AppendList(strings); return *this; } +StringList& +StringList::operator = (const std::vector &rhs) +{ + Clear(); + for (const auto &s : rhs) + m_strings.push_back(s); + + return *this; +} + size_t StringList::AutoComplete (const char *s, StringList &matches, size_t &exact_idx) const { matches.Clear(); exact_idx = SIZE_MAX; if (s && s[0]) { const size_t s_len = strlen (s); const size_t num_strings = m_strings.size(); for (size_t i=0; iDebug("%s", strm.GetData()); +} Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Interpreter/CommandHistory.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Interpreter/CommandHistory.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Interpreter/CommandHistory.cpp (revision 293283) @@ -1,145 +1,145 @@ //===-- CommandHistory.cpp --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include "lldb/Interpreter/CommandHistory.h" #include "lldb/Host/StringConvert.h" using namespace lldb; using namespace lldb_private; CommandHistory::CommandHistory () : m_mutex(Mutex::eMutexTypeRecursive), m_history() {} CommandHistory::~CommandHistory () {} size_t CommandHistory::GetSize () const { Mutex::Locker locker(m_mutex); return m_history.size(); } bool CommandHistory::IsEmpty () const { Mutex::Locker locker(m_mutex); return m_history.empty(); } const char* CommandHistory::FindString (const char* input_str) const { Mutex::Locker locker(m_mutex); if (!input_str) return nullptr; if (input_str[0] != g_repeat_char) return nullptr; if (input_str[1] == '-') { bool success; size_t idx = StringConvert::ToUInt32 (input_str+2, 0, 0, &success); if (!success) return nullptr; if (idx > m_history.size()) return nullptr; idx = m_history.size() - idx; return m_history[idx].c_str(); } else if (input_str[1] == g_repeat_char) { if (m_history.empty()) return nullptr; else return m_history.back().c_str(); } else { bool success; uint32_t idx = StringConvert::ToUInt32 (input_str+1, 0, 0, &success); if (!success) return nullptr; if (idx >= m_history.size()) return nullptr; return m_history[idx].c_str(); } } const char* CommandHistory::GetStringAtIndex (size_t idx) const { Mutex::Locker locker(m_mutex); if (idx < m_history.size()) return m_history[idx].c_str(); return nullptr; } const char* CommandHistory::operator [] (size_t idx) const { return GetStringAtIndex(idx); } const char* CommandHistory::GetRecentmostString () const { Mutex::Locker locker(m_mutex); if (m_history.empty()) return nullptr; return m_history.back().c_str(); } void CommandHistory::AppendString (const std::string& str, bool reject_if_dupe) { Mutex::Locker locker(m_mutex); if (reject_if_dupe) { if (!m_history.empty()) { if (str == m_history.back()) return; } } m_history.push_back(std::string(str)); } void CommandHistory::Clear () { Mutex::Locker locker(m_mutex); m_history.clear(); } void CommandHistory::Dump (Stream& stream, size_t start_idx, size_t stop_idx) const { Mutex::Locker locker(m_mutex); - stop_idx = std::min(stop_idx, m_history.size() - 1); + stop_idx = std::min(stop_idx + 1, m_history.size()); for (size_t counter = start_idx; - counter <= stop_idx; + counter < stop_idx; counter++) { const std::string hist_item = m_history[counter]; if (!hist_item.empty()) { stream.Indent(); stream.Printf("%4" PRIu64 ": %s\n", (uint64_t)counter, hist_item.c_str()); } } } Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp (revision 293283) @@ -1,4064 +1,2776 @@ //===-- EmulateInstructionMIPS.cpp -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "EmulateInstructionMIPS.h" #include #include "llvm-c/Disassembler.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCDisassembler.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCContext.h" #include "lldb/Core/Address.h" #include "lldb/Core/Opcode.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Stream.h" #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Target/Target.h" #include "llvm/ADT/STLExtras.h" #include "Plugins/Process/Utility/InstructionUtils.h" #include "Plugins/Process/Utility/RegisterContext_mips.h" //mips32 has same registers nos as mips64 using namespace lldb; using namespace lldb_private; #define UInt(x) ((uint64_t)x) #define integer int64_t //---------------------------------------------------------------------- // // EmulateInstructionMIPS implementation // //---------------------------------------------------------------------- #ifdef __mips__ extern "C" { void LLVMInitializeMipsTargetInfo (); void LLVMInitializeMipsTarget (); void LLVMInitializeMipsAsmPrinter (); void LLVMInitializeMipsTargetMC (); void LLVMInitializeMipsDisassembler (); } #endif EmulateInstructionMIPS::EmulateInstructionMIPS (const lldb_private::ArchSpec &arch) : EmulateInstruction (arch) { /* Create instance of llvm::MCDisassembler */ std::string Error; llvm::Triple triple = arch.GetTriple(); const llvm::Target *target = llvm::TargetRegistry::lookupTarget (triple.getTriple(), Error); /* * If we fail to get the target then we haven't registered it. The SystemInitializerCommon * does not initialize targets, MCs and disassemblers. However we need the MCDisassembler * to decode the instructions so that the decoding complexity stays with LLVM. * Initialize the MIPS targets and disassemblers. */ #ifdef __mips__ if (!target) { LLVMInitializeMipsTargetInfo (); LLVMInitializeMipsTarget (); LLVMInitializeMipsAsmPrinter (); LLVMInitializeMipsTargetMC (); LLVMInitializeMipsDisassembler (); target = llvm::TargetRegistry::lookupTarget (triple.getTriple(), Error); } #endif assert (target); llvm::StringRef cpu; switch (arch.GetCore()) { case ArchSpec::eCore_mips32: case ArchSpec::eCore_mips32el: cpu = "mips32"; break; case ArchSpec::eCore_mips32r2: case ArchSpec::eCore_mips32r2el: cpu = "mips32r2"; break; case ArchSpec::eCore_mips32r3: case ArchSpec::eCore_mips32r3el: cpu = "mips32r3"; break; case ArchSpec::eCore_mips32r5: case ArchSpec::eCore_mips32r5el: cpu = "mips32r5"; break; case ArchSpec::eCore_mips32r6: case ArchSpec::eCore_mips32r6el: cpu = "mips32r6"; break; case ArchSpec::eCore_mips64: case ArchSpec::eCore_mips64el: cpu = "mips64"; break; case ArchSpec::eCore_mips64r2: case ArchSpec::eCore_mips64r2el: cpu = "mips64r2"; break; case ArchSpec::eCore_mips64r3: case ArchSpec::eCore_mips64r3el: cpu = "mips64r3"; break; case ArchSpec::eCore_mips64r5: case ArchSpec::eCore_mips64r5el: cpu = "mips64r5"; break; case ArchSpec::eCore_mips64r6: case ArchSpec::eCore_mips64r6el: cpu = "mips64r6"; break; default: cpu = "generic"; break; } std::string features = ""; uint32_t arch_flags = arch.GetFlags (); if (arch_flags & ArchSpec::eMIPSAse_msa) features += "+msa,"; if (arch_flags & ArchSpec::eMIPSAse_dsp) features += "+dsp,"; if (arch_flags & ArchSpec::eMIPSAse_dspr2) features += "+dspr2,"; m_reg_info.reset (target->createMCRegInfo (triple.getTriple())); assert (m_reg_info.get()); m_insn_info.reset (target->createMCInstrInfo()); assert (m_insn_info.get()); m_asm_info.reset (target->createMCAsmInfo (*m_reg_info, triple.getTriple())); m_subtype_info.reset (target->createMCSubtargetInfo (triple.getTriple(), cpu, features)); assert (m_asm_info.get() && m_subtype_info.get()); m_context.reset (new llvm::MCContext (m_asm_info.get(), m_reg_info.get(), nullptr)); assert (m_context.get()); m_disasm.reset (target->createMCDisassembler (*m_subtype_info, *m_context)); assert (m_disasm.get()); /* Create alternate disassembler for microMIPS */ if (arch_flags & ArchSpec::eMIPSAse_mips16) features += "+mips16,"; else if (arch_flags & ArchSpec::eMIPSAse_micromips) features += "+micromips,"; m_alt_subtype_info.reset (target->createMCSubtargetInfo (triple.getTriple(), cpu, features)); assert (m_alt_subtype_info.get()); m_alt_disasm.reset (target->createMCDisassembler (*m_alt_subtype_info, *m_context)); assert (m_alt_disasm.get()); m_next_inst_size = 0; m_use_alt_disaasm = false; } void EmulateInstructionMIPS::Initialize () { PluginManager::RegisterPlugin (GetPluginNameStatic (), GetPluginDescriptionStatic (), CreateInstance); } void EmulateInstructionMIPS::Terminate () { PluginManager::UnregisterPlugin (CreateInstance); } ConstString EmulateInstructionMIPS::GetPluginNameStatic () { ConstString g_plugin_name ("lldb.emulate-instruction.mips32"); return g_plugin_name; } lldb_private::ConstString EmulateInstructionMIPS::GetPluginName() { static ConstString g_plugin_name ("EmulateInstructionMIPS"); return g_plugin_name; } const char * EmulateInstructionMIPS::GetPluginDescriptionStatic () { return "Emulate instructions for the MIPS32 architecture."; } EmulateInstruction * EmulateInstructionMIPS::CreateInstance (const ArchSpec &arch, InstructionType inst_type) { if (EmulateInstructionMIPS::SupportsEmulatingInstructionsOfTypeStatic(inst_type)) { if (arch.GetTriple().getArch() == llvm::Triple::mips || arch.GetTriple().getArch() == llvm::Triple::mipsel) { std::auto_ptr emulate_insn_ap (new EmulateInstructionMIPS (arch)); if (emulate_insn_ap.get()) return emulate_insn_ap.release(); } } return NULL; } bool EmulateInstructionMIPS::SetTargetTriple (const ArchSpec &arch) { if (arch.GetTriple().getArch () == llvm::Triple::mips || arch.GetTriple().getArch () == llvm::Triple::mipsel) return true; return false; } const char * EmulateInstructionMIPS::GetRegisterName (unsigned reg_num, bool alternate_name) { if (alternate_name) { switch (reg_num) { case dwarf_sp_mips: return "r29"; case dwarf_r30_mips: return "r30"; case dwarf_ra_mips: return "r31"; case dwarf_f0_mips: return "f0"; case dwarf_f1_mips: return "f1"; case dwarf_f2_mips: return "f2"; case dwarf_f3_mips: return "f3"; case dwarf_f4_mips: return "f4"; case dwarf_f5_mips: return "f5"; case dwarf_f6_mips: return "f6"; case dwarf_f7_mips: return "f7"; case dwarf_f8_mips: return "f8"; case dwarf_f9_mips: return "f9"; case dwarf_f10_mips: return "f10"; case dwarf_f11_mips: return "f11"; case dwarf_f12_mips: return "f12"; case dwarf_f13_mips: return "f13"; case dwarf_f14_mips: return "f14"; case dwarf_f15_mips: return "f15"; case dwarf_f16_mips: return "f16"; case dwarf_f17_mips: return "f17"; case dwarf_f18_mips: return "f18"; case dwarf_f19_mips: return "f19"; case dwarf_f20_mips: return "f20"; case dwarf_f21_mips: return "f21"; case dwarf_f22_mips: return "f22"; case dwarf_f23_mips: return "f23"; case dwarf_f24_mips: return "f24"; case dwarf_f25_mips: return "f25"; case dwarf_f26_mips: return "f26"; case dwarf_f27_mips: return "f27"; case dwarf_f28_mips: return "f28"; case dwarf_f29_mips: return "f29"; case dwarf_f30_mips: return "f30"; case dwarf_f31_mips: return "f31"; case dwarf_w0_mips: return "w0"; case dwarf_w1_mips: return "w1"; case dwarf_w2_mips: return "w2"; case dwarf_w3_mips: return "w3"; case dwarf_w4_mips: return "w4"; case dwarf_w5_mips: return "w5"; case dwarf_w6_mips: return "w6"; case dwarf_w7_mips: return "w7"; case dwarf_w8_mips: return "w8"; case dwarf_w9_mips: return "w9"; case dwarf_w10_mips: return "w10"; case dwarf_w11_mips: return "w11"; case dwarf_w12_mips: return "w12"; case dwarf_w13_mips: return "w13"; case dwarf_w14_mips: return "w14"; case dwarf_w15_mips: return "w15"; case dwarf_w16_mips: return "w16"; case dwarf_w17_mips: return "w17"; case dwarf_w18_mips: return "w18"; case dwarf_w19_mips: return "w19"; case dwarf_w20_mips: return "w20"; case dwarf_w21_mips: return "w21"; case dwarf_w22_mips: return "w22"; case dwarf_w23_mips: return "w23"; case dwarf_w24_mips: return "w24"; case dwarf_w25_mips: return "w25"; case dwarf_w26_mips: return "w26"; case dwarf_w27_mips: return "w27"; case dwarf_w28_mips: return "w28"; case dwarf_w29_mips: return "w29"; case dwarf_w30_mips: return "w30"; case dwarf_w31_mips: return "w31"; case dwarf_mir_mips: return "mir"; case dwarf_mcsr_mips: return "mcsr"; case dwarf_config5_mips: return "config5"; default: break; } return nullptr; } switch (reg_num) { case dwarf_zero_mips: return "r0"; case dwarf_r1_mips: return "r1"; case dwarf_r2_mips: return "r2"; case dwarf_r3_mips: return "r3"; case dwarf_r4_mips: return "r4"; case dwarf_r5_mips: return "r5"; case dwarf_r6_mips: return "r6"; case dwarf_r7_mips: return "r7"; case dwarf_r8_mips: return "r8"; case dwarf_r9_mips: return "r9"; case dwarf_r10_mips: return "r10"; case dwarf_r11_mips: return "r11"; case dwarf_r12_mips: return "r12"; case dwarf_r13_mips: return "r13"; case dwarf_r14_mips: return "r14"; case dwarf_r15_mips: return "r15"; case dwarf_r16_mips: return "r16"; case dwarf_r17_mips: return "r17"; case dwarf_r18_mips: return "r18"; case dwarf_r19_mips: return "r19"; case dwarf_r20_mips: return "r20"; case dwarf_r21_mips: return "r21"; case dwarf_r22_mips: return "r22"; case dwarf_r23_mips: return "r23"; case dwarf_r24_mips: return "r24"; case dwarf_r25_mips: return "r25"; case dwarf_r26_mips: return "r26"; case dwarf_r27_mips: return "r27"; case dwarf_gp_mips: return "gp"; case dwarf_sp_mips: return "sp"; case dwarf_r30_mips: return "fp"; case dwarf_ra_mips: return "ra"; case dwarf_sr_mips: return "sr"; case dwarf_lo_mips: return "lo"; case dwarf_hi_mips: return "hi"; case dwarf_bad_mips: return "bad"; case dwarf_cause_mips: return "cause"; case dwarf_pc_mips: return "pc"; case dwarf_f0_mips: return "f0"; case dwarf_f1_mips: return "f1"; case dwarf_f2_mips: return "f2"; case dwarf_f3_mips: return "f3"; case dwarf_f4_mips: return "f4"; case dwarf_f5_mips: return "f5"; case dwarf_f6_mips: return "f6"; case dwarf_f7_mips: return "f7"; case dwarf_f8_mips: return "f8"; case dwarf_f9_mips: return "f9"; case dwarf_f10_mips: return "f10"; case dwarf_f11_mips: return "f11"; case dwarf_f12_mips: return "f12"; case dwarf_f13_mips: return "f13"; case dwarf_f14_mips: return "f14"; case dwarf_f15_mips: return "f15"; case dwarf_f16_mips: return "f16"; case dwarf_f17_mips: return "f17"; case dwarf_f18_mips: return "f18"; case dwarf_f19_mips: return "f19"; case dwarf_f20_mips: return "f20"; case dwarf_f21_mips: return "f21"; case dwarf_f22_mips: return "f22"; case dwarf_f23_mips: return "f23"; case dwarf_f24_mips: return "f24"; case dwarf_f25_mips: return "f25"; case dwarf_f26_mips: return "f26"; case dwarf_f27_mips: return "f27"; case dwarf_f28_mips: return "f28"; case dwarf_f29_mips: return "f29"; case dwarf_f30_mips: return "f30"; case dwarf_f31_mips: return "f31"; case dwarf_fcsr_mips: return "fcsr"; case dwarf_fir_mips: return "fir"; case dwarf_w0_mips: return "w0"; case dwarf_w1_mips: return "w1"; case dwarf_w2_mips: return "w2"; case dwarf_w3_mips: return "w3"; case dwarf_w4_mips: return "w4"; case dwarf_w5_mips: return "w5"; case dwarf_w6_mips: return "w6"; case dwarf_w7_mips: return "w7"; case dwarf_w8_mips: return "w8"; case dwarf_w9_mips: return "w9"; case dwarf_w10_mips: return "w10"; case dwarf_w11_mips: return "w11"; case dwarf_w12_mips: return "w12"; case dwarf_w13_mips: return "w13"; case dwarf_w14_mips: return "w14"; case dwarf_w15_mips: return "w15"; case dwarf_w16_mips: return "w16"; case dwarf_w17_mips: return "w17"; case dwarf_w18_mips: return "w18"; case dwarf_w19_mips: return "w19"; case dwarf_w20_mips: return "w20"; case dwarf_w21_mips: return "w21"; case dwarf_w22_mips: return "w22"; case dwarf_w23_mips: return "w23"; case dwarf_w24_mips: return "w24"; case dwarf_w25_mips: return "w25"; case dwarf_w26_mips: return "w26"; case dwarf_w27_mips: return "w27"; case dwarf_w28_mips: return "w28"; case dwarf_w29_mips: return "w29"; case dwarf_w30_mips: return "w30"; case dwarf_w31_mips: return "w31"; case dwarf_mcsr_mips: return "mcsr"; case dwarf_mir_mips: return "mir"; case dwarf_config5_mips: return "config5"; } return nullptr; } bool EmulateInstructionMIPS::GetRegisterInfo (RegisterKind reg_kind, uint32_t reg_num, RegisterInfo ®_info) { if (reg_kind == eRegisterKindGeneric) { switch (reg_num) { case LLDB_REGNUM_GENERIC_PC: reg_kind = eRegisterKindDWARF; reg_num = dwarf_pc_mips; break; case LLDB_REGNUM_GENERIC_SP: reg_kind = eRegisterKindDWARF; reg_num = dwarf_sp_mips; break; case LLDB_REGNUM_GENERIC_FP: reg_kind = eRegisterKindDWARF; reg_num = dwarf_r30_mips; break; case LLDB_REGNUM_GENERIC_RA: reg_kind = eRegisterKindDWARF; reg_num = dwarf_ra_mips; break; case LLDB_REGNUM_GENERIC_FLAGS: reg_kind = eRegisterKindDWARF; reg_num = dwarf_sr_mips; break; default: return false; } } if (reg_kind == eRegisterKindDWARF) { ::memset (®_info, 0, sizeof(RegisterInfo)); ::memset (reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds)); if (reg_num == dwarf_sr_mips || reg_num == dwarf_fcsr_mips || reg_num == dwarf_fir_mips || reg_num == dwarf_mcsr_mips || reg_num == dwarf_mir_mips || reg_num == dwarf_config5_mips) { reg_info.byte_size = 4; reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; } else if ((int)reg_num >= dwarf_zero_mips && (int)reg_num <= dwarf_f31_mips) { reg_info.byte_size = 4; reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; } else if ((int)reg_num >= dwarf_w0_mips && (int)reg_num <= dwarf_w31_mips) { reg_info.byte_size = 16; reg_info.format = eFormatVectorOfUInt8; reg_info.encoding = eEncodingVector; } else { return false; } reg_info.name = GetRegisterName (reg_num, false); reg_info.alt_name = GetRegisterName (reg_num, true); reg_info.kinds[eRegisterKindDWARF] = reg_num; switch (reg_num) { case dwarf_r30_mips: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; break; case dwarf_ra_mips: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; break; case dwarf_sp_mips: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; break; case dwarf_pc_mips: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; break; case dwarf_sr_mips: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; break; default: break; } return true; } return false; } EmulateInstructionMIPS::MipsOpcode* EmulateInstructionMIPS::GetOpcodeForInstruction (const char *op_name) { static EmulateInstructionMIPS::MipsOpcode g_opcodes[] = { //---------------------------------------------------------------------- // Prologue/Epilogue instructions //---------------------------------------------------------------------- { "ADDiu", &EmulateInstructionMIPS::Emulate_ADDiu, "ADDIU rt,rs,immediate" }, { "SW", &EmulateInstructionMIPS::Emulate_SW, "SW rt,offset(rs)" }, { "LW", &EmulateInstructionMIPS::Emulate_LW, "LW rt,offset(base)" }, //---------------------------------------------------------------------- // MicroMIPS Prologue/Epilogue instructions //---------------------------------------------------------------------- { "ADDIUSP_MM", &EmulateInstructionMIPS::Emulate_ADDIUSP, "ADDIU immediate" }, { "ADDIUS5_MM", &EmulateInstructionMIPS::Emulate_ADDIUS5, "ADDIUS5 rd,immediate" }, { "SWSP_MM", &EmulateInstructionMIPS::Emulate_SWSP, "SWSP rt,offset(sp)" }, { "SWM16_MM", &EmulateInstructionMIPS::Emulate_SWM16_32, "SWM16 reglist,offset(sp)" }, { "SWM32_MM", &EmulateInstructionMIPS::Emulate_SWM16_32, "SWM32 reglist,offset(base)" }, { "SWP_MM", &EmulateInstructionMIPS::Emulate_SWM16_32, "SWP rs1,offset(base)" }, { "LWSP_MM", &EmulateInstructionMIPS::Emulate_LWSP, "LWSP rt,offset(sp)" }, { "LWM16_MM", &EmulateInstructionMIPS::Emulate_LWM16_32, "LWM16 reglist,offset(sp)" }, { "LWM32_MM", &EmulateInstructionMIPS::Emulate_LWM16_32, "LWM32 reglist,offset(base)" }, { "LWP_MM", &EmulateInstructionMIPS::Emulate_LWM16_32, "LWP rd,offset(base)" }, { "JRADDIUSP", &EmulateInstructionMIPS::Emulate_JRADDIUSP, "JRADDIUSP immediate" }, //---------------------------------------------------------------------- // Load/Store instructions //---------------------------------------------------------------------- /* Following list of emulated instructions are required by implementation of hardware watchpoint for MIPS in lldb. As we just need the address accessed by instructions, we have generalised all these instructions in 2 functions depending on their addressing modes */ { "LB", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LB rt, offset(base)" }, { "LBE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LBE rt, offset(base)" }, { "LBU", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LBU rt, offset(base)" }, { "LBUE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LBUE rt, offset(base)" }, { "LDC1", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LDC1 ft, offset(base)" }, { "LD", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LD rt, offset(base)" }, { "LDL", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LDL rt, offset(base)" }, { "LDR", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LDR rt, offset(base)" }, { "LLD", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LLD rt, offset(base)" }, { "LDC2", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LDC2 rt, offset(base)" }, { "LDXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg, "LDXC1 fd, index (base)" }, { "LH", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LH rt, offset(base)" }, { "LHE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LHE rt, offset(base)" }, { "LHU", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LHU rt, offset(base)" }, { "LHUE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LHUE rt, offset(base)" }, { "LL", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LL rt, offset(base)" }, { "LLE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LLE rt, offset(base)" }, { "LUXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg, "LUXC1 fd, index (base)" }, { "LW", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LW rt, offset(base)" }, { "LWC1", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LWC1 ft, offset(base)" }, { "LWC2", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LWC2 rt, offset(base)" }, { "LWE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LWE rt, offset(base)" }, { "LWL", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LWL rt, offset(base)" }, { "LWLE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LWLE rt, offset(base)" }, { "LWR", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LWR rt, offset(base)" }, { "LWRE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LWRE rt, offset(base)" }, { "LWXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg, "LWXC1 fd, index (base)" }, { "LLX", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LLX rt, offset(base)" }, { "LLXE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LLXE rt, offset(base)" }, { "LLDX", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LLDX rt, offset(base)" }, { "SB", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SB rt, offset(base)" }, { "SBE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SBE rt, offset(base)" }, { "SC", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SC rt, offset(base)" }, { "SCE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SCE rt, offset(base)" }, { "SCD", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SCD rt, offset(base)" }, { "SD", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SD rt, offset(base)" }, { "SDL", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SDL rt, offset(base)" }, { "SDR", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SDR rt, offset(base)" }, { "SDC1", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SDC1 ft, offset(base)" }, { "SDC2", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SDC2 rt, offset(base)" }, { "SDXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg, "SDXC1 fs, index(base)" }, { "SH", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SH rt, offset(base)" }, { "SHE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SHE rt, offset(base)" }, { "SUXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg, "SUXC1 fs, index (base)" }, { "SWC1", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SWC1 ft, offset(base)" }, { "SWC2", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SWC2 rt, offset(base)" }, { "SWE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SWE rt, offset(base)" }, { "SWL", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SWL rt, offset(base)" }, { "SWLE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SWLE rt, offset(base)" }, { "SWR", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SWR rt, offset(base)" }, { "SWRE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SWRE rt, offset(base)" }, { "SWXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg, "SWXC1 fs, index (base)" }, { "SCX", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SCX rt, offset(base)" }, { "SCXE", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SCXE rt, offset(base)" }, { "SCDX", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SCDX rt, offset(base)" }, //---------------------------------------------------------------------- // MicroMIPS Load/Store instructions //---------------------------------------------------------------------- { "LBU16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LBU16 rt, decoded_offset(base)" }, { "LHU16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LHU16 rt, left_shifted_offset(base)" }, { "LW16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LW16 rt, left_shifted_offset(base)" }, { "LWGP_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, "LWGP rt, left_shifted_offset(gp)" }, { "SH16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SH16 rt, left_shifted_offset(base)" }, { "SW16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SW16 rt, left_shifted_offset(base)" }, { "SW_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SWSP rt, left_shifted_offset(base)" }, { "SB16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm, "SB16 rt, offset(base)" }, //---------------------------------------------------------------------- // Branch instructions //---------------------------------------------------------------------- - { "BEQ", &EmulateInstructionMIPS::Emulate_BEQ, "BEQ rs,rt,offset" }, - { "BNE", &EmulateInstructionMIPS::Emulate_BNE, "BNE rs,rt,offset" }, - { "BEQL", &EmulateInstructionMIPS::Emulate_BEQL, "BEQL rs,rt,offset" }, - { "BNEL", &EmulateInstructionMIPS::Emulate_BNEL, "BNEL rs,rt,offset" }, - { "BGEZALL", &EmulateInstructionMIPS::Emulate_BGEZALL, "BGEZALL rt,offset" }, + { "BEQ", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BEQ rs,rt,offset" }, + { "BNE", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BNE rs,rt,offset" }, + { "BEQL", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BEQL rs,rt,offset" }, + { "BNEL", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BNEL rs,rt,offset" }, + { "BGEZALL", &EmulateInstructionMIPS::Emulate_Bcond_Link, "BGEZALL rt,offset" }, { "BAL", &EmulateInstructionMIPS::Emulate_BAL, "BAL offset" }, - { "BGEZAL", &EmulateInstructionMIPS::Emulate_BGEZAL, "BGEZAL rs,offset" }, + { "BGEZAL", &EmulateInstructionMIPS::Emulate_Bcond_Link, "BGEZAL rs,offset" }, { "BALC", &EmulateInstructionMIPS::Emulate_BALC, "BALC offset" }, { "BC", &EmulateInstructionMIPS::Emulate_BC, "BC offset" }, - { "BGEZ", &EmulateInstructionMIPS::Emulate_BGEZ, "BGEZ rs,offset" }, - { "BLEZALC", &EmulateInstructionMIPS::Emulate_BLEZALC, "BLEZALC rs,offset" }, - { "BGEZALC", &EmulateInstructionMIPS::Emulate_BGEZALC, "BGEZALC rs,offset" }, - { "BLTZALC", &EmulateInstructionMIPS::Emulate_BLTZALC, "BLTZALC rs,offset" }, - { "BGTZALC", &EmulateInstructionMIPS::Emulate_BGTZALC, "BGTZALC rs,offset" }, - { "BEQZALC", &EmulateInstructionMIPS::Emulate_BEQZALC, "BEQZALC rs,offset" }, - { "BNEZALC", &EmulateInstructionMIPS::Emulate_BNEZALC, "BNEZALC rs,offset" }, - { "BEQC", &EmulateInstructionMIPS::Emulate_BEQC, "BEQC rs,rt,offset" }, - { "BNEC", &EmulateInstructionMIPS::Emulate_BNEC, "BNEC rs,rt,offset" }, - { "BLTC", &EmulateInstructionMIPS::Emulate_BLTC, "BLTC rs,rt,offset" }, - { "BGEC", &EmulateInstructionMIPS::Emulate_BGEC, "BGEC rs,rt,offset" }, - { "BLTUC", &EmulateInstructionMIPS::Emulate_BLTUC, "BLTUC rs,rt,offset" }, - { "BGEUC", &EmulateInstructionMIPS::Emulate_BGEUC, "BGEUC rs,rt,offset" }, - { "BLTZC", &EmulateInstructionMIPS::Emulate_BLTZC, "BLTZC rt,offset" }, - { "BLEZC", &EmulateInstructionMIPS::Emulate_BLEZC, "BLEZC rt,offset" }, - { "BGEZC", &EmulateInstructionMIPS::Emulate_BGEZC, "BGEZC rt,offset" }, - { "BGTZC", &EmulateInstructionMIPS::Emulate_BGTZC, "BGTZC rt,offset" }, - { "BEQZC", &EmulateInstructionMIPS::Emulate_BEQZC, "BEQZC rt,offset" }, - { "BNEZC", &EmulateInstructionMIPS::Emulate_BNEZC, "BNEZC rt,offset" }, - { "BGEZL", &EmulateInstructionMIPS::Emulate_BGEZL, "BGEZL rt,offset" }, - { "BGTZ", &EmulateInstructionMIPS::Emulate_BGTZ, "BGTZ rt,offset" }, - { "BGTZL", &EmulateInstructionMIPS::Emulate_BGTZL, "BGTZL rt,offset" }, - { "BLEZ", &EmulateInstructionMIPS::Emulate_BLEZ, "BLEZ rt,offset" }, - { "BLEZL", &EmulateInstructionMIPS::Emulate_BLEZL, "BLEZL rt,offset" }, - { "BLTZ", &EmulateInstructionMIPS::Emulate_BLTZ, "BLTZ rt,offset" }, - { "BLTZAL", &EmulateInstructionMIPS::Emulate_BLTZAL, "BLTZAL rt,offset" }, - { "BLTZALL", &EmulateInstructionMIPS::Emulate_BLTZALL, "BLTZALL rt,offset" }, - { "BLTZL", &EmulateInstructionMIPS::Emulate_BLTZL, "BLTZL rt,offset" }, - { "BOVC", &EmulateInstructionMIPS::Emulate_BOVC, "BOVC rs,rt,offset" }, - { "BNVC", &EmulateInstructionMIPS::Emulate_BNVC, "BNVC rs,rt,offset" }, + { "BGEZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGEZ rs,offset" }, + { "BLEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BLEZALC rs,offset" }, + { "BGEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BGEZALC rs,offset" }, + { "BLTZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BLTZALC rs,offset" }, + { "BGTZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BGTZALC rs,offset" }, + { "BEQZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BEQZALC rs,offset" }, + { "BNEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BNEZALC rs,offset" }, + { "BEQC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BEQC rs,rt,offset" }, + { "BNEC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BNEC rs,rt,offset" }, + { "BLTC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BLTC rs,rt,offset" }, + { "BGEC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BGEC rs,rt,offset" }, + { "BLTUC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BLTUC rs,rt,offset" }, + { "BGEUC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BGEUC rs,rt,offset" }, + { "BLTZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BLTZC rt,offset" }, + { "BLEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BLEZC rt,offset" }, + { "BGEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BGEZC rt,offset" }, + { "BGTZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BGTZC rt,offset" }, + { "BEQZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BEQZC rt,offset" }, + { "BNEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BNEZC rt,offset" }, + { "BGEZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGEZL rt,offset" }, + { "BGTZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGTZ rt,offset" }, + { "BGTZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGTZL rt,offset" }, + { "BLEZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLEZ rt,offset" }, + { "BLEZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLEZL rt,offset" }, + { "BLTZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLTZ rt,offset" }, + { "BLTZAL", &EmulateInstructionMIPS::Emulate_Bcond_Link, "BLTZAL rt,offset" }, + { "BLTZALL", &EmulateInstructionMIPS::Emulate_Bcond_Link, "BLTZALL rt,offset" }, + { "BLTZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLTZL rt,offset" }, + { "BOVC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BOVC rs,rt,offset" }, + { "BNVC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BNVC rs,rt,offset" }, { "J", &EmulateInstructionMIPS::Emulate_J, "J target" }, { "JAL", &EmulateInstructionMIPS::Emulate_JAL, "JAL target" }, { "JALX", &EmulateInstructionMIPS::Emulate_JAL, "JALX target" }, { "JALR", &EmulateInstructionMIPS::Emulate_JALR, "JALR target" }, { "JALR_HB", &EmulateInstructionMIPS::Emulate_JALR, "JALR.HB target" }, { "JIALC", &EmulateInstructionMIPS::Emulate_JIALC, "JIALC rt,offset" }, { "JIC", &EmulateInstructionMIPS::Emulate_JIC, "JIC rt,offset" }, { "JR", &EmulateInstructionMIPS::Emulate_JR, "JR target" }, { "JR_HB", &EmulateInstructionMIPS::Emulate_JR, "JR.HB target" }, - { "BC1F", &EmulateInstructionMIPS::Emulate_BC1F, "BC1F cc, offset" }, - { "BC1T", &EmulateInstructionMIPS::Emulate_BC1T, "BC1T cc, offset" }, - { "BC1FL", &EmulateInstructionMIPS::Emulate_BC1FL, "BC1FL cc, offset" }, - { "BC1TL", &EmulateInstructionMIPS::Emulate_BC1TL, "BC1TL cc, offset" }, + { "BC1F", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1F cc, offset" }, + { "BC1T", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1T cc, offset" }, + { "BC1FL", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1FL cc, offset" }, + { "BC1TL", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1TL cc, offset" }, { "BC1EQZ", &EmulateInstructionMIPS::Emulate_BC1EQZ, "BC1EQZ ft, offset" }, { "BC1NEZ", &EmulateInstructionMIPS::Emulate_BC1NEZ, "BC1NEZ ft, offset" }, - { "BC1ANY2F", &EmulateInstructionMIPS::Emulate_BC1ANY2F, "BC1ANY2F cc, offset" }, - { "BC1ANY2T", &EmulateInstructionMIPS::Emulate_BC1ANY2T, "BC1ANY2T cc, offset" }, - { "BC1ANY4F", &EmulateInstructionMIPS::Emulate_BC1ANY4F, "BC1ANY4F cc, offset" }, - { "BC1ANY4T", &EmulateInstructionMIPS::Emulate_BC1ANY4T, "BC1ANY4T cc, offset" }, + { "BC1ANY2F", &EmulateInstructionMIPS::Emulate_3D_branch, "BC1ANY2F cc, offset" }, + { "BC1ANY2T", &EmulateInstructionMIPS::Emulate_3D_branch, "BC1ANY2T cc, offset" }, + { "BC1ANY4F", &EmulateInstructionMIPS::Emulate_3D_branch, "BC1ANY4F cc, offset" }, + { "BC1ANY4T", &EmulateInstructionMIPS::Emulate_3D_branch, "BC1ANY4T cc, offset" }, { "BNZ_B", &EmulateInstructionMIPS::Emulate_BNZB, "BNZ.b wt,s16" }, { "BNZ_H", &EmulateInstructionMIPS::Emulate_BNZH, "BNZ.h wt,s16" }, { "BNZ_W", &EmulateInstructionMIPS::Emulate_BNZW, "BNZ.w wt,s16" }, { "BNZ_D", &EmulateInstructionMIPS::Emulate_BNZD, "BNZ.d wt,s16" }, { "BZ_B", &EmulateInstructionMIPS::Emulate_BZB, "BZ.b wt,s16" }, { "BZ_H", &EmulateInstructionMIPS::Emulate_BZH, "BZ.h wt,s16" }, { "BZ_W", &EmulateInstructionMIPS::Emulate_BZW, "BZ.w wt,s16" }, { "BZ_D", &EmulateInstructionMIPS::Emulate_BZD, "BZ.d wt,s16" }, { "BNZ_V", &EmulateInstructionMIPS::Emulate_BNZV, "BNZ.V wt,s16" }, { "BZ_V", &EmulateInstructionMIPS::Emulate_BZV, "BZ.V wt,s16" }, //---------------------------------------------------------------------- // MicroMIPS Branch instructions //---------------------------------------------------------------------- { "B16_MM", &EmulateInstructionMIPS::Emulate_B16_MM, "B16 offset" }, { "BEQZ16_MM", &EmulateInstructionMIPS::Emulate_Branch_MM, "BEQZ16 rs, offset" }, { "BNEZ16_MM", &EmulateInstructionMIPS::Emulate_Branch_MM, "BNEZ16 rs, offset" }, { "BEQZC_MM", &EmulateInstructionMIPS::Emulate_Branch_MM, "BEQZC rs, offset" }, { "BNEZC_MM", &EmulateInstructionMIPS::Emulate_Branch_MM, "BNEZC rs, offset" }, { "BGEZALS_MM", &EmulateInstructionMIPS::Emulate_Branch_MM, "BGEZALS rs, offset" }, { "BLTZALS_MM", &EmulateInstructionMIPS::Emulate_Branch_MM, "BLTZALS rs, offset" }, { "JALR16_MM", &EmulateInstructionMIPS::Emulate_JALRx16_MM, "JALR16 rs" }, { "JALRS16_MM", &EmulateInstructionMIPS::Emulate_JALRx16_MM, "JALRS16 rs" }, { "JR16_MM", &EmulateInstructionMIPS::Emulate_JR, "JR16 rs rs" }, { "JRC16_MM", &EmulateInstructionMIPS::Emulate_JR, "JRC16 rs rs" }, { "JALS_MM", &EmulateInstructionMIPS::Emulate_JALx, "JALS target" }, { "JALX_MM", &EmulateInstructionMIPS::Emulate_JALx, "JALX target" }, { "JALRS_MM", &EmulateInstructionMIPS::Emulate_JALRS, "JALRS rt, rs" }, }; static const size_t k_num_mips_opcodes = llvm::array_lengthof(g_opcodes); for (size_t i = 0; i < k_num_mips_opcodes; ++i) { if (! strcasecmp (g_opcodes[i].op_name, op_name)) return &g_opcodes[i]; } return NULL; } uint32_t EmulateInstructionMIPS::GetSizeOfInstruction (lldb_private::DataExtractor& data, uint64_t inst_addr) { uint64_t next_inst_size = 0; llvm::MCInst mc_insn; llvm::MCDisassembler::DecodeStatus decode_status; llvm::ArrayRef raw_insn (data.GetDataStart(), data.GetByteSize()); if (m_use_alt_disaasm) decode_status = m_alt_disasm->getInstruction (mc_insn, next_inst_size, raw_insn, inst_addr, llvm::nulls(), llvm::nulls()); else decode_status = m_disasm->getInstruction (mc_insn, next_inst_size, raw_insn, inst_addr, llvm::nulls(), llvm::nulls()); if (decode_status != llvm::MCDisassembler::Success) return false; return m_insn_info->get(mc_insn.getOpcode()).getSize(); } bool EmulateInstructionMIPS::SetInstruction (const Opcode &insn_opcode, const Address &inst_addr, Target *target) { m_use_alt_disaasm = false; if (EmulateInstruction::SetInstruction (insn_opcode, inst_addr, target)) { if (inst_addr.GetAddressClass() == eAddressClassCodeAlternateISA) { Error error; lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; /* * The address belongs to microMIPS function. To find the size of * next instruction use microMIPS disassembler. */ m_use_alt_disaasm = true; uint32_t current_inst_size = insn_opcode.GetByteSize(); uint8_t buf[sizeof(uint32_t)]; uint64_t next_inst_addr = (m_addr & (~1ull)) + current_inst_size; Address next_addr (next_inst_addr); const size_t bytes_read = target->ReadMemory (next_addr, /* Address of next instruction */ true, /* prefer_file_cache */ buf, sizeof(uint32_t), error, &load_addr); if (bytes_read == 0) return true; DataExtractor data (buf, sizeof(uint32_t), GetByteOrder(), GetAddressByteSize()); m_next_inst_size = GetSizeOfInstruction (data, next_inst_addr); return true; } else { /* * If the address class is not eAddressClassCodeAlternateISA then * the function is not microMIPS. In this case instruction size is * always 4 bytes. */ m_next_inst_size = 4; return true; } } return false; } bool EmulateInstructionMIPS::ReadInstruction () { bool success = false; m_addr = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_ADDRESS, &success); if (success) { Context read_inst_context; read_inst_context.type = eContextReadOpcode; read_inst_context.SetNoArgs (); m_opcode.SetOpcode32 (ReadMemoryUnsigned (read_inst_context, m_addr, 4, 0, &success), GetByteOrder()); } if (!success) m_addr = LLDB_INVALID_ADDRESS; return success; } bool EmulateInstructionMIPS::EvaluateInstruction (uint32_t evaluate_options) { bool success = false; llvm::MCInst mc_insn; uint64_t insn_size; DataExtractor data; /* Keep the complexity of the decode logic with the llvm::MCDisassembler class. */ if (m_opcode.GetData (data)) { llvm::MCDisassembler::DecodeStatus decode_status; llvm::ArrayRef raw_insn (data.GetDataStart(), data.GetByteSize()); if (m_use_alt_disaasm) decode_status = m_alt_disasm->getInstruction (mc_insn, insn_size, raw_insn, m_addr, llvm::nulls(), llvm::nulls()); else decode_status = m_disasm->getInstruction (mc_insn, insn_size, raw_insn, m_addr, llvm::nulls(), llvm::nulls()); if (decode_status != llvm::MCDisassembler::Success) return false; } /* * mc_insn.getOpcode() returns decoded opcode. However to make use * of llvm::Mips:: we would need "MipsGenInstrInfo.inc". */ const char *op_name = m_insn_info->getName (mc_insn.getOpcode ()); if (op_name == NULL) return false; /* * Decoding has been done already. Just get the call-back function * and emulate the instruction. */ MipsOpcode *opcode_data = GetOpcodeForInstruction (op_name); if (opcode_data == NULL) return false; uint64_t old_pc = 0, new_pc = 0; const bool auto_advance_pc = evaluate_options & eEmulateInstructionOptionAutoAdvancePC; if (auto_advance_pc) { old_pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; } /* emulate instruction */ success = (this->*opcode_data->callback) (mc_insn); if (!success) return false; if (auto_advance_pc) { new_pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; /* If we haven't changed the PC, change it here */ if (old_pc == new_pc) { new_pc += 4; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, new_pc)) return false; } } return true; } bool EmulateInstructionMIPS::CreateFunctionEntryUnwind (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); UnwindPlan::RowSP row(new UnwindPlan::Row); const bool can_replace = false; // Our previous Call Frame Address is the stack pointer row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_sp_mips, 0); // Our previous PC is in the RA row->SetRegisterLocationToRegister(dwarf_pc_mips, dwarf_ra_mips, can_replace); unwind_plan.AppendRow (row); // All other registers are the same. unwind_plan.SetSourceName ("EmulateInstructionMIPS"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes); unwind_plan.SetReturnAddressRegister (dwarf_ra_mips); return true; } bool EmulateInstructionMIPS::nonvolatile_reg_p (uint32_t regnum) { switch (regnum) { case dwarf_r16_mips: case dwarf_r17_mips: case dwarf_r18_mips: case dwarf_r19_mips: case dwarf_r20_mips: case dwarf_r21_mips: case dwarf_r22_mips: case dwarf_r23_mips: case dwarf_gp_mips: case dwarf_sp_mips: case dwarf_r30_mips: case dwarf_ra_mips: return true; default: return false; } return false; } bool EmulateInstructionMIPS::Emulate_ADDiu (llvm::MCInst& insn) { bool success = false; const uint32_t imm16 = insn.getOperand(2).getImm(); uint32_t imm = SignedBits(imm16, 15, 0); uint64_t result; uint32_t src, dst; dst = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); src = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); /* Check if this is addiu sp,,imm16 */ if (dst == dwarf_sp_mips) { /* read register */ uint64_t src_opd_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + src, 0, &success); if (!success) return false; result = src_opd_val + imm; Context context; RegisterInfo reg_info_sp; if (GetRegisterInfo (eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp)) context.SetRegisterPlusOffset (reg_info_sp, imm); /* We are allocating bytes on stack */ context.type = eContextAdjustStackPointer; WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_sp_mips, result); } return true; } bool EmulateInstructionMIPS::Emulate_SW (llvm::MCInst& insn) { bool success = false; uint32_t imm16 = insn.getOperand(2).getImm(); uint32_t imm = SignedBits(imm16, 15, 0); uint32_t src, base; int32_t address; Context bad_vaddr_context; RegisterInfo reg_info_base; src = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + base, reg_info_base)) return false; /* read base register */ address = (int32_t)ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); if (!success) return false; /* destination address */ address = address + imm; /* Set the bad_vaddr register with base address used in the instruction */ bad_vaddr_context.type = eContextInvalid; WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips, address); /* We look for sp based non-volatile register stores */ if (base == dwarf_sp_mips && nonvolatile_reg_p (src)) { RegisterInfo reg_info_src; if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + src, reg_info_src)) return false; Context context; RegisterValue data_src; context.type = eContextPushRegisterOnStack; context.SetRegisterToRegisterPlusOffset (reg_info_src, reg_info_base, 0); uint8_t buffer [RegisterValue::kMaxRegisterByteSize]; Error error; if (!ReadRegister (®_info_base, data_src)) return false; if (data_src.GetAsMemoryData (®_info_src, buffer, reg_info_src.byte_size, eByteOrderLittle, error) == 0) return false; if (!WriteMemory (context, address, buffer, reg_info_src.byte_size)) return false; return true; } return false; } bool EmulateInstructionMIPS::Emulate_LW (llvm::MCInst& insn) { bool success =false; uint32_t src, base; int32_t imm, address; Context bad_vaddr_context; src = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); imm = insn.getOperand(2).getImm(); RegisterInfo reg_info_base; if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + base, reg_info_base)) return false; /* read base register */ address = (int32_t)ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); if (!success) return false; /* destination address */ address = address + imm; /* Set the bad_vaddr register with base address used in the instruction */ bad_vaddr_context.type = eContextInvalid; WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips, address); if (base == dwarf_sp_mips && nonvolatile_reg_p (src)) { RegisterValue data_src; RegisterInfo reg_info_src; if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + src, reg_info_src)) return false; Context context; context.type = eContextPopRegisterOffStack; context.SetAddress (address); if (!WriteRegister (context, ®_info_src, data_src)) return false; return true; } return false; } bool EmulateInstructionMIPS::Emulate_ADDIUSP (llvm::MCInst& insn) { bool success = false; const uint32_t imm9 = insn.getOperand(0).getImm(); uint64_t result; // This instruction operates implicitly on stack pointer, so read register. uint64_t src_opd_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_sp_mips, 0, &success); if (!success) return false; result = src_opd_val + imm9; Context context; RegisterInfo reg_info_sp; if (GetRegisterInfo (eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp)) context.SetRegisterPlusOffset (reg_info_sp, imm9); // We are adjusting the stack. context.type = eContextAdjustStackPointer; WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_sp_mips, result); return true; } bool EmulateInstructionMIPS::Emulate_ADDIUS5 (llvm::MCInst& insn) { bool success = false; uint32_t base; const uint32_t imm4 = insn.getOperand(2).getImm(); uint64_t result; // The source and destination register is same for this instruction. base = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); // We are looking for stack adjustment only if (base == dwarf_sp_mips) { // Read stack pointer register uint64_t src_opd_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); if (!success) return false; result = src_opd_val + imm4; Context context; RegisterInfo reg_info_sp; if (GetRegisterInfo (eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp)) context.SetRegisterPlusOffset (reg_info_sp, imm4); // We are adjusting the stack. context.type = eContextAdjustStackPointer; WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_sp_mips, result); } return true; } bool EmulateInstructionMIPS::Emulate_SWSP (llvm::MCInst& insn) { bool success = false; uint32_t imm5 = insn.getOperand(2).getImm(); uint32_t src, base; Context bad_vaddr_context; uint32_t address; src = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); RegisterInfo reg_info_base; if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + base, reg_info_base)) return false; // read base register address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); if (!success) return false; // destination address address = address + imm5; // We use bad_vaddr_context to store base address which is used by H/W watchpoint // Set the bad_vaddr register with base address used in the instruction bad_vaddr_context.type = eContextInvalid; WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips, address); // We look for sp based non-volatile register stores. if (base == dwarf_sp_mips && nonvolatile_reg_p (src)) { RegisterInfo reg_info_src; Context context; RegisterValue data_src; context.type = eContextPushRegisterOnStack; context.SetRegisterToRegisterPlusOffset (reg_info_src, reg_info_base, 0); uint8_t buffer [RegisterValue::kMaxRegisterByteSize]; Error error; if (!ReadRegister (®_info_base, data_src)) return false; if (data_src.GetAsMemoryData (®_info_src, buffer, reg_info_src.byte_size, eByteOrderLittle, error) == 0) return false; if (!WriteMemory (context, address, buffer, reg_info_src.byte_size)) return false; return true; } return false; } /* Emulate SWM16,SWM32 and SWP instruction. SWM16 always has stack pointer as a base register (but it is still available in MCInst as an operand). SWM32 and SWP can have base register other than stack pointer. */ bool EmulateInstructionMIPS::Emulate_SWM16_32 (llvm::MCInst& insn) { bool success = false; uint32_t src, base; uint32_t num_operands = insn.getNumOperands(); // No of operands vary based on no of regs to store. // Base register is second last operand of the instruction. base = m_reg_info->getEncodingValue (insn.getOperand(num_operands-2).getReg()); // We are looking for sp based stores so if base is not a stack pointer then don't proceed. if (base != dwarf_sp_mips) return false; // offset is always the last operand. uint32_t offset = insn.getOperand(num_operands-1).getImm(); RegisterInfo reg_info_base; RegisterInfo reg_info_src; if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + base, reg_info_base)) return false; // read SP uint32_t base_address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); if (!success) return false; // Resulting base addrss base_address = base_address + offset; // Total no of registers to be stored are num_operands-2. for (uint32_t i = 0; i < num_operands - 2; i++) { // Get the register number to be stored. src = m_reg_info->getEncodingValue (insn.getOperand(i).getReg()); /* Record only non-volatile stores. This check is required for SWP instruction because source operand could be any register. SWM16 and SWM32 instruction always has saved registers as source operands. */ if (!nonvolatile_reg_p (src)) return false; if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + src, reg_info_src)) return false; Context context; RegisterValue data_src; context.type = eContextPushRegisterOnStack; context.SetRegisterToRegisterPlusOffset (reg_info_src, reg_info_base, 0); uint8_t buffer [RegisterValue::kMaxRegisterByteSize]; Error error; if (!ReadRegister (®_info_base, data_src)) return false; if (data_src.GetAsMemoryData (®_info_src, buffer, reg_info_src.byte_size, eByteOrderLittle, error) == 0) return false; if (!WriteMemory (context, base_address, buffer, reg_info_src.byte_size)) return false; // Stack address for next register base_address = base_address + reg_info_src.byte_size; } return true; } bool EmulateInstructionMIPS::Emulate_LWSP (llvm::MCInst& insn) { bool success = false; uint32_t src = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); uint32_t base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); uint32_t imm5 = insn.getOperand(2).getImm(); Context bad_vaddr_context; RegisterInfo reg_info_base; if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + base, reg_info_base)) return false; // read base register uint32_t base_address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); if (!success) return false; base_address = base_address + imm5; // We use bad_vaddr_context to store base address which is used by H/W watchpoint // Set the bad_vaddr register with base address used in the instruction bad_vaddr_context.type = eContextInvalid; WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips, base_address); if (base == dwarf_sp_mips && nonvolatile_reg_p (src)) { RegisterValue data_src; RegisterInfo reg_info_src; if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + src, reg_info_src)) return false; Context context; context.type = eContextPopRegisterOffStack; context.SetAddress (base_address); if (!WriteRegister (context, ®_info_src, data_src)) return false; return true; } return false; } /* Emulate LWM16, LWM32 and LWP instructions. LWM16 always has stack pointer as a base register (but it is still available in MCInst as an operand). LWM32 and LWP can have base register other than stack pointer. */ bool EmulateInstructionMIPS::Emulate_LWM16_32 (llvm::MCInst& insn) { bool success = false; uint32_t dst, base; uint32_t num_operands = insn.getNumOperands(); // No of operands vary based on no of regs to store. uint32_t imm = insn.getOperand(num_operands-1).getImm(); // imm is the last operand in the instruction. // Base register is second last operand of the instruction. base = m_reg_info->getEncodingValue (insn.getOperand(num_operands-2).getReg()); // We are looking for sp based loads so if base is not a stack pointer then don't proceed. if (base != dwarf_sp_mips) return false; uint32_t base_address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); if (!success) return false; base_address = base_address + imm; RegisterValue data_dst; RegisterInfo reg_info_dst; // Total no of registers to be re-stored are num_operands-2. for (uint32_t i = 0; i < num_operands - 2; i++) { // Get the register number to be re-stored. dst = m_reg_info->getEncodingValue (insn.getOperand(i).getReg()); /* Record only non-volatile loads. This check is required for LWP instruction because destination operand could be any register. LWM16 and LWM32 instruction always has saved registers as destination operands. */ if (!nonvolatile_reg_p (dst)) return false; if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + dst, reg_info_dst)) return false; Context context; context.type = eContextPopRegisterOffStack; context.SetAddress (base_address + (i*4)); if (!WriteRegister (context, ®_info_dst, data_dst)) return false; } return true; } bool EmulateInstructionMIPS::Emulate_JRADDIUSP (llvm::MCInst& insn) { bool success = false; int32_t imm5 = insn.getOperand(0).getImm(); /* JRADDIUSP immediate * PC <- RA * SP <- SP + zero_extend(Immediate << 2) */ // This instruction operates implicitly on stack pointer, so read register. int32_t src_opd_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_sp_mips, 0, &success); if (!success) return false; int32_t ra_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_ra_mips, 0, &success); if (!success) return false; int32_t result = src_opd_val + imm5; Context context; // Update the PC if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, ra_val)) return false; RegisterInfo reg_info_sp; if (GetRegisterInfo (eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp)) context.SetRegisterPlusOffset (reg_info_sp, imm5); // We are adjusting stack context.type = eContextAdjustStackPointer; // update SP if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_sp_mips, result)) return false; return true; } +static int +IsAdd64bitOverflow (int32_t a, int32_t b) +{ + int32_t r = (uint32_t) a + (uint32_t) b; + return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0); +} + +/* + Emulate below MIPS branch instructions. + BEQ, BNE : Branch on condition + BEQL, BNEL : Branch likely +*/ bool -EmulateInstructionMIPS::Emulate_BEQ (llvm::MCInst& insn) +EmulateInstructionMIPS::Emulate_BXX_3ops (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; + int32_t offset, pc, target = 0, rs_val, rt_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); - /* - * BEQ rs, rt, offset - * condition <- (GPR[rs] = GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); if (!success) return false; - if (rs_val == rt_val) - target = pc + offset; - else - target = pc + 8; + if (!strcasecmp (op_name, "BEQ") || + !strcasecmp (op_name, "BEQL")) + { + if (rs_val == rt_val) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BNE") || + !strcasecmp (op_name, "BNEL")) + { + if (rs_val != rt_val) + target = pc + offset; + else + target = pc + 8; + } Context context; context.type = eContextRelativeBranchImmediate; + context.SetImmediate (offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + return true; +} + +/* + Emulate below MIPS branch instructions. + BEQC, BNEC, BLTC, BGEC, BLTUC, BGEUC, BOVC, BNVC: Compact branch instructions with no delay slot +*/ +bool +EmulateInstructionMIPS::Emulate_BXX_3ops_C (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs, rt; + int32_t offset, pc, target = 0, rs_val, rt_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize(); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); + offset = insn.getOperand(2).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) return false; + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BEQC")) + { + if (rs_val == rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BNEC")) + { + if (rs_val != rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BLTC")) + { + if (rs_val < rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGEC")) + { + if (rs_val >= rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BLTUC")) + { + if (rs_val < rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGEUC")) + { + if ((uint32_t)rs_val >= (uint32_t)rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BOVC")) + { + if (IsAdd64bitOverflow (rs_val, rt_val)) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BNVC")) + { + if (!IsAdd64bitOverflow (rs_val, rt_val)) + target = pc + 4 + offset; + else + target = pc + 4; + } + + Context context; + context.type = eContextRelativeBranchImmediate; + context.SetImmediate (current_inst_size + offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + return true; } +/* + Emulate below MIPS conditional branch and link instructions. + BLEZALC, BGEZALC, BLTZALC, BGTZALC, BEQZALC, BNEZALC : Compact branches +*/ bool +EmulateInstructionMIPS::Emulate_Bcond_Link_C (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs; + int32_t offset, pc, target = 0; + int32_t rs_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + offset = insn.getOperand(1).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BLEZALC")) + { + if (rs_val <= 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGEZALC")) + { + if (rs_val >= 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BLTZALC")) + { + if (rs_val < 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGTZALC")) + { + if (rs_val > 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BEQZALC")) + { + if (rs_val == 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BNEZALC")) + { + if (rs_val != 0) + target = pc + offset; + else + target = pc + 4; + } + + Context context; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) + return false; + + return true; +} + +/* + Emulate below MIPS Non-Compact conditional branch and link instructions. + BLTZAL, BGEZAL : + BLTZALL, BGEZALL : Branch likely +*/ +bool +EmulateInstructionMIPS::Emulate_Bcond_Link (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs; + int32_t offset, pc, target = 0; + int32_t rs_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + offset = insn.getOperand(1).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BLTZAL") || + !strcasecmp (op_name, "BLTZALL")) + { + if ((int32_t) rs_val < 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BGEZAL") || + !strcasecmp (op_name, "BGEZALL")) + { + if ((int32_t) rs_val >= 0) + target = pc + offset; + else + target = pc + 8; + } + + Context context; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) + return false; + + return true; +} + +/* + Emulate below MIPS branch instructions. + BLTZL, BGEZL, BGTZL, BLEZL : Branch likely + BLTZ, BGEZ, BGTZ, BLEZ : Non-compact branches +*/ +bool +EmulateInstructionMIPS::Emulate_BXX_2ops (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs; + int32_t offset, pc, target = 0; + int32_t rs_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + offset = insn.getOperand(1).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BLTZL") || + !strcasecmp (op_name, "BLTZ")) + { + if (rs_val < 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BGEZL") || + !strcasecmp (op_name, "BGEZ")) + { + if (rs_val >= 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BGTZL") || + !strcasecmp (op_name, "BGTZ")) + { + if (rs_val > 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BLEZL") || + !strcasecmp (op_name, "BLEZ")) + { + if (rs_val <= 0) + target = pc + offset; + else + target = pc + 8; + } + + Context context; + context.type = eContextRelativeBranchImmediate; + context.SetImmediate (offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + return true; +} + +/* + Emulate below MIPS branch instructions. + BLTZC, BLEZC, BGEZC, BGTZC, BEQZC, BNEZC : Compact Branches +*/ +bool +EmulateInstructionMIPS::Emulate_BXX_2ops_C (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs; + int32_t offset, pc, target = 0; + int32_t rs_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize(); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + offset = insn.getOperand(1).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BLTZC")) + { + if (rs_val < 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BLEZC")) + { + if (rs_val <= 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGEZC")) + { + if (rs_val >= 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGTZC")) + { + if (rs_val > 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BEQZC")) + { + if (rs_val == 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BNEZC")) + { + if (rs_val != 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + + Context context; + context.type = eContextRelativeBranchImmediate; + context.SetImmediate (current_inst_size + offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + return true; +} + +bool EmulateInstructionMIPS::Emulate_B16_MM (llvm::MCInst& insn) { bool success = false; int32_t offset, pc, target; uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize(); offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; // unconditional branch target = pc + offset; Context context; context.type = eContextRelativeBranchImmediate; context.SetImmediate (current_inst_size + offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; return true; } /* BEQZC, BNEZC are 32 bit compact instructions without a delay slot. BEQZ16, BNEZ16 are 16 bit instructions with delay slot. BGEZALS, BLTZALS are 16 bit instructions with short (2-byte) delay slot. */ bool EmulateInstructionMIPS::Emulate_Branch_MM (llvm::MCInst& insn) { bool success = false; int32_t target = 0; uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize(); const char *op_name = m_insn_info->getName (insn.getOpcode ()); bool update_ra = false; uint32_t ra_offset = 0; /* * BEQZ16 rs, offset * condition <- (GPR[rs] = 0) * if condition then * PC = PC + sign_ext (offset || 0) * * BNEZ16 rs, offset * condition <- (GPR[rs] != 0) * if condition then * PC = PC + sign_ext (offset || 0) * * BEQZC rs, offset (compact instruction: No delay slot) * condition <- (GPR[rs] == 0) * if condition then * PC = PC + 4 + sign_ext (offset || 0) */ uint32_t rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); int32_t offset = insn.getOperand(1).getImm(); int32_t pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; int32_t rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (!strcasecmp (op_name, "BEQZ16_MM")) { if (rs_val == 0) target = pc + offset; else target = pc + current_inst_size + m_next_inst_size; // Skip delay slot instruction. } else if (!strcasecmp (op_name, "BNEZ16_MM")) { if (rs_val != 0) target = pc + offset; else target = pc + current_inst_size + m_next_inst_size; // Skip delay slot instruction. } else if (!strcasecmp (op_name, "BEQZC_MM")) { if (rs_val == 0) target = pc + 4 + offset; else target = pc + 4; // 32 bit instruction and does not have delay slot instruction. } else if (!strcasecmp (op_name, "BNEZC_MM")) { if (rs_val != 0) target = pc + 4 + offset; else target = pc + 4; // 32 bit instruction and does not have delay slot instruction. } else if (!strcasecmp (op_name, "BGEZALS_MM")) { if (rs_val >= 0) target = pc + offset; else target = pc + 6; // 32 bit instruction with short (2-byte) delay slot update_ra = true; ra_offset = 6; } else if (!strcasecmp (op_name, "BLTZALS_MM")) { if (rs_val >= 0) target = pc + offset; else target = pc + 6; // 32 bit instruction with short (2-byte) delay slot update_ra = true; ra_offset = 6; } Context context; context.type = eContextRelativeBranchImmediate; context.SetImmediate (current_inst_size + offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; if (update_ra) { if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + ra_offset)) return false; } return true; } /* Emulate micromips jump instructions. JALR16,JALRS16 */ bool EmulateInstructionMIPS::Emulate_JALRx16_MM (llvm::MCInst& insn) { bool success = false; uint32_t ra_offset = 0; const char *op_name = m_insn_info->getName (insn.getOpcode ()); uint32_t rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); uint32_t pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; uint32_t rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (!strcasecmp (op_name, "JALR16_MM")) ra_offset = 6; // 2-byte instruction with 4-byte delay slot. else if (!strcasecmp (op_name, "JALRS16_MM")) ra_offset = 4; // 2-byte instruction with 2-byte delay slot. Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, rs_val)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + ra_offset)) return false; return true; } /* Emulate JALS and JALX instructions. JALS 32 bit instruction with short (2-byte) delay slot. JALX 32 bit instruction with 4-byte delay slot. */ bool EmulateInstructionMIPS::Emulate_JALx (llvm::MCInst& insn) { bool success = false; uint32_t offset=0, target=0, pc=0, ra_offset=0; const char *op_name = m_insn_info->getName (insn.getOpcode ()); /* * JALS target * RA = PC + 6 * offset = sign_ext (offset << 1) * PC = PC[31-27] | offset * JALX target * RA = PC + 8 * offset = sign_ext (offset << 2) * PC = PC[31-28] | offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; // These are PC-region branches and not PC-relative. if (!strcasecmp (op_name, "JALS_MM")) { // target address is in the “current” 128 MB-aligned region target = (pc & 0xF8000000UL) | offset; ra_offset = 6; } else if (!strcasecmp (op_name, "JALX_MM")) { // target address is in the “current” 256 MB-aligned region target = (pc & 0xF0000000UL) | offset; ra_offset = 8; } Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + ra_offset)) return false; return true; } bool EmulateInstructionMIPS::Emulate_JALRS (llvm::MCInst& insn) { bool success = false; uint32_t rs=0, rt=0; int32_t pc=0, rs_val=0; /* JALRS rt, rs GPR[rt] <- PC + 6 PC <- GPR[rs] */ rt = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rs = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); if (!success) return false; pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, rs_val)) return false; // This is 4-byte instruction with 2-byte delay slot. if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_zero_mips + rt, pc + 6)) return false; return true; } bool -EmulateInstructionMIPS::Emulate_BNE (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BNE rs, rt, offset - * condition <- (GPR[rs] != GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val != rt_val) - target = pc + offset; - else - target = pc + 8; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BEQL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BEQL rs, rt, offset - * condition <- (GPR[rs] = GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val == rt_val) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNEL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BNEL rs, rt, offset - * condition <- (GPR[rs] != GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val != rt_val) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZL rs, offset - * condition <- (GPR[rs] >= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val >= 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZL rs, offset - * condition <- (GPR[rs] < 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val < 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGTZL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGTZL rs, offset - * condition <- (GPR[rs] > 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val > 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLEZL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLEZL rs, offset - * condition <- (GPR[rs] <= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val <= 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGTZ (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGTZ rs, offset - * condition <- (GPR[rs] > 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val > 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLEZ (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLEZ rs, offset - * condition <- (GPR[rs] <= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val <= 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZ (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZ rs, offset - * condition <- (GPR[rs] < 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val < 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZALL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZALL rt, offset - * condition <- (GPR[rs] >= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val >= 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) - return false; - - return true; -} - -bool EmulateInstructionMIPS::Emulate_BAL (llvm::MCInst& insn) { bool success = false; int32_t offset, pc, target; /* * BAL offset * offset = sign_ext (offset << 2) * RA = PC + 8 * PC = PC + offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; target = pc + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BALC (llvm::MCInst& insn) { bool success = false; int32_t offset, pc, target; /* * BALC offset * offset = sign_ext (offset << 2) * RA = PC + 4 * PC = PC + 4 + offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; target = pc + 4 + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) return false; return true; } bool -EmulateInstructionMIPS::Emulate_BGEZAL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZAL rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] >= 0) - * if condition then - * RA = PC + 8 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if ((int32_t) rs_val >= 0) - target = pc + offset; - else - target = pc + 8; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZAL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZAL rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] < 0) - * if condition then - * RA = PC + 8 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if ((int32_t) rs_val < 0) - target = pc + offset; - else - target = pc + 8; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZALL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZALL rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] < 0) - * if condition then - * RA = PC + 8 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val < 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) - return false; - - return true; -} - - -bool -EmulateInstructionMIPS::Emulate_BLEZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLEZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] <= 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val <= 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] >= 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val >= 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] < 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val < 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGTZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGTZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] > 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val > 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BEQZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target, rs_val; - - /* - * BEQZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] == 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val == 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNEZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target, rs_val; - - /* - * BNEZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] != 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val != 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZ (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target, rs_val; - - /* - * BGEZ rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] >= 0) - * if condition then - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val >= 0) - target = pc + offset; - else - target = pc + 8; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool EmulateInstructionMIPS::Emulate_BC (llvm::MCInst& insn) { bool success = false; int32_t offset, pc, target; /* * BC offset * offset = sign_ext (offset << 2) * PC = PC + 4 + offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; target = pc + 4 + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; return true; } bool -EmulateInstructionMIPS::Emulate_BEQC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BEQC rs, rt, offset - * condition <- (GPR[rs] = GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val == rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNEC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BNEC rs, rt, offset - * condition <- (GPR[rs] != GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val != rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - int32_t rs_val, rt_val; - - /* - * BLTC rs, rt, offset - * condition <- (GPR[rs] < GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val < rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - int32_t rs_val, rt_val; - - /* - * BGEC rs, rt, offset - * condition <- (GPR[rs] > GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val > rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTUC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - uint32_t rs_val, rt_val; - - /* - * BLTUC rs, rt, offset - * condition <- (GPR[rs] < GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val < rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEUC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - uint32_t rs_val, rt_val; - - /* - * BGEUC rs, rt, offset - * condition <- (GPR[rs] > GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val > rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZC rs, offset - * condition <- (GPR[rs] < 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val < 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLEZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLEZC rs, offset - * condition <- (GPR[rs] <= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val <= 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZC rs, offset - * condition <- (GPR[rs] >= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val >= 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGTZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGTZC rs, offset - * condition <- (GPR[rs] > 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val > 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BEQZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - uint32_t rs_val; - - /* - * BEQZC rs, offset - * condition <- (GPR[rs] = 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val == 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNEZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - uint32_t rs_val; - - /* - * BNEZC rs, offset - * condition <- (GPR[rs] != 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val != 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -static int -IsAdd64bitOverflow (int32_t a, int32_t b) -{ - int32_t r = (uint32_t) a + (uint32_t) b; - return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0); -} - -bool -EmulateInstructionMIPS::Emulate_BOVC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - int32_t rs_val, rt_val; - - /* - * BOVC rs, rt, offset - * condition <- overflow(GPR[rs] + GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (IsAdd64bitOverflow (rs_val, rt_val)) - target = pc + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNVC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - int32_t rs_val, rt_val; - - /* - * BNVC rs, rt, offset - * condition <- overflow(GPR[rs] + GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (! IsAdd64bitOverflow (rs_val, rt_val)) - target = pc + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool EmulateInstructionMIPS::Emulate_J (llvm::MCInst& insn) { bool success = false; uint32_t offset, pc; /* * J offset * offset = sign_ext (offset << 2) * PC = PC[63-28] | offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; /* This is a PC-region branch and not PC-relative */ pc = (pc & 0xF0000000UL) | offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, pc)) return false; return true; } bool EmulateInstructionMIPS::Emulate_JAL (llvm::MCInst& insn) { bool success = false; uint32_t offset, target, pc; /* * JAL offset * offset = sign_ext (offset << 2) * PC = PC[63-28] | offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; /* This is a PC-region branch and not PC-relative */ target = (pc & 0xF0000000UL) | offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) return false; return true; } bool EmulateInstructionMIPS::Emulate_JALR (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; uint32_t pc, rs_val; /* * JALR rt, rs * GPR[rt] = PC + 8 * PC = GPR[rs] */ rt = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rs = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, rs_val)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_zero_mips + rt, pc + 8)) return false; return true; } bool EmulateInstructionMIPS::Emulate_JIALC (llvm::MCInst& insn) { bool success = false; uint32_t rt; int32_t target, offset, pc, rt_val; /* * JIALC rt, offset * offset = sign_ext (offset) * PC = GPR[rt] + offset * RA = PC + 4 */ rt = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); if (!success) return false; target = rt_val + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) return false; return true; } bool EmulateInstructionMIPS::Emulate_JIC (llvm::MCInst& insn) { bool success = false; uint32_t rt; int32_t target, offset, rt_val; /* * JIC rt, offset * offset = sign_ext (offset) * PC = GPR[rt] + offset */ rt = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); if (!success) return false; target = rt_val + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_JR (llvm::MCInst& insn) { bool success = false; uint32_t rs; uint32_t rs_val; /* * JR rs * PC = GPR[rs] */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, rs_val)) return false; return true; } +/* + Emulate Branch on FP True/False + BC1F, BC1FL : Branch on FP False (L stands for branch likely) + BC1T, BC1TL : Branch on FP True (L stands for branch likely) +*/ bool -EmulateInstructionMIPS::Emulate_BC1F (llvm::MCInst& insn) +EmulateInstructionMIPS::Emulate_FP_branch (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1F cc, offset - * condition <- (FPConditionCode(cc) == 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; + int32_t pc, offset, target = 0; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); - fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - if ((fcsr & (1 << cc)) == 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1T (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1T cc, offset - * condition <- (FPConditionCode(cc) != 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - if ((fcsr & (1 << cc)) != 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1FL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1F cc, offset - * condition <- (FPConditionCode(cc) == 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - if ((fcsr & (1 << cc)) == 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - + if (!strcasecmp (op_name, "BC1F") || + !strcasecmp (op_name, "BC1FL")) + { + if ((fcsr & (1 << cc)) == 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BC1T") || + !strcasecmp (op_name, "BC1TL")) + { + if ((fcsr & (1 << cc)) != 0) + target = pc + offset; + else + target = pc + 8; + } Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; return true; } bool -EmulateInstructionMIPS::Emulate_BC1TL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1T cc, offset - * condition <- (FPConditionCode(cc) != 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - if ((fcsr & (1 << cc)) != 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool EmulateInstructionMIPS::Emulate_BC1EQZ (llvm::MCInst& insn) { bool success = false; uint32_t ft; uint32_t ft_val; int32_t target, pc, offset; /* * BC1EQZ ft, offset * condition <- (FPR[ft].bit0 == 0) * if condition then * offset = sign_ext (offset) * PC = PC + 4 + offset */ ft = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; ft_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + ft, 0, &success); if (!success) return false; if ((ft_val & 1) == 0) target = pc + 4 + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BC1NEZ (llvm::MCInst& insn) { bool success = false; uint32_t ft; uint32_t ft_val; int32_t target, pc, offset; /* * BC1NEZ ft, offset * condition <- (FPR[ft].bit0 != 0) * if condition then * offset = sign_ext (offset) * PC = PC + 4 + offset */ ft = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; ft_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + ft, 0, &success); if (!success) return false; if ((ft_val & 1) != 0) target = pc + 4 + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; return true; } +/* + Emulate MIPS-3D Branch instructions + BC1ANY2F, BC1ANY2T : Branch on Any of Two Floating Point Condition Codes False/True + BC1ANY4F, BC1ANY4T : Branch on Any of Four Floating Point Condition Codes False/True +*/ bool -EmulateInstructionMIPS::Emulate_BC1ANY2F (llvm::MCInst& insn) +EmulateInstructionMIPS::Emulate_3D_branch (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1ANY2F cc, offset - * condition <- (FPConditionCode(cc) == 0 - * || FPConditionCode(cc+1) == 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; + int32_t pc, offset, target = 0; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); - fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - /* if any one bit is 0 */ - if (((fcsr >> cc) & 3) != 3) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1ANY2T (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1ANY2T cc, offset - * condition <- (FPConditionCode(cc) == 1 - * || FPConditionCode(cc+1) == 1) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - /* if any one bit is 1 */ - if (((fcsr >> cc) & 3) != 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1ANY4F (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1ANY4F cc, offset - * condition <- (FPConditionCode(cc) == 0 - * || FPConditionCode(cc+1) == 0) - * || FPConditionCode(cc+2) == 0) - * || FPConditionCode(cc+3) == 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - /* if any one bit is 0 */ - if (((fcsr >> cc) & 0xf) != 0xf) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1ANY4T (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1ANY4T cc, offset - * condition <- (FPConditionCode(cc) == 1 - * || FPConditionCode(cc+1) == 1) - * || FPConditionCode(cc+2) == 1) - * || FPConditionCode(cc+3) == 1) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - /* if any one bit is 1 */ - if (((fcsr >> cc) & 0xf) != 0) - target = pc + offset; - else - target = pc + 8; - + if (!strcasecmp (op_name, "BC1ANY2F")) + { + /* if any one bit is 0 */ + if (((fcsr >> cc) & 3) != 3) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BC1ANY2T")) + { + /* if any one bit is 1 */ + if (((fcsr >> cc) & 3) != 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BC1ANY4F")) + { + /* if any one bit is 0 */ + if (((fcsr >> cc) & 0xf) != 0xf) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BC1ANY4T")) + { + /* if any one bit is 1 */ + if (((fcsr >> cc) & 0xf) != 0) + target = pc + offset; + else + target = pc + 8; + } Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BNZB (llvm::MCInst& insn) { return Emulate_MSA_Branch_DF(insn, 1, true); } bool EmulateInstructionMIPS::Emulate_BNZH (llvm::MCInst& insn) { return Emulate_MSA_Branch_DF(insn, 2, true); } bool EmulateInstructionMIPS::Emulate_BNZW (llvm::MCInst& insn) { return Emulate_MSA_Branch_DF(insn, 4, true); } bool EmulateInstructionMIPS::Emulate_BNZD (llvm::MCInst& insn) { return Emulate_MSA_Branch_DF(insn, 8, true); } bool EmulateInstructionMIPS::Emulate_BZB (llvm::MCInst& insn) { return Emulate_MSA_Branch_DF(insn, 1, false); } bool EmulateInstructionMIPS::Emulate_BZH (llvm::MCInst& insn) { return Emulate_MSA_Branch_DF(insn, 2, false); } bool EmulateInstructionMIPS::Emulate_BZW (llvm::MCInst& insn) { return Emulate_MSA_Branch_DF(insn, 4, false); } bool EmulateInstructionMIPS::Emulate_BZD (llvm::MCInst& insn) { return Emulate_MSA_Branch_DF(insn, 8, false); } bool EmulateInstructionMIPS::Emulate_MSA_Branch_DF (llvm::MCInst& insn, int element_byte_size, bool bnz) { bool success = false, branch_hit = true; int32_t target = 0; RegisterValue reg_value; uint8_t * ptr = NULL; uint32_t wt = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); int32_t offset = insn.getOperand(1).getImm(); int32_t pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; if (ReadRegister (eRegisterKindDWARF, dwarf_w0_mips + wt, reg_value)) ptr = (uint8_t *)reg_value.GetBytes(); else return false; for(int i = 0; i < 16 / element_byte_size; i++) { switch(element_byte_size) { case 1: if((*ptr == 0 && bnz) || (*ptr != 0 && !bnz) ) branch_hit = false; break; case 2: if((*(uint16_t *)ptr == 0 && bnz) || (*(uint16_t *)ptr != 0 && !bnz)) branch_hit = false; break; case 4: if((*(uint32_t *)ptr == 0 && bnz) || (*(uint32_t *)ptr != 0 && !bnz)) branch_hit = false; break; case 8: if((*(uint64_t *)ptr == 0 && bnz) || (*(uint64_t *)ptr != 0 && !bnz)) branch_hit = false; break; } if(!branch_hit) break; ptr = ptr + element_byte_size; } if(branch_hit) target = pc + offset; else target = pc + 8; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BNZV (llvm::MCInst& insn) { return Emulate_MSA_Branch_V (insn, true); } bool EmulateInstructionMIPS::Emulate_BZV (llvm::MCInst& insn) { return Emulate_MSA_Branch_V (insn, false); } bool EmulateInstructionMIPS::Emulate_MSA_Branch_V (llvm::MCInst& insn, bool bnz) { bool success = false; int32_t target = 0; llvm::APInt wr_val = llvm::APInt::getNullValue(128); llvm::APInt fail_value = llvm::APInt::getMaxValue(128); llvm::APInt zero_value = llvm::APInt::getNullValue(128); RegisterValue reg_value; uint32_t wt = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); int32_t offset = insn.getOperand(1).getImm(); int32_t pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); if (!success) return false; if (ReadRegister (eRegisterKindDWARF, dwarf_w0_mips + wt, reg_value)) wr_val = reg_value.GetAsUInt128(fail_value); else return false; if((llvm::APInt::isSameValue(zero_value, wr_val) && !bnz) || (!llvm::APInt::isSameValue(zero_value, wr_val) && bnz)) target = pc + offset; else target = pc + 8; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_LDST_Imm (llvm::MCInst& insn) { bool success = false; uint32_t base; int32_t imm, address; Context bad_vaddr_context; uint32_t num_operands = insn.getNumOperands(); base = m_reg_info->getEncodingValue (insn.getOperand(num_operands-2).getReg()); imm = insn.getOperand(num_operands-1).getImm(); RegisterInfo reg_info_base; if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + base, reg_info_base)) return false; /* read base register */ address =(int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); if (!success) return false; /* destination address */ address = address + imm; /* Set the bad_vaddr register with base address used in the instruction */ bad_vaddr_context.type = eContextInvalid; WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips, address); return true; } bool EmulateInstructionMIPS::Emulate_LDST_Reg (llvm::MCInst& insn) { bool success = false; uint32_t base, index; int32_t address, index_address; Context bad_vaddr_context; uint32_t num_operands = insn.getNumOperands(); base = m_reg_info->getEncodingValue (insn.getOperand(num_operands-2).getReg()); index = m_reg_info->getEncodingValue (insn.getOperand(num_operands-1).getReg()); RegisterInfo reg_info_base, reg_info_index; if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + base, reg_info_base)) return false; if (!GetRegisterInfo (eRegisterKindDWARF, dwarf_zero_mips + index, reg_info_index)) return false; /* read base register */ address =(int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success); if (!success) return false; /* read index register */ index_address =(int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + index, 0, &success); if (!success) return false; /* destination address */ address = address + index_address; /* Set the bad_vaddr register with base address used in the instruction */ bad_vaddr_context.type = eContextInvalid; WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips, address); return true; } Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h (revision 293283) @@ -1,397 +1,289 @@ //===-- EmulateInstructionMIPS.h ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef EmulateInstructionMIPS_h_ #define EmulateInstructionMIPS_h_ namespace llvm { class MCDisassembler; class MCSubtargetInfo; class MCRegisterInfo; class MCAsmInfo; class MCContext; class MCInstrInfo; class MCInst; } #include "lldb/Core/EmulateInstruction.h" #include "lldb/Core/Error.h" #include "lldb/Interpreter/OptionValue.h" class EmulateInstructionMIPS : public lldb_private::EmulateInstruction { public: static void Initialize (); static void Terminate (); static lldb_private::ConstString GetPluginNameStatic (); static const char * GetPluginDescriptionStatic (); static lldb_private::EmulateInstruction * CreateInstance (const lldb_private::ArchSpec &arch, lldb_private::InstructionType inst_type); static bool SupportsEmulatingInstructionsOfTypeStatic (lldb_private::InstructionType inst_type) { switch (inst_type) { case lldb_private::eInstructionTypeAny: case lldb_private::eInstructionTypePrologueEpilogue: case lldb_private::eInstructionTypePCModifying: return true; case lldb_private::eInstructionTypeAll: return false; } return false; } lldb_private::ConstString GetPluginName() override; uint32_t GetPluginVersion() override { return 1; } bool SetTargetTriple (const lldb_private::ArchSpec &arch) override; EmulateInstructionMIPS (const lldb_private::ArchSpec &arch); bool SupportsEmulatingInstructionsOfType (lldb_private::InstructionType inst_type) override { return SupportsEmulatingInstructionsOfTypeStatic (inst_type); } bool ReadInstruction () override; bool EvaluateInstruction (uint32_t evaluate_options) override; bool SetInstruction (const lldb_private::Opcode &insn_opcode, const lldb_private::Address &inst_addr, lldb_private::Target *target) override; bool TestEmulation (lldb_private::Stream *out_stream, lldb_private::ArchSpec &arch, lldb_private::OptionValueDictionary *test_data) override { return false; } bool GetRegisterInfo (lldb::RegisterKind reg_kind, uint32_t reg_num, lldb_private::RegisterInfo ®_info) override; bool CreateFunctionEntryUnwind (lldb_private::UnwindPlan &unwind_plan) override; protected: typedef struct { const char *op_name; bool (EmulateInstructionMIPS::*callback) (llvm::MCInst& insn); const char *insn_name; } MipsOpcode; static MipsOpcode* GetOpcodeForInstruction (const char *op_name); uint32_t GetSizeOfInstruction (lldb_private::DataExtractor& data, uint64_t inst_addr); bool Emulate_ADDiu (llvm::MCInst& insn); bool Emulate_SW (llvm::MCInst& insn); bool Emulate_LW (llvm::MCInst& insn); bool Emulate_ADDIUSP (llvm::MCInst& insn); bool Emulate_ADDIUS5 (llvm::MCInst& insn); bool Emulate_SWSP (llvm::MCInst& insn); bool Emulate_SWM16_32 (llvm::MCInst& insn); bool Emulate_LWSP (llvm::MCInst& insn); bool Emulate_LWM16_32 (llvm::MCInst& insn); bool Emulate_JRADDIUSP (llvm::MCInst& insn); bool Emulate_LDST_Imm (llvm::MCInst& insn); bool Emulate_LDST_Reg (llvm::MCInst& insn); bool - Emulate_BEQ (llvm::MCInst& insn); + Emulate_BXX_3ops (llvm::MCInst& insn); bool - Emulate_BNE (llvm::MCInst& insn); + Emulate_BXX_3ops_C (llvm::MCInst& insn); bool - Emulate_BEQL (llvm::MCInst& insn); + Emulate_BXX_2ops (llvm::MCInst& insn); bool - Emulate_BNEL (llvm::MCInst& insn); + Emulate_BXX_2ops_C (llvm::MCInst& insn); bool - Emulate_BGEZALL (llvm::MCInst& insn); + Emulate_Bcond_Link_C (llvm::MCInst& insn); bool - Emulate_BAL (llvm::MCInst& insn); + Emulate_Bcond_Link (llvm::MCInst& insn); bool - Emulate_BGEZAL (llvm::MCInst& insn); + Emulate_FP_branch (llvm::MCInst& insn); bool - Emulate_BALC (llvm::MCInst& insn); + Emulate_3D_branch (llvm::MCInst& insn); bool - Emulate_BC (llvm::MCInst& insn); + Emulate_BAL (llvm::MCInst& insn); bool - Emulate_BGEZ (llvm::MCInst& insn); + Emulate_BALC (llvm::MCInst& insn); bool - Emulate_BLEZALC (llvm::MCInst& insn); + Emulate_BC (llvm::MCInst& insn); bool - Emulate_BGEZALC (llvm::MCInst& insn); - - bool - Emulate_BLTZALC (llvm::MCInst& insn); - - bool - Emulate_BGTZALC (llvm::MCInst& insn); - - bool - Emulate_BEQZALC (llvm::MCInst& insn); - - bool - Emulate_BNEZALC (llvm::MCInst& insn); - - bool - Emulate_BEQC (llvm::MCInst& insn); - - bool - Emulate_BNEC (llvm::MCInst& insn); - - bool - Emulate_BLTC (llvm::MCInst& insn); - - bool - Emulate_BGEC (llvm::MCInst& insn); - - bool - Emulate_BLTUC (llvm::MCInst& insn); - - bool - Emulate_BGEUC (llvm::MCInst& insn); - - bool - Emulate_BLTZC (llvm::MCInst& insn); - - bool - Emulate_BLEZC (llvm::MCInst& insn); - - bool - Emulate_BGEZC (llvm::MCInst& insn); - - bool - Emulate_BGTZC (llvm::MCInst& insn); - - bool - Emulate_BEQZC (llvm::MCInst& insn); - - bool - Emulate_BNEZC (llvm::MCInst& insn); - - bool - Emulate_BGEZL (llvm::MCInst& insn); - - bool - Emulate_BGTZ (llvm::MCInst& insn); - - bool - Emulate_BGTZL (llvm::MCInst& insn); - - bool - Emulate_BLEZ (llvm::MCInst& insn); - - bool - Emulate_BLEZL (llvm::MCInst& insn); - - bool - Emulate_BLTZ (llvm::MCInst& insn); - - bool - Emulate_BLTZAL (llvm::MCInst& insn); - - bool - Emulate_BLTZALL (llvm::MCInst& insn); - - bool - Emulate_BLTZL (llvm::MCInst& insn); - - bool - Emulate_BOVC (llvm::MCInst& insn); - - bool - Emulate_BNVC (llvm::MCInst& insn); - - bool Emulate_J (llvm::MCInst& insn); bool Emulate_JAL (llvm::MCInst& insn); bool Emulate_JALR (llvm::MCInst& insn); bool Emulate_JIALC (llvm::MCInst& insn); bool Emulate_JIC (llvm::MCInst& insn); bool Emulate_JR (llvm::MCInst& insn); bool - Emulate_BC1F (llvm::MCInst& insn); - - bool - Emulate_BC1T (llvm::MCInst& insn); - - bool - Emulate_BC1FL (llvm::MCInst& insn); - - bool - Emulate_BC1TL (llvm::MCInst& insn); - - bool Emulate_BC1EQZ (llvm::MCInst& insn); bool Emulate_BC1NEZ (llvm::MCInst& insn); - - bool - Emulate_BC1ANY2F (llvm::MCInst& insn); - - bool - Emulate_BC1ANY2T (llvm::MCInst& insn); - - bool - Emulate_BC1ANY4F (llvm::MCInst& insn); - - bool - Emulate_BC1ANY4T (llvm::MCInst& insn); bool Emulate_BNZB (llvm::MCInst& insn); bool Emulate_BNZH (llvm::MCInst& insn); bool Emulate_BNZW (llvm::MCInst& insn); bool Emulate_BNZD (llvm::MCInst& insn); bool Emulate_BZB (llvm::MCInst& insn); bool Emulate_BZH (llvm::MCInst& insn); bool Emulate_BZW (llvm::MCInst& insn); bool Emulate_BZD (llvm::MCInst& insn); bool Emulate_MSA_Branch_DF (llvm::MCInst& insn, int element_byte_size, bool bnz); bool Emulate_BNZV (llvm::MCInst& insn); bool Emulate_BZV (llvm::MCInst& insn); bool Emulate_MSA_Branch_V (llvm::MCInst& insn, bool bnz); bool Emulate_B16_MM (llvm::MCInst& insn); bool Emulate_Branch_MM (llvm::MCInst& insn); bool Emulate_JALRx16_MM (llvm::MCInst& insn); bool Emulate_JALx (llvm::MCInst& insn); bool Emulate_JALRS (llvm::MCInst& insn); bool nonvolatile_reg_p (uint32_t regnum); const char * GetRegisterName (unsigned reg_num, bool altnernate_name); private: std::unique_ptr m_disasm; std::unique_ptr m_alt_disasm; std::unique_ptr m_subtype_info; std::unique_ptr m_alt_subtype_info; std::unique_ptr m_reg_info; std::unique_ptr m_asm_info; std::unique_ptr m_context; std::unique_ptr m_insn_info; uint32_t m_next_inst_size; bool m_use_alt_disaasm; }; #endif // EmulateInstructionMIPS_h_ Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp (revision 293283) @@ -1,3922 +1,3921 @@ //===-- RenderScriptRuntime.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "RenderScriptRuntime.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/Core/RegularExpression.h" #include "lldb/DataFormatters/DumpValueObjectOptions.h" #include "lldb/Host/StringConvert.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/Type.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Expression/UserExpression.h" #include "lldb/Symbol/VariableList.h" using namespace lldb; using namespace lldb_private; using namespace lldb_renderscript; namespace { // The empirical_type adds a basic level of validation to arbitrary data // allowing us to track if data has been discovered and stored or not. // An empirical_type will be marked as valid only if it has been explicitly assigned to. template class empirical_type { public: // Ctor. Contents is invalid when constructed. empirical_type() : valid(false) {} // Return true and copy contents to out if valid, else return false. bool get(type_t& out) const { if (valid) out = data; return valid; } // Return a pointer to the contents or nullptr if it was not valid. const type_t* get() const { return valid ? &data : nullptr; } // Assign data explicitly. void set(const type_t in) { data = in; valid = true; } // Mark contents as invalid. void invalidate() { valid = false; } // Returns true if this type contains valid data. bool isValid() const { return valid; } // Assignment operator. empirical_type& operator = (const type_t in) { set(in); return *this; } // Dereference operator returns contents. // Warning: Will assert if not valid so use only when you know data is valid. const type_t& operator * () const { assert(valid); return data; } protected: bool valid; type_t data; }; } // anonymous namespace // The ScriptDetails class collects data associated with a single script instance. struct RenderScriptRuntime::ScriptDetails { ~ScriptDetails() = default; enum ScriptType { eScript, eScriptC }; // The derived type of the script. empirical_type type; // The name of the original source file. empirical_type resName; // Path to script .so file on the device. empirical_type scriptDyLib; // Directory where kernel objects are cached on device. empirical_type cacheDir; // Pointer to the context which owns this script. empirical_type context; // Pointer to the script object itself. empirical_type script; }; // This Element class represents the Element object in RS, // defining the type associated with an Allocation. struct RenderScriptRuntime::Element { // Taken from rsDefines.h enum DataKind { RS_KIND_USER, RS_KIND_PIXEL_L = 7, RS_KIND_PIXEL_A, RS_KIND_PIXEL_LA, RS_KIND_PIXEL_RGB, RS_KIND_PIXEL_RGBA, RS_KIND_PIXEL_DEPTH, RS_KIND_PIXEL_YUV, RS_KIND_INVALID = 100 }; // Taken from rsDefines.h enum DataType { RS_TYPE_NONE = 0, RS_TYPE_FLOAT_16, RS_TYPE_FLOAT_32, RS_TYPE_FLOAT_64, RS_TYPE_SIGNED_8, RS_TYPE_SIGNED_16, RS_TYPE_SIGNED_32, RS_TYPE_SIGNED_64, RS_TYPE_UNSIGNED_8, RS_TYPE_UNSIGNED_16, RS_TYPE_UNSIGNED_32, RS_TYPE_UNSIGNED_64, RS_TYPE_BOOLEAN, RS_TYPE_UNSIGNED_5_6_5, RS_TYPE_UNSIGNED_5_5_5_1, RS_TYPE_UNSIGNED_4_4_4_4, RS_TYPE_MATRIX_4X4, RS_TYPE_MATRIX_3X3, RS_TYPE_MATRIX_2X2, RS_TYPE_ELEMENT = 1000, RS_TYPE_TYPE, RS_TYPE_ALLOCATION, RS_TYPE_SAMPLER, RS_TYPE_SCRIPT, RS_TYPE_MESH, RS_TYPE_PROGRAM_FRAGMENT, RS_TYPE_PROGRAM_VERTEX, RS_TYPE_PROGRAM_RASTER, RS_TYPE_PROGRAM_STORE, RS_TYPE_FONT, RS_TYPE_INVALID = 10000 }; std::vector children; // Child Element fields for structs empirical_type element_ptr; // Pointer to the RS Element of the Type empirical_type type; // Type of each data pointer stored by the allocation empirical_type type_kind; // Defines pixel type if Allocation is created from an image empirical_type type_vec_size; // Vector size of each data point, e.g '4' for uchar4 empirical_type field_count; // Number of Subelements empirical_type datum_size; // Size of a single Element with padding empirical_type padding; // Number of padding bytes empirical_type array_size; // Number of items in array, only needed for strucrs ConstString type_name; // Name of type, only needed for structs static const ConstString &GetFallbackStructName(); // Print this as the type name of a struct Element // If we can't resolve the actual struct name bool shouldRefresh() const { const bool valid_ptr = element_ptr.isValid() && *element_ptr.get() != 0x0; const bool valid_type = type.isValid() && type_vec_size.isValid() && type_kind.isValid(); return !valid_ptr || !valid_type || !datum_size.isValid(); } }; // This AllocationDetails class collects data associated with a single // allocation instance. struct RenderScriptRuntime::AllocationDetails { struct Dimension { uint32_t dim_1; uint32_t dim_2; uint32_t dim_3; uint32_t cubeMap; Dimension() { dim_1 = 0; dim_2 = 0; dim_3 = 0; cubeMap = 0; } }; // Header for reading and writing allocation contents // to a binary file. struct FileHeader { uint8_t ident[4]; // ASCII 'RSAD' identifying the file uint16_t hdr_size; // Header size in bytes, for backwards compatability uint16_t type; // DataType enum uint32_t kind; // DataKind enum uint32_t dims[3]; // Dimensions uint32_t element_size; // Size of a single element, including padding }; // Monotonically increasing from 1 static unsigned int ID; // Maps Allocation DataType enum and vector size to printable strings // using mapping from RenderScript numerical types summary documentation static const char* RsDataTypeToString[][4]; // Maps Allocation DataKind enum to printable strings static const char* RsDataKindToString[]; // Maps allocation types to format sizes for printing. static const unsigned int RSTypeToFormat[][3]; // Give each allocation an ID as a way // for commands to reference it. const unsigned int id; RenderScriptRuntime::Element element; // Allocation Element type empirical_type dimension; // Dimensions of the Allocation empirical_type address; // Pointer to address of the RS Allocation empirical_type data_ptr; // Pointer to the data held by the Allocation empirical_type type_ptr; // Pointer to the RS Type of the Allocation empirical_type context; // Pointer to the RS Context of the Allocation empirical_type size; // Size of the allocation empirical_type stride; // Stride between rows of the allocation // Give each allocation an id, so we can reference it in user commands. AllocationDetails(): id(ID++) { } bool shouldRefresh() const { bool valid_ptrs = data_ptr.isValid() && *data_ptr.get() != 0x0; valid_ptrs = valid_ptrs && type_ptr.isValid() && *type_ptr.get() != 0x0; return !valid_ptrs || !dimension.isValid() || !size.isValid() || element.shouldRefresh(); } }; const ConstString & RenderScriptRuntime::Element::GetFallbackStructName() { static const ConstString FallbackStructName("struct"); return FallbackStructName; } unsigned int RenderScriptRuntime::AllocationDetails::ID = 1; const char* RenderScriptRuntime::AllocationDetails::RsDataKindToString[] = { "User", "Undefined", "Undefined", "Undefined", // Enum jumps from 0 to 7 "Undefined", "Undefined", "Undefined", "L Pixel", "A Pixel", "LA Pixel", "RGB Pixel", "RGBA Pixel", "Pixel Depth", "YUV Pixel" }; const char* RenderScriptRuntime::AllocationDetails::RsDataTypeToString[][4] = { {"None", "None", "None", "None"}, {"half", "half2", "half3", "half4"}, {"float", "float2", "float3", "float4"}, {"double", "double2", "double3", "double4"}, {"char", "char2", "char3", "char4"}, {"short", "short2", "short3", "short4"}, {"int", "int2", "int3", "int4"}, {"long", "long2", "long3", "long4"}, {"uchar", "uchar2", "uchar3", "uchar4"}, {"ushort", "ushort2", "ushort3", "ushort4"}, {"uint", "uint2", "uint3", "uint4"}, {"ulong", "ulong2", "ulong3", "ulong4"}, {"bool", "bool2", "bool3", "bool4"}, {"packed_565", "packed_565", "packed_565", "packed_565"}, {"packed_5551", "packed_5551", "packed_5551", "packed_5551"}, {"packed_4444", "packed_4444", "packed_4444", "packed_4444"}, {"rs_matrix4x4", "rs_matrix4x4", "rs_matrix4x4", "rs_matrix4x4"}, {"rs_matrix3x3", "rs_matrix3x3", "rs_matrix3x3", "rs_matrix3x3"}, {"rs_matrix2x2", "rs_matrix2x2", "rs_matrix2x2", "rs_matrix2x2"}, // Handlers {"RS Element", "RS Element", "RS Element", "RS Element"}, {"RS Type", "RS Type", "RS Type", "RS Type"}, {"RS Allocation", "RS Allocation", "RS Allocation", "RS Allocation"}, {"RS Sampler", "RS Sampler", "RS Sampler", "RS Sampler"}, {"RS Script", "RS Script", "RS Script", "RS Script"}, // Deprecated {"RS Mesh", "RS Mesh", "RS Mesh", "RS Mesh"}, {"RS Program Fragment", "RS Program Fragment", "RS Program Fragment", "RS Program Fragment"}, {"RS Program Vertex", "RS Program Vertex", "RS Program Vertex", "RS Program Vertex"}, {"RS Program Raster", "RS Program Raster", "RS Program Raster", "RS Program Raster"}, {"RS Program Store", "RS Program Store", "RS Program Store", "RS Program Store"}, {"RS Font", "RS Font", "RS Font", "RS Font"} }; // Used as an index into the RSTypeToFormat array elements enum TypeToFormatIndex { eFormatSingle = 0, eFormatVector, eElementSize }; // { format enum of single element, format enum of element vector, size of element} const unsigned int RenderScriptRuntime::AllocationDetails::RSTypeToFormat[][3] = { {eFormatHex, eFormatHex, 1}, // RS_TYPE_NONE {eFormatFloat, eFormatVectorOfFloat16, 2}, // RS_TYPE_FLOAT_16 {eFormatFloat, eFormatVectorOfFloat32, sizeof(float)}, // RS_TYPE_FLOAT_32 {eFormatFloat, eFormatVectorOfFloat64, sizeof(double)}, // RS_TYPE_FLOAT_64 {eFormatDecimal, eFormatVectorOfSInt8, sizeof(int8_t)}, // RS_TYPE_SIGNED_8 {eFormatDecimal, eFormatVectorOfSInt16, sizeof(int16_t)}, // RS_TYPE_SIGNED_16 {eFormatDecimal, eFormatVectorOfSInt32, sizeof(int32_t)}, // RS_TYPE_SIGNED_32 {eFormatDecimal, eFormatVectorOfSInt64, sizeof(int64_t)}, // RS_TYPE_SIGNED_64 {eFormatDecimal, eFormatVectorOfUInt8, sizeof(uint8_t)}, // RS_TYPE_UNSIGNED_8 {eFormatDecimal, eFormatVectorOfUInt16, sizeof(uint16_t)}, // RS_TYPE_UNSIGNED_16 {eFormatDecimal, eFormatVectorOfUInt32, sizeof(uint32_t)}, // RS_TYPE_UNSIGNED_32 {eFormatDecimal, eFormatVectorOfUInt64, sizeof(uint64_t)}, // RS_TYPE_UNSIGNED_64 {eFormatBoolean, eFormatBoolean, 1}, // RS_TYPE_BOOL {eFormatHex, eFormatHex, sizeof(uint16_t)}, // RS_TYPE_UNSIGNED_5_6_5 {eFormatHex, eFormatHex, sizeof(uint16_t)}, // RS_TYPE_UNSIGNED_5_5_5_1 {eFormatHex, eFormatHex, sizeof(uint16_t)}, // RS_TYPE_UNSIGNED_4_4_4_4 {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 16}, // RS_TYPE_MATRIX_4X4 {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 9}, // RS_TYPE_MATRIX_3X3 {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 4} // RS_TYPE_MATRIX_2X2 }; //------------------------------------------------------------------ // Static Functions //------------------------------------------------------------------ LanguageRuntime * RenderScriptRuntime::CreateInstance(Process *process, lldb::LanguageType language) { if (language == eLanguageTypeExtRenderScript) return new RenderScriptRuntime(process); else return NULL; } // Callback with a module to search for matching symbols. // We first check that the module contains RS kernels. // Then look for a symbol which matches our kernel name. // The breakpoint address is finally set using the address of this symbol. Searcher::CallbackReturn RSBreakpointResolver::SearchCallback(SearchFilter &filter, SymbolContext &context, Address*, bool) { ModuleSP module = context.module_sp; if (!module) return Searcher::eCallbackReturnContinue; // Is this a module containing renderscript kernels? if (nullptr == module->FindFirstSymbolWithNameAndType(ConstString(".rs.info"), eSymbolTypeData)) return Searcher::eCallbackReturnContinue; // Attempt to set a breakpoint on the kernel name symbol within the module library. // If it's not found, it's likely debug info is unavailable - try to set a // breakpoint on .expand. const Symbol* kernel_sym = module->FindFirstSymbolWithNameAndType(m_kernel_name, eSymbolTypeCode); if (!kernel_sym) { std::string kernel_name_expanded(m_kernel_name.AsCString()); kernel_name_expanded.append(".expand"); kernel_sym = module->FindFirstSymbolWithNameAndType(ConstString(kernel_name_expanded.c_str()), eSymbolTypeCode); } if (kernel_sym) { Address bp_addr = kernel_sym->GetAddress(); if (filter.AddressPasses(bp_addr)) m_breakpoint->AddLocation(bp_addr); } return Searcher::eCallbackReturnContinue; } void RenderScriptRuntime::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), "RenderScript language support", CreateInstance, GetCommandObject); } void RenderScriptRuntime::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } lldb_private::ConstString RenderScriptRuntime::GetPluginNameStatic() { static ConstString g_name("renderscript"); return g_name; } -RenderScriptRuntime::ModuleKind +RenderScriptRuntime::ModuleKind RenderScriptRuntime::GetModuleKind(const lldb::ModuleSP &module_sp) { if (module_sp) { // Is this a module containing renderscript kernels? const Symbol *info_sym = module_sp->FindFirstSymbolWithNameAndType(ConstString(".rs.info"), eSymbolTypeData); if (info_sym) { return eModuleKindKernelObj; } // Is this the main RS runtime library const ConstString rs_lib("libRS.so"); if (module_sp->GetFileSpec().GetFilename() == rs_lib) { return eModuleKindLibRS; } const ConstString rs_driverlib("libRSDriver.so"); if (module_sp->GetFileSpec().GetFilename() == rs_driverlib) { return eModuleKindDriver; } const ConstString rs_cpureflib("libRSCpuRef.so"); if (module_sp->GetFileSpec().GetFilename() == rs_cpureflib) { return eModuleKindImpl; } } return eModuleKindIgnored; } bool RenderScriptRuntime::IsRenderScriptModule(const lldb::ModuleSP &module_sp) { return GetModuleKind(module_sp) != eModuleKindIgnored; } -void +void RenderScriptRuntime::ModulesDidLoad(const ModuleList &module_list ) { Mutex::Locker locker (module_list.GetMutex ()); size_t num_modules = module_list.GetSize(); for (size_t i = 0; i < num_modules; i++) { auto mod = module_list.GetModuleAtIndex (i); if (IsRenderScriptModule (mod)) { LoadModule(mod); } } } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ lldb_private::ConstString RenderScriptRuntime::GetPluginName() { return GetPluginNameStatic(); } uint32_t RenderScriptRuntime::GetPluginVersion() { return 1; } bool RenderScriptRuntime::IsVTableName(const char *name) { return false; } bool RenderScriptRuntime::GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, Address &address, Value::ValueType &value_type) { return false; } TypeAndOrName RenderScriptRuntime::FixUpDynamicType (const TypeAndOrName& type_and_or_name, ValueObject& static_value) { return type_and_or_name; } bool RenderScriptRuntime::CouldHaveDynamicValue(ValueObject &in_value) { return false; } lldb::BreakpointResolverSP RenderScriptRuntime::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp, bool throw_bp) { BreakpointResolverSP resolver_sp; return resolver_sp; } const RenderScriptRuntime::HookDefn RenderScriptRuntime::s_runtimeHookDefns[] = { //rsdScript { "rsdScriptInit", //name "_Z13rsdScriptInitPKN7android12renderscript7ContextEPNS0_7ScriptCEPKcS7_PKhjj", // symbol name 32 bit "_Z13rsdScriptInitPKN7android12renderscript7ContextEPNS0_7ScriptCEPKcS7_PKhmj", // symbol name 64 bit 0, // version RenderScriptRuntime::eModuleKindDriver, // type &lldb_private::RenderScriptRuntime::CaptureScriptInit1 // handler }, { "rsdScriptInvokeForEach", // name "_Z22rsdScriptInvokeForEachPKN7android12renderscript7ContextEPNS0_6ScriptEjPKNS0_10AllocationEPS6_PKvjPK12RsScriptCall", // symbol name 32bit "_Z22rsdScriptInvokeForEachPKN7android12renderscript7ContextEPNS0_6ScriptEjPKNS0_10AllocationEPS6_PKvmPK12RsScriptCall", // symbol name 64bit 0, // version RenderScriptRuntime::eModuleKindDriver, // type nullptr // handler }, { "rsdScriptInvokeForEachMulti", // name "_Z27rsdScriptInvokeForEachMultiPKN7android12renderscript7ContextEPNS0_6ScriptEjPPKNS0_10AllocationEjPS6_PKvjPK12RsScriptCall", // symbol name 32bit "_Z27rsdScriptInvokeForEachMultiPKN7android12renderscript7ContextEPNS0_6ScriptEjPPKNS0_10AllocationEmPS6_PKvmPK12RsScriptCall", // symbol name 64bit 0, // version RenderScriptRuntime::eModuleKindDriver, // type nullptr // handler }, { "rsdScriptInvokeFunction", // name "_Z23rsdScriptInvokeFunctionPKN7android12renderscript7ContextEPNS0_6ScriptEjPKvj", // symbol name 32bit "_Z23rsdScriptInvokeFunctionPKN7android12renderscript7ContextEPNS0_6ScriptEjPKvm", // symbol name 64bit 0, // version RenderScriptRuntime::eModuleKindDriver, // type nullptr // handler }, { "rsdScriptSetGlobalVar", // name "_Z21rsdScriptSetGlobalVarPKN7android12renderscript7ContextEPKNS0_6ScriptEjPvj", // symbol name 32bit "_Z21rsdScriptSetGlobalVarPKN7android12renderscript7ContextEPKNS0_6ScriptEjPvm", // symbol name 64bit 0, // version RenderScriptRuntime::eModuleKindDriver, // type &lldb_private::RenderScriptRuntime::CaptureSetGlobalVar1 // handler }, //rsdAllocation { "rsdAllocationInit", // name "_Z17rsdAllocationInitPKN7android12renderscript7ContextEPNS0_10AllocationEb", // symbol name 32bit "_Z17rsdAllocationInitPKN7android12renderscript7ContextEPNS0_10AllocationEb", // symbol name 64bit 0, // version RenderScriptRuntime::eModuleKindDriver, // type &lldb_private::RenderScriptRuntime::CaptureAllocationInit1 // handler }, { "rsdAllocationRead2D", //name "_Z19rsdAllocationRead2DPKN7android12renderscript7ContextEPKNS0_10AllocationEjjj23RsAllocationCubemapFacejjPvjj", // symbol name 32bit "_Z19rsdAllocationRead2DPKN7android12renderscript7ContextEPKNS0_10AllocationEjjj23RsAllocationCubemapFacejjPvmm", // symbol name 64bit 0, // version RenderScriptRuntime::eModuleKindDriver, // type nullptr // handler }, { "rsdAllocationDestroy", // name "_Z20rsdAllocationDestroyPKN7android12renderscript7ContextEPNS0_10AllocationE", // symbol name 32bit "_Z20rsdAllocationDestroyPKN7android12renderscript7ContextEPNS0_10AllocationE", // symbol name 64bit 0, // version RenderScriptRuntime::eModuleKindDriver, // type &lldb_private::RenderScriptRuntime::CaptureAllocationDestroy // handler }, }; const size_t RenderScriptRuntime::s_runtimeHookCount = sizeof(s_runtimeHookDefns)/sizeof(s_runtimeHookDefns[0]); bool RenderScriptRuntime::HookCallback(void *baton, StoppointCallbackContext *ctx, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { RuntimeHook* hook_info = (RuntimeHook*)baton; ExecutionContext context(ctx->exe_ctx_ref); RenderScriptRuntime *lang_rt = (RenderScriptRuntime *)context.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); lang_rt->HookCallback(hook_info, context); - + return false; } -void +void RenderScriptRuntime::HookCallback(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); if (log) log->Printf ("RenderScriptRuntime::HookCallback - '%s' .", hook_info->defn->name); - if (hook_info->defn->grabber) + if (hook_info->defn->grabber) { (this->*(hook_info->defn->grabber))(hook_info, context); } } bool RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint64_t *data) { // Get a positional integer argument. // Given an ExecutionContext, ``context`` which should be a RenderScript // frame, get the value of the positional argument ``arg`` and save its value // to the address pointed to by ``data``. // returns true on success, false otherwise. // If unsuccessful, the value pointed to by ``data`` is undefined. Otherwise, // ``data`` will be set to the value of the the given ``arg``. // NOTE: only natural width integer arguments for the machine are supported. // Behaviour with non primitive arguments is undefined. if (!data) return false; Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); Error error; RegisterContext* reg_ctx = context.GetRegisterContext(); Process* process = context.GetProcessPtr(); bool success = false; // return value if (!context.GetTargetPtr()) { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - Invalid target"); return false; } switch (context.GetTargetPtr()->GetArchitecture().GetMachine()) { case llvm::Triple::ArchType::x86: { uint64_t sp = reg_ctx->GetSP(); uint32_t offset = (1 + arg) * sizeof(uint32_t); uint32_t result = 0; process->ReadMemory(sp + offset, &result, sizeof(uint32_t), error); if (error.Fail()) { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - error reading X86 stack: %s.", error.AsCString()); } else { *data = result; success = true; } - break; } case llvm::Triple::ArchType::x86_64: { // amd64 has 6 integer registers, and 8 XMM registers for parameter passing. // Surplus args are spilled onto the stack. // rdi, rsi, rdx, rcx, r8, r9, (zmm0 - 7 for vectors) // ref: AMD64 ABI Draft 0.99.6 – October 7, 2013 – 10:35; Figure 3.4. Retrieved from // http://www.x86-64.org/documentation/abi.pdf if (arg > 5) { if (log) log->Warning("X86_64 register spill is not supported."); break; } const char * regnames[] = {"rdi", "rsi", "rdx", "rcx", "r8", "r9"}; assert((sizeof(regnames) / sizeof(const char *)) > arg); const RegisterInfo *rArg = reg_ctx->GetRegisterInfoByName(regnames[arg]); RegisterValue rVal; success = reg_ctx->ReadRegister(rArg, rVal); if (success) { *data = rVal.GetAsUInt64(0u, &success); } else { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - error reading x86_64 register: %d.", arg); } break; } case llvm::Triple::ArchType::arm: { // arm 32 bit + // first 4 arguments are passed via registers if (arg < 4) { const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg); RegisterValue rVal; success = reg_ctx->ReadRegister(rArg, rVal); if (success) { (*data) = rVal.GetAsUInt32(0u, &success); } else { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - error reading ARM register: %d.", arg); } } else { uint64_t sp = reg_ctx->GetSP(); uint32_t offset = (arg-4) * sizeof(uint32_t); - process->ReadMemory(sp + offset, &data, sizeof(uint32_t), error); - if (error.Fail()) + uint32_t value = 0; + size_t bytes_read = process->ReadMemory(sp + offset, &value, sizeof(value), error); + if (error.Fail() || bytes_read != sizeof(value)) { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - error reading ARM stack: %s.", error.AsCString()); } else { + *data = value; success = true; } } - break; } case llvm::Triple::ArchType::aarch64: { // arm 64 bit // first 8 arguments are in the registers if (arg < 8) { const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg); RegisterValue rVal; success = reg_ctx->ReadRegister(rArg, rVal); if (success) { *data = rVal.GetAsUInt64(0u, &success); } else { if (log) log->Printf("RenderScriptRuntime::GetArgSimple() - AARCH64 - Error while reading the argument #%d", arg); } } else { // @TODO: need to find the argument in the stack if (log) log->Printf("RenderScriptRuntime::GetArgSimple - AARCH64 - FOR #ARG >= 8 NOT IMPLEMENTED YET. Argument number: %d", arg); } break; } case llvm::Triple::ArchType::mipsel: { - // read from the registers + // first 4 arguments are passed in registers if (arg < 4){ const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg + 4); RegisterValue rVal; success = reg_ctx->ReadRegister(rArg, rVal); if (success) { *data = rVal.GetAsUInt64(0u, &success); } else { if (log) log->Printf("RenderScriptRuntime::GetArgSimple() - Mips - Error while reading the argument #%d", arg); } - } - - // read from the stack + // arguments > 4 are read from the stack else { uint64_t sp = reg_ctx->GetSP(); uint32_t offset = arg * sizeof(uint32_t); - process->ReadMemory(sp + offset, &data, sizeof(uint32_t), error); - if (error.Fail()) + uint32_t value = 0; + size_t bytes_read = process->ReadMemory(sp + offset, &value, sizeof(value), error); + if (error.Fail() || bytes_read != sizeof(value)) { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - error reading Mips stack: %s.", error.AsCString()); } else { + *data = value; success = true; } } - break; } case llvm::Triple::ArchType::mips64el: { // read from the registers if (arg < 8) { const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg + 4); RegisterValue rVal; success = reg_ctx->ReadRegister(rArg, rVal); if (success) { (*data) = rVal.GetAsUInt64(0u, &success); } else { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - Mips64 - Error reading the argument #%d", arg); } } - - // read from the stack + // arguments > 8 are read from the stack else { uint64_t sp = reg_ctx->GetSP(); uint32_t offset = (arg - 8) * sizeof(uint64_t); - process->ReadMemory(sp + offset, &data, sizeof(uint64_t), error); - if (error.Fail()) + uint64_t value = 0; + size_t bytes_read = process->ReadMemory(sp + offset, &value, sizeof(value), error); + if (error.Fail() || bytes_read != sizeof(value)) { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - Mips64 - Error reading Mips64 stack: %s.", error.AsCString()); } else { + *data = value; success = true; } } - break; } default: { // invalid architecture if (log) log->Printf("RenderScriptRuntime::GetArgSimple - Architecture not supported"); - } } if (!success) { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - failed to get argument at index %" PRIu32, arg); } return success; } -void +void RenderScriptRuntime::CaptureSetGlobalVar1(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); - + //Context, Script, int, data, length uint64_t rs_context_u64 = 0U; uint64_t rs_script_u64 = 0U; uint64_t rs_id_u64 = 0U; uint64_t rs_data_u64 = 0U; uint64_t rs_length_u64 = 0U; bool success = GetArgSimple(context, 0, &rs_context_u64) && GetArgSimple(context, 1, &rs_script_u64) && GetArgSimple(context, 2, &rs_id_u64) && GetArgSimple(context, 3, &rs_data_u64) && GetArgSimple(context, 4, &rs_length_u64); if (!success) { if (log) log->Printf("RenderScriptRuntime::CaptureSetGlobalVar1 - Error while reading the function parameters"); return; } - + if (log) { log->Printf ("RenderScriptRuntime::CaptureSetGlobalVar1 - 0x%" PRIx64 ",0x%" PRIx64 " slot %" PRIu64 " = 0x%" PRIx64 ":%" PRIu64 "bytes.", rs_context_u64, rs_script_u64, rs_id_u64, rs_data_u64, rs_length_u64); addr_t script_addr = (addr_t)rs_script_u64; if (m_scriptMappings.find( script_addr ) != m_scriptMappings.end()) { auto rsm = m_scriptMappings[script_addr]; if (rs_id_u64 < rsm->m_globals.size()) { auto rsg = rsm->m_globals[rs_id_u64]; - log->Printf ("RenderScriptRuntime::CaptureSetGlobalVar1 - Setting of '%s' within '%s' inferred", rsg.m_name.AsCString(), + log->Printf ("RenderScriptRuntime::CaptureSetGlobalVar1 - Setting of '%s' within '%s' inferred", rsg.m_name.AsCString(), rsm->m_module->GetFileSpec().GetFilename().AsCString()); } } } } -void +void RenderScriptRuntime::CaptureAllocationInit1(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); - + //Context, Alloc, bool uint64_t rs_context_u64 = 0U; uint64_t rs_alloc_u64 = 0U; uint64_t rs_forceZero_u64 = 0U; bool success = GetArgSimple(context, 0, &rs_context_u64) && GetArgSimple(context, 1, &rs_alloc_u64) && GetArgSimple(context, 2, &rs_forceZero_u64); if (!success) // error case { if (log) log->Printf("RenderScriptRuntime::CaptureAllocationInit1 - Error while reading the function parameters"); return; // abort } if (log) log->Printf ("RenderScriptRuntime::CaptureAllocationInit1 - 0x%" PRIx64 ",0x%" PRIx64 ",0x%" PRIx64 " .", rs_context_u64, rs_alloc_u64, rs_forceZero_u64); AllocationDetails* alloc = LookUpAllocation(rs_alloc_u64, true); if (alloc) alloc->context = rs_context_u64; } void RenderScriptRuntime::CaptureAllocationDestroy(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); // Context, Alloc uint64_t rs_context_u64 = 0U; uint64_t rs_alloc_u64 = 0U; bool success = GetArgSimple(context, 0, &rs_context_u64) && GetArgSimple(context, 1, &rs_alloc_u64); if (!success) // error case { if (log) log->Printf("RenderScriptRuntime::CaptureAllocationDestroy - Error while reading the function parameters"); return; // abort } if (log) log->Printf("RenderScriptRuntime::CaptureAllocationDestroy - 0x%" PRIx64 ", 0x%" PRIx64 ".", rs_context_u64, rs_alloc_u64); for (auto iter = m_allocations.begin(); iter != m_allocations.end(); ++iter) { auto& allocation_ap = *iter; // get the unique pointer if (allocation_ap->address.isValid() && *allocation_ap->address.get() == rs_alloc_u64) { m_allocations.erase(iter); if (log) log->Printf("RenderScriptRuntime::CaptureAllocationDestroy - Deleted allocation entry"); return; } } if (log) log->Printf("RenderScriptRuntime::CaptureAllocationDestroy - Couldn't find destroyed allocation"); } -void +void RenderScriptRuntime::CaptureScriptInit1(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); //Context, Script, resname Str, cachedir Str Error error; Process* process = context.GetProcessPtr(); uint64_t rs_context_u64 = 0U; uint64_t rs_script_u64 = 0U; uint64_t rs_resnameptr_u64 = 0U; uint64_t rs_cachedirptr_u64 = 0U; std::string resname; std::string cachedir; // read the function parameters bool success = GetArgSimple(context, 0, &rs_context_u64) && GetArgSimple(context, 1, &rs_script_u64) && GetArgSimple(context, 2, &rs_resnameptr_u64) && GetArgSimple(context, 3, &rs_cachedirptr_u64); if (!success) { if (log) log->Printf("RenderScriptRuntime::CaptureScriptInit1 - Error while reading the function parameters"); return; } process->ReadCStringFromMemory((lldb::addr_t)rs_resnameptr_u64, resname, error); if (error.Fail()) { if (log) log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - error reading resname: %s.", error.AsCString()); - + } process->ReadCStringFromMemory((lldb::addr_t)rs_cachedirptr_u64, cachedir, error); if (error.Fail()) { if (log) - log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - error reading cachedir: %s.", error.AsCString()); + log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - error reading cachedir: %s.", error.AsCString()); } - + if (log) log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - 0x%" PRIx64 ",0x%" PRIx64 " => '%s' at '%s' .", rs_context_u64, rs_script_u64, resname.c_str(), cachedir.c_str()); if (resname.size() > 0) { StreamString strm; strm.Printf("librs.%s.so", resname.c_str()); ScriptDetails* script = LookUpScript(rs_script_u64, true); if (script) { script->type = ScriptDetails::eScriptC; script->cacheDir = cachedir; script->resName = resname; script->scriptDyLib = strm.GetData(); script->context = addr_t(rs_context_u64); } if (log) log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - '%s' tagged with context 0x%" PRIx64 " and script 0x%" PRIx64 ".", strm.GetData(), rs_context_u64, rs_script_u64); - } + } else if (log) { log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - resource name invalid, Script not tagged"); } } void RenderScriptRuntime::LoadRuntimeHooks(lldb::ModuleSP module, ModuleKind kind) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); if (!module) { return; } Target &target = GetProcess()->GetTarget(); llvm::Triple::ArchType targetArchType = target.GetArchitecture().GetMachine(); if (targetArchType != llvm::Triple::ArchType::x86 && targetArchType != llvm::Triple::ArchType::arm && targetArchType != llvm::Triple::ArchType::aarch64 && targetArchType != llvm::Triple::ArchType::mipsel && targetArchType != llvm::Triple::ArchType::mips64el && targetArchType != llvm::Triple::ArchType::x86_64 ) { if (log) log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Unable to hook runtime. Only X86, ARM, Mips supported currently."); return; } uint32_t archByteSize = target.GetArchitecture().GetAddressByteSize(); for (size_t idx = 0; idx < s_runtimeHookCount; idx++) { const HookDefn* hook_defn = &s_runtimeHookDefns[idx]; if (hook_defn->kind != kind) { continue; } const char* symbol_name = (archByteSize == 4) ? hook_defn->symbol_name_m32 : hook_defn->symbol_name_m64; const Symbol *sym = module->FindFirstSymbolWithNameAndType(ConstString(symbol_name), eSymbolTypeCode); if (!sym){ if (log){ log->Printf("RenderScriptRuntime::LoadRuntimeHooks - ERROR: Symbol '%s' related to the function %s not found", symbol_name, hook_defn->name); } continue; } addr_t addr = sym->GetLoadAddress(&target); if (addr == LLDB_INVALID_ADDRESS) { if (log) - log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Unable to resolve the address of hook function '%s' with symbol '%s'.", + log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Unable to resolve the address of hook function '%s' with symbol '%s'.", hook_defn->name, symbol_name); continue; } else { if (log) log->Printf("RenderScriptRuntime::LoadRuntimeHooks - Function %s, address resolved at 0x%" PRIx64, hook_defn->name, addr); } RuntimeHookSP hook(new RuntimeHook()); hook->address = addr; hook->defn = hook_defn; hook->bp_sp = target.CreateBreakpoint(addr, true, false); hook->bp_sp->SetCallback(HookCallback, hook.get(), true); m_runtimeHooks[addr] = hook; if (log) { - log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Successfully hooked '%s' in '%s' version %" PRIu64 " at 0x%" PRIx64 ".", + log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Successfully hooked '%s' in '%s' version %" PRIu64 " at 0x%" PRIx64 ".", hook_defn->name, module->GetFileSpec().GetFilename().AsCString(), (uint64_t)hook_defn->version, (uint64_t)addr); } } } void RenderScriptRuntime::FixupScriptDetails(RSModuleDescriptorSP rsmodule_sp) { if (!rsmodule_sp) return; Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); const ModuleSP module = rsmodule_sp->m_module; const FileSpec& file = module->GetPlatformFileSpec(); // Iterate over all of the scripts that we currently know of. // Note: We cant push or pop to m_scripts here or it may invalidate rs_script. for (const auto & rs_script : m_scripts) { // Extract the expected .so file path for this script. std::string dylib; if (!rs_script->scriptDyLib.get(dylib)) continue; // Only proceed if the module that has loaded corresponds to this script. if (file.GetFilename() != ConstString(dylib.c_str())) continue; // Obtain the script address which we use as a key. lldb::addr_t script; if (!rs_script->script.get(script)) continue; // If we have a script mapping for the current script. if (m_scriptMappings.find(script) != m_scriptMappings.end()) { // if the module we have stored is different to the one we just received. if (m_scriptMappings[script] != rsmodule_sp) { if (log) log->Printf ("RenderScriptRuntime::FixupScriptDetails - Error: script %" PRIx64 " wants reassigned to new rsmodule '%s'.", (uint64_t)script, rsmodule_sp->m_module->GetFileSpec().GetFilename().AsCString()); } } // We don't have a script mapping for the current script. else { // Obtain the script resource name. std::string resName; if (rs_script->resName.get(resName)) // Set the modules resource name. rsmodule_sp->m_resname = resName; // Add Script/Module pair to map. m_scriptMappings[script] = rsmodule_sp; if (log) log->Printf ("RenderScriptRuntime::FixupScriptDetails - script %" PRIx64 " associated with rsmodule '%s'.", (uint64_t)script, rsmodule_sp->m_module->GetFileSpec().GetFilename().AsCString()); } } } // Uses the Target API to evaluate the expression passed as a parameter to the function // The result of that expression is returned an unsigned 64 bit int, via the result* paramter. // Function returns true on success, and false on failure bool RenderScriptRuntime::EvalRSExpression(const char* expression, StackFrame* frame_ptr, uint64_t* result) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); if (log) log->Printf("RenderScriptRuntime::EvalRSExpression(%s)", expression); ValueObjectSP expr_result; // Perform the actual expression evaluation GetProcess()->GetTarget().EvaluateExpression(expression, frame_ptr, expr_result); if (!expr_result) { if (log) log->Printf("RenderScriptRuntime::EvalRSExpression - Error: Couldn't evaluate expression"); return false; } // The result of the expression is invalid if (!expr_result->GetError().Success()) { Error err = expr_result->GetError(); if (err.GetError() == UserExpression::kNoResult) // Expression returned void, so this is actually a success { if (log) log->Printf("RenderScriptRuntime::EvalRSExpression - Expression returned void"); result = nullptr; return true; } if (log) log->Printf("RenderScriptRuntime::EvalRSExpression - Error evaluating expression result: %s", err.AsCString()); return false; } bool success = false; *result = expr_result->GetValueAsUnsigned(0, &success); // We only read the result as an unsigned int. if (!success) { if (log) log->Printf("RenderScriptRuntime::EvalRSExpression - Error: Couldn't convert expression result to unsigned int"); return false; } return true; } namespace // anonymous { // max length of an expanded expression const int jit_max_expr_size = 768; // Format strings containing the expressions we may need to evaluate. const char runtimeExpressions[][256] = { // Mangled GetOffsetPointer(Allocation*, xoff, yoff, zoff, lod, cubemap) "(int*)_Z12GetOffsetPtrPKN7android12renderscript10AllocationEjjjj23RsAllocationCubemapFace(0x%lx, %u, %u, %u, 0, 0)", // Type* rsaAllocationGetType(Context*, Allocation*) "(void*)rsaAllocationGetType(0x%lx, 0x%lx)", // rsaTypeGetNativeData(Context*, Type*, void* typeData, size) // Pack the data in the following way mHal.state.dimX; mHal.state.dimY; mHal.state.dimZ; // mHal.state.lodCount; mHal.state.faces; mElement; into typeData // Need to specify 32 or 64 bit for uint_t since this differs between devices "uint%u_t data[6]; (void*)rsaTypeGetNativeData(0x%lx, 0x%lx, data, 6); data[0]", // X dim "uint%u_t data[6]; (void*)rsaTypeGetNativeData(0x%lx, 0x%lx, data, 6); data[1]", // Y dim "uint%u_t data[6]; (void*)rsaTypeGetNativeData(0x%lx, 0x%lx, data, 6); data[2]", // Z dim "uint%u_t data[6]; (void*)rsaTypeGetNativeData(0x%lx, 0x%lx, data, 6); data[5]", // Element ptr // rsaElementGetNativeData(Context*, Element*, uint32_t* elemData,size) // Pack mType; mKind; mNormalized; mVectorSize; NumSubElements into elemData "uint32_t data[5]; (void*)rsaElementGetNativeData(0x%lx, 0x%lx, data, 5); data[0]", // Type "uint32_t data[5]; (void*)rsaElementGetNativeData(0x%lx, 0x%lx, data, 5); data[1]", // Kind "uint32_t data[5]; (void*)rsaElementGetNativeData(0x%lx, 0x%lx, data, 5); data[3]", // Vector Size "uint32_t data[5]; (void*)rsaElementGetNativeData(0x%lx, 0x%lx, data, 5); data[4]", // Field Count // rsaElementGetSubElements(RsContext con, RsElement elem, uintptr_t *ids, const char **names, // size_t *arraySizes, uint32_t dataSize) // Needed for Allocations of structs to gather details about fields/Subelements "void* ids[%u]; const char* names[%u]; size_t arr_size[%u];" "(void*)rsaElementGetSubElements(0x%lx, 0x%lx, ids, names, arr_size, %u); ids[%u]", // Element* of field "void* ids[%u]; const char* names[%u]; size_t arr_size[%u];" "(void*)rsaElementGetSubElements(0x%lx, 0x%lx, ids, names, arr_size, %u); names[%u]", // Name of field "void* ids[%u]; const char* names[%u]; size_t arr_size[%u];" "(void*)rsaElementGetSubElements(0x%lx, 0x%lx, ids, names, arr_size, %u); arr_size[%u]" // Array size of field }; // Temporary workaround for MIPS, until the compiler emits the JAL instruction when invoking directly the function. // At the moment, when evaluating an expression involving a function call, the LLVM codegen for Mips emits a JAL // instruction, which is able to jump in the range +/- 128MB with respect to the current program counter ($pc). If // the requested function happens to reside outside the above region, the function address will be truncated and the // function invocation will fail. This is a problem in the RS plugin as we rely on the RS API to probe the number and // the nature of allocations. A proper solution in the MIPS compiler is currently being investigated. As temporary // work around for this context, we'll invoke the RS API through function pointers, which cause the compiler to emit a // register based JALR instruction. const char runtimeExpressions_mips[][512] = { // Mangled GetOffsetPointer(Allocation*, xoff, yoff, zoff, lod, cubemap) "int* (*f) (void*, int, int, int, int, int) = (int* (*) (void*, int, int, int, int, int)) " "_Z12GetOffsetPtrPKN7android12renderscript10AllocationEjjjj23RsAllocationCubemapFace; " "(int*) f((void*) 0x%lx, %u, %u, %u, 0, 0)", // Type* rsaAllocationGetType(Context*, Allocation*) "void* (*f) (void*, void*) = (void* (*) (void*, void*)) rsaAllocationGetType; (void*) f((void*) 0x%lx, (void*) 0x%lx)", // rsaTypeGetNativeData(Context*, Type*, void* typeData, size) // Pack the data in the following way mHal.state.dimX; mHal.state.dimY; mHal.state.dimZ; // mHal.state.lodCount; mHal.state.faces; mElement; into typeData // Need to specify 32 or 64 bit for uint_t since this differs between devices "uint%u_t data[6]; void* (*f)(void*, void*, uintptr_t*, uint32_t) = (void* (*)(void*, void*, uintptr_t*, uint32_t)) " "rsaTypeGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 6); data[0]", "uint%u_t data[6]; void* (*f)(void*, void*, uintptr_t*, uint32_t) = (void* (*)(void*, void*, uintptr_t*, uint32_t)) " "rsaTypeGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 6); data[1]", "uint%u_t data[6]; void* (*f)(void*, void*, uintptr_t*, uint32_t) = (void* (*)(void*, void*, uintptr_t*, uint32_t)) " "rsaTypeGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 6); data[2]", "uint%u_t data[6]; void* (*f)(void*, void*, uintptr_t*, uint32_t) = (void* (*)(void*, void*, uintptr_t*, uint32_t)) " "rsaTypeGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 6); data[5]", // rsaElementGetNativeData(Context*, Element*, uint32_t* elemData,size) // Pack mType; mKind; mNormalized; mVectorSize; NumSubElements into elemData "uint32_t data[5]; void* (*f)(void*, void*, uint32_t*, uint32_t) = (void* (*)(void*, void*, uint32_t*, uint32_t)) " "rsaElementGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 5); data[0]", // Type "uint32_t data[5]; void* (*f)(void*, void*, uint32_t*, uint32_t) = (void* (*)(void*, void*, uint32_t*, uint32_t)) " "rsaElementGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 5); data[1]", // Kind "uint32_t data[5]; void* (*f)(void*, void*, uint32_t*, uint32_t) = (void* (*)(void*, void*, uint32_t*, uint32_t)) " "rsaElementGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 5); data[3]", // Vector size "uint32_t data[5]; void* (*f)(void*, void*, uint32_t*, uint32_t) = (void* (*)(void*, void*, uint32_t*, uint32_t)) " "rsaElementGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 5); data[4]", // Field count // rsaElementGetSubElements(RsContext con, RsElement elem, uintptr_t *ids, const char **names, // size_t *arraySizes, uint32_t dataSize) // Needed for Allocations of structs to gather details about fields/Subelements "void* ids[%u]; const char* names[%u]; size_t arr_size[%u];" "void* (*f) (void*, void*, uintptr_t*, const char**, size_t*, uint32_t) = " "(void* (*) (void*, void*, uintptr_t*, const char**, size_t*, uint32_t)) rsaElementGetSubElements;" "(void*) f((void*) 0x%lx, (void*) 0x%lx, (uintptr_t*) ids, names, arr_size, (uint32_t) %u);" "ids[%u]", // Element* of field "void* ids[%u]; const char* names[%u]; size_t arr_size[%u];" "void* (*f) (void*, void*, uintptr_t*, const char**, size_t*, uint32_t) = " "(void* (*) (void*, void*, uintptr_t*, const char**, size_t*, uint32_t)) rsaElementGetSubElements;" "(void*) f((void*) 0x%lx, (void*) 0x%lx, (uintptr_t*) ids, names, arr_size, (uint32_t) %u);" "names[%u]", // Name of field "void* ids[%u]; const char* names[%u]; size_t arr_size[%u];" "void* (*f) (void*, void*, uintptr_t*, const char**, size_t*, uint32_t) = " "(void* (*) (void*, void*, uintptr_t*, const char**, size_t*, uint32_t)) rsaElementGetSubElements;" "(void*) f((void*) 0x%lx, (void*) 0x%lx, (uintptr_t*) ids, names, arr_size, (uint32_t) %u);" "arr_size[%u]" // Array size of field }; } // end of the anonymous namespace // Retrieve the string to JIT for the given expression const char* RenderScriptRuntime::JITTemplate(ExpressionStrings e) { // be nice to your Mips friend when adding new expression strings static_assert(sizeof(runtimeExpressions)/sizeof(runtimeExpressions[0]) == sizeof(runtimeExpressions_mips)/sizeof(runtimeExpressions_mips[0]), "#runtimeExpressions != #runtimeExpressions_mips"); assert((e >= eExprGetOffsetPtr && e <= eExprSubelementsArrSize) && "Expression string out of bounds"); llvm::Triple::ArchType arch = GetTargetRef().GetArchitecture().GetMachine(); // mips JAL workaround if(arch == llvm::Triple::ArchType::mips64el || arch == llvm::Triple::ArchType::mipsel) return runtimeExpressions_mips[e]; else return runtimeExpressions[e]; } // JITs the RS runtime for the internal data pointer of an allocation. // Is passed x,y,z coordinates for the pointer to a specific element. // Then sets the data_ptr member in Allocation with the result. // Returns true on success, false otherwise bool RenderScriptRuntime::JITDataPointer(AllocationDetails* allocation, StackFrame* frame_ptr, unsigned int x, unsigned int y, unsigned int z) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); if (!allocation->address.isValid()) { if (log) log->Printf("RenderScriptRuntime::JITDataPointer - Failed to find allocation details"); return false; } const char* expr_cstr = JITTemplate(eExprGetOffsetPtr); char buffer[jit_max_expr_size]; int chars_written = snprintf(buffer, jit_max_expr_size, expr_cstr, *allocation->address.get(), x, y, z); if (chars_written < 0) { if (log) log->Printf("RenderScriptRuntime::JITDataPointer - Encoding error in snprintf()"); return false; } else if (chars_written >= jit_max_expr_size) { if (log) log->Printf("RenderScriptRuntime::JITDataPointer - Expression too long"); return false; } uint64_t result = 0; if (!EvalRSExpression(buffer, frame_ptr, &result)) return false; addr_t mem_ptr = static_cast(result); allocation->data_ptr = mem_ptr; return true; } // JITs the RS runtime for the internal pointer to the RS Type of an allocation // Then sets the type_ptr member in Allocation with the result. // Returns true on success, false otherwise bool RenderScriptRuntime::JITTypePointer(AllocationDetails* allocation, StackFrame* frame_ptr) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); if (!allocation->address.isValid() || !allocation->context.isValid()) { if (log) log->Printf("RenderScriptRuntime::JITTypePointer - Failed to find allocation details"); return false; } const char* expr_cstr = JITTemplate(eExprAllocGetType); char buffer[jit_max_expr_size]; int chars_written = snprintf(buffer, jit_max_expr_size, expr_cstr, *allocation->context.get(), *allocation->address.get()); if (chars_written < 0) { if (log) log->Printf("RenderScriptRuntime::JITDataPointer - Encoding error in snprintf()"); return false; } else if (chars_written >= jit_max_expr_size) { if (log) log->Printf("RenderScriptRuntime::JITTypePointer - Expression too long"); return false; } uint64_t result = 0; if (!EvalRSExpression(buffer, frame_ptr, &result)) return false; addr_t type_ptr = static_cast(result); allocation->type_ptr = type_ptr; return true; } // JITs the RS runtime for information about the dimensions and type of an allocation // Then sets dimension and element_ptr members in Allocation with the result. // Returns true on success, false otherwise bool RenderScriptRuntime::JITTypePacked(AllocationDetails* allocation, StackFrame* frame_ptr) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); if (!allocation->type_ptr.isValid() || !allocation->context.isValid()) { if (log) log->Printf("RenderScriptRuntime::JITTypePacked - Failed to find allocation details"); return false; } // Expression is different depending on if device is 32 or 64 bit uint32_t archByteSize = GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize(); const unsigned int bits = archByteSize == 4 ? 32 : 64; // We want 4 elements from packed data const unsigned int num_exprs = 4; assert(num_exprs == (eExprTypeElemPtr - eExprTypeDimX + 1) && "Invalid number of expressions"); char buffer[num_exprs][jit_max_expr_size]; uint64_t results[num_exprs]; for (unsigned int i = 0; i < num_exprs; ++i) { const char* expr_cstr = JITTemplate((ExpressionStrings) (eExprTypeDimX + i)); int chars_written = snprintf(buffer[i], jit_max_expr_size, expr_cstr, bits, *allocation->context.get(), *allocation->type_ptr.get()); if (chars_written < 0) { if (log) log->Printf("RenderScriptRuntime::JITDataPointer - Encoding error in snprintf()"); return false; } else if (chars_written >= jit_max_expr_size) { if (log) log->Printf("RenderScriptRuntime::JITTypePacked - Expression too long"); return false; } // Perform expression evaluation if (!EvalRSExpression(buffer[i], frame_ptr, &results[i])) return false; } // Assign results to allocation members AllocationDetails::Dimension dims; dims.dim_1 = static_cast(results[0]); dims.dim_2 = static_cast(results[1]); dims.dim_3 = static_cast(results[2]); allocation->dimension = dims; addr_t elem_ptr = static_cast(results[3]); allocation->element.element_ptr = elem_ptr; if (log) log->Printf("RenderScriptRuntime::JITTypePacked - dims (%u, %u, %u) Element*: 0x%" PRIx64, dims.dim_1, dims.dim_2, dims.dim_3, elem_ptr); return true; } // JITs the RS runtime for information about the Element of an allocation // Then sets type, type_vec_size, field_count and type_kind members in Element with the result. // Returns true on success, false otherwise bool RenderScriptRuntime::JITElementPacked(Element& elem, const lldb::addr_t context, StackFrame* frame_ptr) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); if (!elem.element_ptr.isValid()) { if (log) log->Printf("RenderScriptRuntime::JITElementPacked - Failed to find allocation details"); return false; } // We want 4 elements from packed data const unsigned int num_exprs = 4; assert(num_exprs == (eExprElementFieldCount - eExprElementType + 1) && "Invalid number of expressions"); char buffer[num_exprs][jit_max_expr_size]; uint64_t results[num_exprs]; for (unsigned int i = 0; i < num_exprs; i++) { const char* expr_cstr = JITTemplate((ExpressionStrings) (eExprElementType + i)); int chars_written = snprintf(buffer[i], jit_max_expr_size, expr_cstr, context, *elem.element_ptr.get()); if (chars_written < 0) { if (log) log->Printf("RenderScriptRuntime::JITElementPacked - Encoding error in snprintf()"); return false; } else if (chars_written >= jit_max_expr_size) { if (log) log->Printf("RenderScriptRuntime::JITElementPacked - Expression too long"); return false; } // Perform expression evaluation if (!EvalRSExpression(buffer[i], frame_ptr, &results[i])) return false; } // Assign results to allocation members elem.type = static_cast(results[0]); elem.type_kind = static_cast(results[1]); elem.type_vec_size = static_cast(results[2]); elem.field_count = static_cast(results[3]); if (log) log->Printf("RenderScriptRuntime::JITElementPacked - data type %u, pixel type %u, vector size %u, field count %u", *elem.type.get(), *elem.type_kind.get(), *elem.type_vec_size.get(), *elem.field_count.get()); // If this Element has subelements then JIT rsaElementGetSubElements() for details about its fields if (*elem.field_count.get() > 0 && !JITSubelements(elem, context, frame_ptr)) return false; return true; } // JITs the RS runtime for information about the subelements/fields of a struct allocation // This is necessary for infering the struct type so we can pretty print the allocation's contents. // Returns true on success, false otherwise bool RenderScriptRuntime::JITSubelements(Element& elem, const lldb::addr_t context, StackFrame* frame_ptr) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); if (!elem.element_ptr.isValid() || !elem.field_count.isValid()) { if (log) log->Printf("RenderScriptRuntime::JITSubelements - Failed to find allocation details"); return false; } const short num_exprs = 3; assert(num_exprs == (eExprSubelementsArrSize - eExprSubelementsId + 1) && "Invalid number of expressions"); char expr_buffer[jit_max_expr_size]; uint64_t results; // Iterate over struct fields. const uint32_t field_count = *elem.field_count.get(); for (unsigned int field_index = 0; field_index < field_count; ++field_index) { Element child; for (unsigned int expr_index = 0; expr_index < num_exprs; ++expr_index) { const char* expr_cstr = JITTemplate((ExpressionStrings) (eExprSubelementsId + expr_index)); int chars_written = snprintf(expr_buffer, jit_max_expr_size, expr_cstr, field_count, field_count, field_count, context, *elem.element_ptr.get(), field_count, field_index); if (chars_written < 0) { if (log) log->Printf("RenderScriptRuntime::JITSubelements - Encoding error in snprintf()"); return false; } else if (chars_written >= jit_max_expr_size) { if (log) log->Printf("RenderScriptRuntime::JITSubelements - Expression too long"); return false; } // Perform expression evaluation if (!EvalRSExpression(expr_buffer, frame_ptr, &results)) return false; if (log) log->Printf("RenderScriptRuntime::JITSubelements - Expr result 0x%" PRIx64, results); switch(expr_index) { case 0: // Element* of child child.element_ptr = static_cast(results); break; case 1: // Name of child { lldb::addr_t address = static_cast(results); Error err; std::string name; GetProcess()->ReadCStringFromMemory(address, name, err); if (!err.Fail()) child.type_name = ConstString(name); else { if (log) log->Printf("RenderScriptRuntime::JITSubelements - Warning: Couldn't read field name"); } break; } case 2: // Array size of child child.array_size = static_cast(results); break; } } // We need to recursively JIT each Element field of the struct since // structs can be nested inside structs. if (!JITElementPacked(child, context, frame_ptr)) return false; elem.children.push_back(child); } // Try to infer the name of the struct type so we can pretty print the allocation contents. FindStructTypeName(elem, frame_ptr); return true; } // JITs the RS runtime for the address of the last element in the allocation. // The `elem_size` paramter represents the size of a single element, including padding. // Which is needed as an offset from the last element pointer. // Using this offset minus the starting address we can calculate the size of the allocation. // Returns true on success, false otherwise bool RenderScriptRuntime::JITAllocationSize(AllocationDetails* allocation, StackFrame* frame_ptr) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); if (!allocation->address.isValid() || !allocation->dimension.isValid() || !allocation->data_ptr.isValid() || !allocation->element.datum_size.isValid()) { if (log) log->Printf("RenderScriptRuntime::JITAllocationSize - Failed to find allocation details"); return false; } // Find dimensions unsigned int dim_x = allocation->dimension.get()->dim_1; unsigned int dim_y = allocation->dimension.get()->dim_2; unsigned int dim_z = allocation->dimension.get()->dim_3; // Our plan of jitting the last element address doesn't seem to work for struct Allocations // Instead try to infer the size ourselves without any inter element padding. if (allocation->element.children.size() > 0) { if (dim_x == 0) dim_x = 1; if (dim_y == 0) dim_y = 1; if (dim_z == 0) dim_z = 1; allocation->size = dim_x * dim_y * dim_z * *allocation->element.datum_size.get(); if (log) log->Printf("RenderScriptRuntime::JITAllocationSize - Infered size of struct allocation %u", *allocation->size.get()); return true; } const char* expr_cstr = JITTemplate(eExprGetOffsetPtr); char buffer[jit_max_expr_size]; // Calculate last element dim_x = dim_x == 0 ? 0 : dim_x - 1; dim_y = dim_y == 0 ? 0 : dim_y - 1; dim_z = dim_z == 0 ? 0 : dim_z - 1; int chars_written = snprintf(buffer, jit_max_expr_size, expr_cstr, *allocation->address.get(), dim_x, dim_y, dim_z); if (chars_written < 0) { if (log) log->Printf("RenderScriptRuntime::JITAllocationSize - Encoding error in snprintf()"); return false; } else if (chars_written >= jit_max_expr_size) { if (log) log->Printf("RenderScriptRuntime::JITAllocationSize - Expression too long"); return false; } uint64_t result = 0; if (!EvalRSExpression(buffer, frame_ptr, &result)) return false; addr_t mem_ptr = static_cast(result); // Find pointer to last element and add on size of an element allocation->size = static_cast(mem_ptr - *allocation->data_ptr.get()) + *allocation->element.datum_size.get(); return true; } // JITs the RS runtime for information about the stride between rows in the allocation. // This is done to detect padding, since allocated memory is 16-byte aligned. // Returns true on success, false otherwise bool RenderScriptRuntime::JITAllocationStride(AllocationDetails* allocation, StackFrame* frame_ptr) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); if (!allocation->address.isValid() || !allocation->data_ptr.isValid()) { if (log) log->Printf("RenderScriptRuntime::JITAllocationStride - Failed to find allocation details"); return false; } const char* expr_cstr = JITTemplate(eExprGetOffsetPtr); char buffer[jit_max_expr_size]; int chars_written = snprintf(buffer, jit_max_expr_size, expr_cstr, *allocation->address.get(), 0, 1, 0); if (chars_written < 0) { if (log) log->Printf("RenderScriptRuntime::JITAllocationStride - Encoding error in snprintf()"); return false; } else if (chars_written >= jit_max_expr_size) { if (log) log->Printf("RenderScriptRuntime::JITAllocationStride - Expression too long"); return false; } uint64_t result = 0; if (!EvalRSExpression(buffer, frame_ptr, &result)) return false; addr_t mem_ptr = static_cast(result); allocation->stride = static_cast(mem_ptr - *allocation->data_ptr.get()); return true; } // JIT all the current runtime info regarding an allocation bool RenderScriptRuntime::RefreshAllocation(AllocationDetails* allocation, StackFrame* frame_ptr) { // GetOffsetPointer() if (!JITDataPointer(allocation, frame_ptr)) return false; // rsaAllocationGetType() if (!JITTypePointer(allocation, frame_ptr)) return false; // rsaTypeGetNativeData() if (!JITTypePacked(allocation, frame_ptr)) return false; // rsaElementGetNativeData() if (!JITElementPacked(allocation->element, *allocation->context.get(), frame_ptr)) return false; // Sets the datum_size member in Element SetElementSize(allocation->element); // Use GetOffsetPointer() to infer size of the allocation if (!JITAllocationSize(allocation, frame_ptr)) return false; return true; } // Function attempts to set the type_name member of the paramaterised Element object. // This string should be the name of the struct type the Element represents. // We need this string for pretty printing the Element to users. void RenderScriptRuntime::FindStructTypeName(Element& elem, StackFrame* frame_ptr) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); if (!elem.type_name.IsEmpty()) // Name already set return; else elem.type_name = Element::GetFallbackStructName(); // Default type name if we don't succeed // Find all the global variables from the script rs modules VariableList variable_list; for (auto module_sp : m_rsmodules) module_sp->m_module->FindGlobalVariables(RegularExpression("."), true, UINT32_MAX, variable_list); // Iterate over all the global variables looking for one with a matching type to the Element. // We make the assumption a match exists since there needs to be a global variable to reflect the // struct type back into java host code. for (uint32_t var_index = 0; var_index < variable_list.GetSize(); ++var_index) { const VariableSP var_sp(variable_list.GetVariableAtIndex(var_index)); if (!var_sp) continue; ValueObjectSP valobj_sp = ValueObjectVariable::Create(frame_ptr, var_sp); if (!valobj_sp) continue; // Find the number of variable fields. // If it has no fields, or more fields than our Element, then it can't be the struct we're looking for. // Don't check for equality since RS can add extra struct members for padding. size_t num_children = valobj_sp->GetNumChildren(); if (num_children > elem.children.size() || num_children == 0) continue; // Iterate over children looking for members with matching field names. // If all the field names match, this is likely the struct we want. // // TODO: This could be made more robust by also checking children data sizes, or array size bool found = true; for (size_t child_index = 0; child_index < num_children; ++child_index) { ValueObjectSP child = valobj_sp->GetChildAtIndex(child_index, true); if (!child || (child->GetName() != elem.children[child_index].type_name)) { found = false; break; } } // RS can add extra struct members for padding in the format '#rs_padding_[0-9]+' if (found && num_children < elem.children.size()) { const unsigned int size_diff = elem.children.size() - num_children; if (log) log->Printf("RenderScriptRuntime::FindStructTypeName - %u padding struct entries", size_diff); for (unsigned int padding_index = 0; padding_index < size_diff; ++padding_index) { const ConstString& name = elem.children[num_children + padding_index].type_name; if (strcmp(name.AsCString(), "#rs_padding") < 0) found = false; } } // We've found a global var with matching type if (found) { // Dereference since our Element type isn't a pointer. if (valobj_sp->IsPointerType()) { Error err; ValueObjectSP deref_valobj = valobj_sp->Dereference(err); if (!err.Fail()) valobj_sp = deref_valobj; } // Save name of variable in Element. elem.type_name = valobj_sp->GetTypeName(); if (log) log->Printf("RenderScriptRuntime::FindStructTypeName - Element name set to %s", elem.type_name.AsCString()); return; } } } // Function sets the datum_size member of Element. Representing the size of a single instance including padding. // Assumes the relevant allocation information has already been jitted. void RenderScriptRuntime::SetElementSize(Element& elem) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); const Element::DataType type = *elem.type.get(); assert(type >= Element::RS_TYPE_NONE && type <= Element::RS_TYPE_FONT && "Invalid allocation type"); const unsigned int vec_size = *elem.type_vec_size.get(); unsigned int data_size = 0; unsigned int padding = 0; // Element is of a struct type, calculate size recursively. if ((type == Element::RS_TYPE_NONE) && (elem.children.size() > 0)) { for (Element& child : elem.children) { SetElementSize(child); const unsigned int array_size = child.array_size.isValid() ? *child.array_size.get() : 1; data_size += *child.datum_size.get() * array_size; } } else if (type == Element::RS_TYPE_UNSIGNED_5_6_5 || type == Element::RS_TYPE_UNSIGNED_5_5_5_1 || type == Element::RS_TYPE_UNSIGNED_4_4_4_4) // These have been packed already { data_size = AllocationDetails::RSTypeToFormat[type][eElementSize]; } else if (type < Element::RS_TYPE_ELEMENT) { data_size = vec_size * AllocationDetails::RSTypeToFormat[type][eElementSize]; if (vec_size == 3) padding = AllocationDetails::RSTypeToFormat[type][eElementSize]; } else data_size = GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize(); elem.padding = padding; elem.datum_size = data_size + padding; if (log) log->Printf("RenderScriptRuntime::SetElementSize - element size set to %u", data_size + padding); } // Given an allocation, this function copies the allocation contents from device into a buffer on the heap. // Returning a shared pointer to the buffer containing the data. std::shared_ptr RenderScriptRuntime::GetAllocationData(AllocationDetails* allocation, StackFrame* frame_ptr) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); // JIT all the allocation details if (allocation->shouldRefresh()) { if (log) log->Printf("RenderScriptRuntime::GetAllocationData - Allocation details not calculated yet, jitting info"); if (!RefreshAllocation(allocation, frame_ptr)) { if (log) log->Printf("RenderScriptRuntime::GetAllocationData - Couldn't JIT allocation details"); return nullptr; } } assert(allocation->data_ptr.isValid() && allocation->element.type.isValid() && allocation->element.type_vec_size.isValid() && allocation->size.isValid() && "Allocation information not available"); // Allocate a buffer to copy data into const unsigned int size = *allocation->size.get(); std::shared_ptr buffer(new uint8_t[size]); if (!buffer) { if (log) log->Printf("RenderScriptRuntime::GetAllocationData - Couldn't allocate a %u byte buffer", size); return nullptr; } // Read the inferior memory Error error; lldb::addr_t data_ptr = *allocation->data_ptr.get(); GetProcess()->ReadMemory(data_ptr, buffer.get(), size, error); if (error.Fail()) { if (log) log->Printf("RenderScriptRuntime::GetAllocationData - '%s' Couldn't read %u bytes of allocation data from 0x%" PRIx64, error.AsCString(), size, data_ptr); return nullptr; } return buffer; } // Function copies data from a binary file into an allocation. // There is a header at the start of the file, FileHeader, before the data content itself. // Information from this header is used to display warnings to the user about incompatabilities bool RenderScriptRuntime::LoadAllocation(Stream &strm, const uint32_t alloc_id, const char* filename, StackFrame* frame_ptr) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); // Find allocation with the given id AllocationDetails* alloc = FindAllocByID(strm, alloc_id); if (!alloc) return false; if (log) log->Printf("RenderScriptRuntime::LoadAllocation - Found allocation 0x%" PRIx64, *alloc->address.get()); // JIT all the allocation details if (alloc->shouldRefresh()) { if (log) log->Printf("RenderScriptRuntime::LoadAllocation - Allocation details not calculated yet, jitting info"); if (!RefreshAllocation(alloc, frame_ptr)) { if (log) log->Printf("RenderScriptRuntime::LoadAllocation - Couldn't JIT allocation details"); return false; } } assert(alloc->data_ptr.isValid() && alloc->element.type.isValid() && alloc->element.type_vec_size.isValid() && alloc->size.isValid() && alloc->element.datum_size.isValid() && "Allocation information not available"); // Check we can read from file FileSpec file(filename, true); if (!file.Exists()) { strm.Printf("Error: File %s does not exist", filename); strm.EOL(); return false; } if (!file.Readable()) { strm.Printf("Error: File %s does not have readable permissions", filename); strm.EOL(); return false; } // Read file into data buffer DataBufferSP data_sp(file.ReadFileContents()); // Cast start of buffer to FileHeader and use pointer to read metadata void* file_buffer = data_sp->GetBytes(); const AllocationDetails::FileHeader* head = static_cast(file_buffer); // Advance buffer past header file_buffer = static_cast(file_buffer) + head->hdr_size; if (log) log->Printf("RenderScriptRuntime::LoadAllocation - header type %u, element size %u", head->type, head->element_size); // Check if the target allocation and file both have the same number of bytes for an Element if (*alloc->element.datum_size.get() != head->element_size) { strm.Printf("Warning: Mismatched Element sizes - file %u bytes, allocation %u bytes", head->element_size, *alloc->element.datum_size.get()); strm.EOL(); } // Check if the target allocation and file both have the same integral type const unsigned int type = static_cast(*alloc->element.type.get()); if (type != head->type) { // Enum value isn't monotonous, so doesn't always index RsDataTypeToString array unsigned int printable_target_type_index = type; unsigned int printable_head_type_index = head->type; if (type >= Element::RS_TYPE_ELEMENT && type <= Element::RS_TYPE_FONT) printable_target_type_index = static_cast( (type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1); if (head->type >= Element::RS_TYPE_ELEMENT && head->type <= Element::RS_TYPE_FONT) printable_head_type_index = static_cast( (head->type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1); const char* file_type_cstr = AllocationDetails::RsDataTypeToString[printable_head_type_index][0]; const char* target_type_cstr = AllocationDetails::RsDataTypeToString[printable_target_type_index][0]; strm.Printf("Warning: Mismatched Types - file '%s' type, allocation '%s' type", file_type_cstr, target_type_cstr); strm.EOL(); } // Calculate size of allocation data in file size_t length = data_sp->GetByteSize() - head->hdr_size; // Check if the target allocation and file both have the same total data size. const unsigned int alloc_size = *alloc->size.get(); if (alloc_size != length) { strm.Printf("Warning: Mismatched allocation sizes - file 0x%" PRIx64 " bytes, allocation 0x%x bytes", (uint64_t) length, alloc_size); strm.EOL(); length = alloc_size < length ? alloc_size : length; // Set length to copy to minimum } // Copy file data from our buffer into the target allocation. lldb::addr_t alloc_data = *alloc->data_ptr.get(); Error error; size_t bytes_written = GetProcess()->WriteMemory(alloc_data, file_buffer, length, error); if (!error.Success() || bytes_written != length) { strm.Printf("Error: Couldn't write data to allocation %s", error.AsCString()); strm.EOL(); return false; } strm.Printf("Contents of file '%s' read into allocation %u", filename, alloc->id); strm.EOL(); return true; } // Function copies allocation contents into a binary file. // This file can then be loaded later into a different allocation. // There is a header, FileHeader, before the allocation data containing meta-data. bool RenderScriptRuntime::SaveAllocation(Stream &strm, const uint32_t alloc_id, const char* filename, StackFrame* frame_ptr) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); // Find allocation with the given id AllocationDetails* alloc = FindAllocByID(strm, alloc_id); if (!alloc) return false; if (log) log->Printf("RenderScriptRuntime::SaveAllocation - Found allocation 0x%" PRIx64, *alloc->address.get()); // JIT all the allocation details if (alloc->shouldRefresh()) { if (log) log->Printf("RenderScriptRuntime::SaveAllocation - Allocation details not calculated yet, jitting info"); if (!RefreshAllocation(alloc, frame_ptr)) { if (log) log->Printf("RenderScriptRuntime::SaveAllocation - Couldn't JIT allocation details"); return false; } } assert(alloc->data_ptr.isValid() && alloc->element.type.isValid() && alloc->element.type_vec_size.isValid() && alloc->element.datum_size.get() && alloc->element.type_kind.isValid() && alloc->dimension.isValid() && "Allocation information not available"); // Check we can create writable file FileSpec file_spec(filename, true); File file(file_spec, File::eOpenOptionWrite | File::eOpenOptionCanCreate | File::eOpenOptionTruncate); if (!file) { strm.Printf("Error: Failed to open '%s' for writing", filename); strm.EOL(); return false; } // Read allocation into buffer of heap memory const std::shared_ptr buffer = GetAllocationData(alloc, frame_ptr); if (!buffer) { strm.Printf("Error: Couldn't read allocation data into buffer"); strm.EOL(); return false; } // Create the file header AllocationDetails::FileHeader head; head.ident[0] = 'R'; head.ident[1] = 'S'; head.ident[2] = 'A'; head.ident[3] = 'D'; head.hdr_size = static_cast(sizeof(AllocationDetails::FileHeader)); head.type = static_cast(*alloc->element.type.get()); head.kind = static_cast(*alloc->element.type_kind.get()); head.dims[0] = static_cast(alloc->dimension.get()->dim_1); head.dims[1] = static_cast(alloc->dimension.get()->dim_2); head.dims[2] = static_cast(alloc->dimension.get()->dim_3); head.element_size = static_cast(*alloc->element.datum_size.get()); // Write the file header size_t num_bytes = sizeof(AllocationDetails::FileHeader); Error err = file.Write(static_cast(&head), num_bytes); if (!err.Success()) { strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), filename); strm.EOL(); return false; } // Write allocation data to file num_bytes = static_cast(*alloc->size.get()); if (log) - log->Printf("RenderScriptRuntime::SaveAllocation - Writing 0x%" PRIx64 " bytes from %p", (uint64_t) num_bytes, buffer.get()); + log->Printf("RenderScriptRuntime::SaveAllocation - Writing 0x%" PRIx64 " bytes from %p", (uint64_t) num_bytes, (void*) buffer.get()); err = file.Write(buffer.get(), num_bytes); if (!err.Success()) { strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), filename); strm.EOL(); return false; } strm.Printf("Allocation written to file '%s'", filename); strm.EOL(); return true; } bool RenderScriptRuntime::LoadModule(const lldb::ModuleSP &module_sp) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); if (module_sp) { for (const auto &rs_module : m_rsmodules) { if (rs_module->m_module == module_sp) { // Check if the user has enabled automatically breaking on // all RS kernels. if (m_breakAllKernels) BreakOnModuleKernels(rs_module); return false; } } bool module_loaded = false; switch (GetModuleKind(module_sp)) { case eModuleKindKernelObj: { RSModuleDescriptorSP module_desc; module_desc.reset(new RSModuleDescriptor(module_sp)); if (module_desc->ParseRSInfo()) { m_rsmodules.push_back(module_desc); module_loaded = true; } if (module_loaded) { FixupScriptDetails(module_desc); } break; } case eModuleKindDriver: { if (!m_libRSDriver) { m_libRSDriver = module_sp; LoadRuntimeHooks(m_libRSDriver, RenderScriptRuntime::eModuleKindDriver); } break; } case eModuleKindImpl: { m_libRSCpuRef = module_sp; break; } case eModuleKindLibRS: { - if (!m_libRS) + if (!m_libRS) { m_libRS = module_sp; static ConstString gDbgPresentStr("gDebuggerPresent"); const Symbol* debug_present = m_libRS->FindFirstSymbolWithNameAndType(gDbgPresentStr, eSymbolTypeData); if (debug_present) { Error error; uint32_t flag = 0x00000001U; Target &target = GetProcess()->GetTarget(); addr_t addr = debug_present->GetLoadAddress(&target); GetProcess()->WriteMemory(addr, &flag, sizeof(flag), error); if(error.Success()) { if (log) log->Printf ("RenderScriptRuntime::LoadModule - Debugger present flag set on debugee"); m_debuggerPresentFlagged = true; } else if (log) { log->Printf ("RenderScriptRuntime::LoadModule - Error writing debugger present flags '%s' ", error.AsCString()); } } else if (log) { log->Printf ("RenderScriptRuntime::LoadModule - Error writing debugger present flags - symbol not found"); } } break; } default: break; } if (module_loaded) - Update(); + Update(); return module_loaded; } return false; } void RenderScriptRuntime::Update() { if (m_rsmodules.size() > 0) { if (!m_initiated) { Initiate(); } } } // The maximum line length of an .rs.info packet #define MAXLINE 500 // The .rs.info symbol in renderscript modules contains a string which needs to be parsed. // The string is basic and is parsed on a line by line basis. bool RSModuleDescriptor::ParseRSInfo() { const Symbol *info_sym = m_module->FindFirstSymbolWithNameAndType(ConstString(".rs.info"), eSymbolTypeData); if (info_sym) { const addr_t addr = info_sym->GetAddressRef().GetFileAddress(); const addr_t size = info_sym->GetByteSize(); const FileSpec fs = m_module->GetFileSpec(); DataBufferSP buffer = fs.ReadFileContents(addr, size); if (!buffer) return false; std::string info((const char *)buffer->GetBytes()); std::vector info_lines; size_t lpos = info.find('\n'); while (lpos != std::string::npos) { info_lines.push_back(info.substr(0, lpos)); info = info.substr(lpos + 1); lpos = info.find('\n'); } size_t offset = 0; while (offset < info_lines.size()) { std::string line = info_lines[offset]; // Parse directives uint32_t numDefns = 0; if (sscanf(line.c_str(), "exportVarCount: %u", &numDefns) == 1) { while (numDefns--) m_globals.push_back(RSGlobalDescriptor(this, info_lines[++offset].c_str())); } else if (sscanf(line.c_str(), "exportFuncCount: %u", &numDefns) == 1) { } else if (sscanf(line.c_str(), "exportForEachCount: %u", &numDefns) == 1) { char name[MAXLINE]; while (numDefns--) { uint32_t slot = 0; name[0] = '\0'; if (sscanf(info_lines[++offset].c_str(), "%u - %s", &slot, &name[0]) == 2) { m_kernels.push_back(RSKernelDescriptor(this, name, slot)); } } - } + } else if (sscanf(line.c_str(), "pragmaCount: %u", &numDefns) == 1) { char name[MAXLINE]; char value[MAXLINE]; while (numDefns--) { name[0] = '\0'; value[0] = '\0'; - if (sscanf(info_lines[++offset].c_str(), "%s - %s", &name[0], &value[0]) != 0 + if (sscanf(info_lines[++offset].c_str(), "%s - %s", &name[0], &value[0]) != 0 && (name[0] != '\0')) { m_pragmas[std::string(name)] = value; } } } else if (sscanf(line.c_str(), "objectSlotCount: %u", &numDefns) == 1) { } offset++; } return m_kernels.size() > 0; } return false; } bool RenderScriptRuntime::ProbeModules(const ModuleList module_list) { bool rs_found = false; size_t num_modules = module_list.GetSize(); for (size_t i = 0; i < num_modules; i++) { auto module = module_list.GetModuleAtIndex(i); rs_found |= LoadModule(module); } return rs_found; } void RenderScriptRuntime::Status(Stream &strm) const { if (m_libRS) { strm.Printf("Runtime Library discovered."); strm.EOL(); } if (m_libRSDriver) { strm.Printf("Runtime Driver discovered."); strm.EOL(); } if (m_libRSCpuRef) { strm.Printf("CPU Reference Implementation discovered."); strm.EOL(); } - + if (m_runtimeHooks.size()) { strm.Printf("Runtime functions hooked:"); strm.EOL(); for (auto b : m_runtimeHooks) { strm.Indent(b.second->defn->name); strm.EOL(); } - } + } else { strm.Printf("Runtime is not hooked."); strm.EOL(); } } -void +void RenderScriptRuntime::DumpContexts(Stream &strm) const { strm.Printf("Inferred RenderScript Contexts:"); strm.EOL(); strm.IndentMore(); std::map contextReferences; // Iterate over all of the currently discovered scripts. // Note: We cant push or pop from m_scripts inside this loop or it may invalidate script. for (const auto & script : m_scripts) { if (!script->context.isValid()) continue; lldb::addr_t context = *script->context; if (contextReferences.find(context) != contextReferences.end()) { contextReferences[context]++; } else { contextReferences[context] = 1; } } for (const auto& cRef : contextReferences) { strm.Printf("Context 0x%" PRIx64 ": %" PRIu64 " script instances", cRef.first, cRef.second); strm.EOL(); } strm.IndentLess(); } -void +void RenderScriptRuntime::DumpKernels(Stream &strm) const { strm.Printf("RenderScript Kernels:"); strm.EOL(); strm.IndentMore(); for (const auto &module : m_rsmodules) { strm.Printf("Resource '%s':",module->m_resname.c_str()); strm.EOL(); for (const auto &kernel : module->m_kernels) { strm.Indent(kernel.m_name.AsCString()); strm.EOL(); } } strm.IndentLess(); } RenderScriptRuntime::AllocationDetails* RenderScriptRuntime::FindAllocByID(Stream &strm, const uint32_t alloc_id) { AllocationDetails* alloc = nullptr; // See if we can find allocation using id as an index; if (alloc_id <= m_allocations.size() && alloc_id != 0 && m_allocations[alloc_id-1]->id == alloc_id) { alloc = m_allocations[alloc_id-1].get(); return alloc; } // Fallback to searching for (const auto & a : m_allocations) { if (a->id == alloc_id) { alloc = a.get(); break; } } if (alloc == nullptr) { strm.Printf("Error: Couldn't find allocation with id matching %u", alloc_id); strm.EOL(); } return alloc; } // Prints the contents of an allocation to the output stream, which may be a file bool RenderScriptRuntime::DumpAllocation(Stream &strm, StackFrame* frame_ptr, const uint32_t id) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); // Check we can find the desired allocation AllocationDetails* alloc = FindAllocByID(strm, id); if (!alloc) return false; // FindAllocByID() will print error message for us here if (log) log->Printf("RenderScriptRuntime::DumpAllocation - Found allocation 0x%" PRIx64, *alloc->address.get()); // Check we have information about the allocation, if not calculate it if (alloc->shouldRefresh()) { if (log) log->Printf("RenderScriptRuntime::DumpAllocation - Allocation details not calculated yet, jitting info"); // JIT all the allocation information if (!RefreshAllocation(alloc, frame_ptr)) { strm.Printf("Error: Couldn't JIT allocation details"); strm.EOL(); return false; } } // Establish format and size of each data element const unsigned int vec_size = *alloc->element.type_vec_size.get(); const Element::DataType type = *alloc->element.type.get(); assert(type >= Element::RS_TYPE_NONE && type <= Element::RS_TYPE_FONT && "Invalid allocation type"); lldb::Format format; if (type >= Element::RS_TYPE_ELEMENT) format = eFormatHex; else format = vec_size == 1 ? static_cast(AllocationDetails::RSTypeToFormat[type][eFormatSingle]) : static_cast(AllocationDetails::RSTypeToFormat[type][eFormatVector]); const unsigned int data_size = *alloc->element.datum_size.get(); if (log) log->Printf("RenderScriptRuntime::DumpAllocation - Element size %u bytes, including padding", data_size); // Allocate a buffer to copy data into std::shared_ptr buffer = GetAllocationData(alloc, frame_ptr); if (!buffer) { strm.Printf("Error: Couldn't read allocation data"); strm.EOL(); return false; } // Calculate stride between rows as there may be padding at end of rows since // allocated memory is 16-byte aligned if (!alloc->stride.isValid()) { if (alloc->dimension.get()->dim_2 == 0) // We only have one dimension alloc->stride = 0; else if (!JITAllocationStride(alloc, frame_ptr)) { strm.Printf("Error: Couldn't calculate allocation row stride"); strm.EOL(); return false; } } const unsigned int stride = *alloc->stride.get(); const unsigned int size = *alloc->size.get(); // Size of whole allocation const unsigned int padding = alloc->element.padding.isValid() ? *alloc->element.padding.get() : 0; if (log) log->Printf("RenderScriptRuntime::DumpAllocation - stride %u bytes, size %u bytes, padding %u", stride, size, padding); // Find dimensions used to index loops, so need to be non-zero unsigned int dim_x = alloc->dimension.get()->dim_1; dim_x = dim_x == 0 ? 1 : dim_x; unsigned int dim_y = alloc->dimension.get()->dim_2; dim_y = dim_y == 0 ? 1 : dim_y; unsigned int dim_z = alloc->dimension.get()->dim_3; dim_z = dim_z == 0 ? 1 : dim_z; // Use data extractor to format output const uint32_t archByteSize = GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize(); DataExtractor alloc_data(buffer.get(), size, GetProcess()->GetByteOrder(), archByteSize); unsigned int offset = 0; // Offset in buffer to next element to be printed unsigned int prev_row = 0; // Offset to the start of the previous row // Iterate over allocation dimensions, printing results to user strm.Printf("Data (X, Y, Z):"); for (unsigned int z = 0; z < dim_z; ++z) { for (unsigned int y = 0; y < dim_y; ++y) { // Use stride to index start of next row. if (!(y==0 && z==0)) offset = prev_row + stride; prev_row = offset; // Print each element in the row individually for (unsigned int x = 0; x < dim_x; ++x) { strm.Printf("\n(%u, %u, %u) = ", x, y, z); if ((type == Element::RS_TYPE_NONE) && (alloc->element.children.size() > 0) && (alloc->element.type_name != Element::GetFallbackStructName())) { // Here we are dumping an Element of struct type. // This is done using expression evaluation with the name of the struct type and pointer to element. // Don't print the name of the resulting expression, since this will be '$[0-9]+' DumpValueObjectOptions expr_options; expr_options.SetHideName(true); // Setup expression as derefrencing a pointer cast to element address. char expr_char_buffer[jit_max_expr_size]; int chars_written = snprintf(expr_char_buffer, jit_max_expr_size, "*(%s*) 0x%" PRIx64, alloc->element.type_name.AsCString(), *alloc->data_ptr.get() + offset); if (chars_written < 0 || chars_written >= jit_max_expr_size) { if (log) log->Printf("RenderScriptRuntime::DumpAllocation- Error in snprintf()"); continue; } // Evaluate expression ValueObjectSP expr_result; GetProcess()->GetTarget().EvaluateExpression(expr_char_buffer, frame_ptr, expr_result); // Print the results to our stream. expr_result->Dump(strm, expr_options); } else { alloc_data.Dump(&strm, offset, format, data_size - padding, 1, 1, LLDB_INVALID_ADDRESS, 0, 0); } offset += data_size; } } } strm.EOL(); return true; } // Prints infomation regarding all the currently loaded allocations. // These details are gathered by jitting the runtime, which has as latency. void RenderScriptRuntime::ListAllocations(Stream &strm, StackFrame* frame_ptr, bool recompute) { strm.Printf("RenderScript Allocations:"); strm.EOL(); strm.IndentMore(); for (auto &alloc : m_allocations) { // JIT the allocation info if we haven't done it, or the user forces us to. bool do_refresh = alloc->shouldRefresh() || recompute; // JIT current allocation information if (do_refresh && !RefreshAllocation(alloc.get(), frame_ptr)) { strm.Printf("Error: Couldn't evaluate details for allocation %u\n", alloc->id); continue; } strm.Printf("%u:\n",alloc->id); strm.IndentMore(); strm.Indent("Context: "); if (!alloc->context.isValid()) strm.Printf("unknown\n"); else strm.Printf("0x%" PRIx64 "\n", *alloc->context.get()); strm.Indent("Address: "); if (!alloc->address.isValid()) strm.Printf("unknown\n"); else strm.Printf("0x%" PRIx64 "\n", *alloc->address.get()); strm.Indent("Data pointer: "); if (!alloc->data_ptr.isValid()) strm.Printf("unknown\n"); else strm.Printf("0x%" PRIx64 "\n", *alloc->data_ptr.get()); strm.Indent("Dimensions: "); if (!alloc->dimension.isValid()) strm.Printf("unknown\n"); else strm.Printf("(%d, %d, %d)\n", alloc->dimension.get()->dim_1, alloc->dimension.get()->dim_2, alloc->dimension.get()->dim_3); strm.Indent("Data Type: "); if (!alloc->element.type.isValid() || !alloc->element.type_vec_size.isValid()) strm.Printf("unknown\n"); else { const int vector_size = *alloc->element.type_vec_size.get(); Element::DataType type = *alloc->element.type.get(); if (!alloc->element.type_name.IsEmpty()) strm.Printf("%s\n", alloc->element.type_name.AsCString()); else { // Enum value isn't monotonous, so doesn't always index RsDataTypeToString array if (type >= Element::RS_TYPE_ELEMENT && type <= Element::RS_TYPE_FONT) type = static_cast((type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1); if (type >= (sizeof(AllocationDetails::RsDataTypeToString) / sizeof(AllocationDetails::RsDataTypeToString[0])) || vector_size > 4 || vector_size < 1) strm.Printf("invalid type\n"); else strm.Printf("%s\n", AllocationDetails::RsDataTypeToString[static_cast(type)][vector_size-1]); } } strm.Indent("Data Kind: "); if (!alloc->element.type_kind.isValid()) strm.Printf("unknown\n"); else { const Element::DataKind kind = *alloc->element.type_kind.get(); if (kind < Element::RS_KIND_USER || kind > Element::RS_KIND_PIXEL_YUV) strm.Printf("invalid kind\n"); else strm.Printf("%s\n", AllocationDetails::RsDataKindToString[static_cast(kind)]); } strm.EOL(); strm.IndentLess(); } strm.IndentLess(); } // Set breakpoints on every kernel found in RS module void RenderScriptRuntime::BreakOnModuleKernels(const RSModuleDescriptorSP rsmodule_sp) { for (const auto &kernel : rsmodule_sp->m_kernels) { // Don't set breakpoint on 'root' kernel if (strcmp(kernel.m_name.AsCString(), "root") == 0) continue; CreateKernelBreakpoint(kernel.m_name); } } // Method is internally called by the 'kernel breakpoint all' command to // enable or disable breaking on all kernels. // // When do_break is true we want to enable this functionality. // When do_break is false we want to disable it. void RenderScriptRuntime::SetBreakAllKernels(bool do_break, TargetSP target) { Log* log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS)); InitSearchFilter(target); // Set breakpoints on all the kernels if (do_break && !m_breakAllKernels) { m_breakAllKernels = true; for (const auto &module : m_rsmodules) BreakOnModuleKernels(module); if (log) log->Printf("RenderScriptRuntime::SetBreakAllKernels(True)" "- breakpoints set on all currently loaded kernels"); } else if (!do_break && m_breakAllKernels) // Breakpoints won't be set on any new kernels. { m_breakAllKernels = false; if (log) log->Printf("RenderScriptRuntime::SetBreakAllKernels(False) - breakpoints no longer automatically set"); } } // Given the name of a kernel this function creates a breakpoint using our // own breakpoint resolver, and returns the Breakpoint shared pointer. BreakpointSP RenderScriptRuntime::CreateKernelBreakpoint(const ConstString& name) { Log* log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS)); if (!m_filtersp) { if (log) log->Printf("RenderScriptRuntime::CreateKernelBreakpoint - Error: No breakpoint search filter set"); return nullptr; } BreakpointResolverSP resolver_sp(new RSBreakpointResolver(nullptr, name)); BreakpointSP bp = GetProcess()->GetTarget().CreateBreakpoint(m_filtersp, resolver_sp, false, false, false); // Give RS breakpoints a specific name, so the user can manipulate them as a group. Error err; if (!bp->AddName("RenderScriptKernel", err) && log) log->Printf("RenderScriptRuntime::CreateKernelBreakpoint: Error setting break name, %s", err.AsCString()); return bp; } // Given an expression for a variable this function tries to calculate the variable's value. // If this is possible it returns true and sets the uint64_t parameter to the variables unsigned value. // Otherwise function returns false. bool RenderScriptRuntime::GetFrameVarAsUnsigned(const StackFrameSP frame_sp, const char* var_name, uint64_t& val) { Log* log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE)); Error error; VariableSP var_sp; // Find variable in stack frame ValueObjectSP value_sp(frame_sp->GetValueForVariableExpressionPath(var_name, eNoDynamicValues, StackFrame::eExpressionPathOptionCheckPtrVsMember | StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, var_sp, error)); if (!error.Success()) { if (log) log->Printf("RenderScriptRuntime::GetFrameVarAsUnsigned - Error, couldn't find '%s' in frame", var_name); return false; } // Find the unsigned int value for the variable bool success = false; val = value_sp->GetValueAsUnsigned(0, &success); if (!success) { if (log) log->Printf("RenderScriptRuntime::GetFrameVarAsUnsigned - Error, couldn't parse '%s' as an unsigned int", var_name); return false; } return true; } // Callback when a kernel breakpoint hits and we're looking for a specific coordinate. // Baton parameter contains a pointer to the target coordinate we want to break on. // Function then checks the .expand frame for the current coordinate and breaks to user if it matches. // Parameter 'break_id' is the id of the Breakpoint which made the callback. // Parameter 'break_loc_id' is the id for the BreakpointLocation which was hit, // a single logical breakpoint can have multiple addresses. bool RenderScriptRuntime::KernelBreakpointHit(void *baton, StoppointCallbackContext *ctx, user_id_t break_id, user_id_t break_loc_id) { Log* log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS)); assert(baton && "Error: null baton in conditional kernel breakpoint callback"); // Coordinate we want to stop on const int* target_coord = static_cast(baton); if (log) log->Printf("RenderScriptRuntime::KernelBreakpointHit - Break ID %" PRIu64 ", target coord (%d, %d, %d)", break_id, target_coord[0], target_coord[1], target_coord[2]); // Go up one stack frame to .expand kernel ExecutionContext context(ctx->exe_ctx_ref); ThreadSP thread_sp = context.GetThreadSP(); if (!thread_sp->SetSelectedFrameByIndex(1)) { if (log) log->Printf("RenderScriptRuntime::KernelBreakpointHit - Error, couldn't go up stack frame"); return false; } StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); if (!frame_sp) { if (log) log->Printf("RenderScriptRuntime::KernelBreakpointHit - Error, couldn't select .expand stack frame"); return false; } // Get values for variables in .expand frame that tell us the current kernel invocation const char* coord_expressions[] = {"rsIndex", "p->current.y", "p->current.z"}; uint64_t current_coord[3] = {0, 0, 0}; for(int i = 0; i < 3; ++i) { if (!GetFrameVarAsUnsigned(frame_sp, coord_expressions[i], current_coord[i])) return false; if (log) log->Printf("RenderScriptRuntime::KernelBreakpointHit, %s = %" PRIu64, coord_expressions[i], current_coord[i]); } // Check if the current kernel invocation coordinate matches our target coordinate if (current_coord[0] == static_cast(target_coord[0]) && current_coord[1] == static_cast(target_coord[1]) && current_coord[2] == static_cast(target_coord[2])) { if (log) log->Printf("RenderScriptRuntime::KernelBreakpointHit, BREAKING %" PRIu64 ", %" PRIu64 ", %" PRIu64, current_coord[0], current_coord[1], current_coord[2]); BreakpointSP breakpoint_sp = context.GetTargetPtr()->GetBreakpointByID(break_id); assert(breakpoint_sp != nullptr && "Error: Couldn't find breakpoint matching break id for callback"); breakpoint_sp->SetEnabled(false); // Optimise since conditional breakpoint should only be hit once. return true; } // No match on coordinate return false; } // Tries to set a breakpoint on the start of a kernel, resolved using the kernel name. // Argument 'coords', represents a three dimensional coordinate which can be used to specify // a single kernel instance to break on. If this is set then we add a callback to the breakpoint. void RenderScriptRuntime::PlaceBreakpointOnKernel(Stream &strm, const char* name, const std::array coords, Error& error, TargetSP target) { if (!name) { error.SetErrorString("invalid kernel name"); return; } InitSearchFilter(target); ConstString kernel_name(name); BreakpointSP bp = CreateKernelBreakpoint(kernel_name); // We have a conditional breakpoint on a specific coordinate if (coords[0] != -1) { strm.Printf("Conditional kernel breakpoint on coordinate %d, %d, %d", coords[0], coords[1], coords[2]); strm.EOL(); // Allocate memory for the baton, and copy over coordinate int* baton = new int[3]; baton[0] = coords[0]; baton[1] = coords[1]; baton[2] = coords[2]; // Create a callback that will be invoked everytime the breakpoint is hit. // The baton object passed to the handler is the target coordinate we want to break on. bp->SetCallback(KernelBreakpointHit, baton, true); // Store a shared pointer to the baton, so the memory will eventually be cleaned up after destruction m_conditional_breaks[bp->GetID()] = std::shared_ptr(baton); } if (bp) bp->GetDescription(&strm, lldb::eDescriptionLevelInitial, false); } void RenderScriptRuntime::DumpModules(Stream &strm) const { strm.Printf("RenderScript Modules:"); strm.EOL(); strm.IndentMore(); for (const auto &module : m_rsmodules) { module->Dump(strm); } strm.IndentLess(); } RenderScriptRuntime::ScriptDetails* RenderScriptRuntime::LookUpScript(addr_t address, bool create) { for (const auto & s : m_scripts) { if (s->script.isValid()) if (*s->script == address) return s.get(); } if (create) { std::unique_ptr s(new ScriptDetails); s->script = address; m_scripts.push_back(std::move(s)); return m_scripts.back().get(); } return nullptr; } RenderScriptRuntime::AllocationDetails* RenderScriptRuntime::LookUpAllocation(addr_t address, bool create) { for (const auto & a : m_allocations) { if (a->address.isValid()) if (*a->address == address) return a.get(); } if (create) { std::unique_ptr a(new AllocationDetails); a->address = address; m_allocations.push_back(std::move(a)); return m_allocations.back().get(); } return nullptr; } void RSModuleDescriptor::Dump(Stream &strm) const { strm.Indent(); m_module->GetFileSpec().Dump(&strm); if(m_module->GetNumCompileUnits()) { strm.Indent("Debug info loaded."); } else { strm.Indent("Debug info does not exist."); } strm.EOL(); strm.IndentMore(); strm.Indent(); strm.Printf("Globals: %" PRIu64, static_cast(m_globals.size())); strm.EOL(); strm.IndentMore(); for (const auto &global : m_globals) { global.Dump(strm); } strm.IndentLess(); strm.Indent(); strm.Printf("Kernels: %" PRIu64, static_cast(m_kernels.size())); strm.EOL(); strm.IndentMore(); for (const auto &kernel : m_kernels) { kernel.Dump(strm); } strm.Printf("Pragmas: %" PRIu64 , static_cast(m_pragmas.size())); strm.EOL(); strm.IndentMore(); for (const auto &key_val : m_pragmas) { strm.Printf("%s: %s", key_val.first.c_str(), key_val.second.c_str()); strm.EOL(); } strm.IndentLess(4); } void RSGlobalDescriptor::Dump(Stream &strm) const { strm.Indent(m_name.AsCString()); VariableList var_list; m_module->m_module->FindGlobalVariables(m_name, nullptr, true, 1U, var_list); if (var_list.GetSize() == 1) { auto var = var_list.GetVariableAtIndex(0); auto type = var->GetType(); if(type) { strm.Printf(" - "); type->DumpTypeName(&strm); } else { strm.Printf(" - Unknown Type"); } } else { strm.Printf(" - variable identified, but not found in binary"); const Symbol* s = m_module->m_module->FindFirstSymbolWithNameAndType(m_name, eSymbolTypeData); if (s) { strm.Printf(" (symbol exists) "); } } strm.EOL(); } void RSKernelDescriptor::Dump(Stream &strm) const { strm.Indent(m_name.AsCString()); strm.EOL(); } class CommandObjectRenderScriptRuntimeModuleProbe : public CommandObjectParsed { public: CommandObjectRenderScriptRuntimeModuleProbe(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "renderscript module probe", "Initiates a Probe of all loaded modules for kernels and other renderscript objects.", "renderscript module probe", eCommandRequiresTarget | eCommandRequiresProcess | eCommandProcessMustBeLaunched) { } ~CommandObjectRenderScriptRuntimeModuleProbe() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { Target *target = m_exe_ctx.GetTargetPtr(); RenderScriptRuntime *runtime = (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); auto module_list = target->GetImages(); bool new_rs_details = runtime->ProbeModules(module_list); if (new_rs_details) { result.AppendMessage("New renderscript modules added to runtime model."); } result.SetStatus(eReturnStatusSuccessFinishResult); return true; } result.AppendErrorWithFormat("'%s' takes no arguments", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } }; class CommandObjectRenderScriptRuntimeModuleDump : public CommandObjectParsed { public: CommandObjectRenderScriptRuntimeModuleDump(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "renderscript module dump", "Dumps renderscript specific information for all modules.", "renderscript module dump", eCommandRequiresProcess | eCommandProcessMustBeLaunched) { } ~CommandObjectRenderScriptRuntimeModuleDump() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { RenderScriptRuntime *runtime = (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); runtime->DumpModules(result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } }; class CommandObjectRenderScriptRuntimeModule : public CommandObjectMultiword { public: CommandObjectRenderScriptRuntimeModule(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "renderscript module", "Commands that deal with renderscript modules.", NULL) { LoadSubCommand("probe", CommandObjectSP(new CommandObjectRenderScriptRuntimeModuleProbe(interpreter))); LoadSubCommand("dump", CommandObjectSP(new CommandObjectRenderScriptRuntimeModuleDump(interpreter))); } ~CommandObjectRenderScriptRuntimeModule() override = default; }; class CommandObjectRenderScriptRuntimeKernelList : public CommandObjectParsed { public: CommandObjectRenderScriptRuntimeKernelList(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "renderscript kernel list", "Lists renderscript kernel names and associated script resources.", "renderscript kernel list", eCommandRequiresProcess | eCommandProcessMustBeLaunched) { } ~CommandObjectRenderScriptRuntimeKernelList() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { RenderScriptRuntime *runtime = (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); runtime->DumpKernels(result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } }; class CommandObjectRenderScriptRuntimeKernelBreakpointSet : public CommandObjectParsed { public: CommandObjectRenderScriptRuntimeKernelBreakpointSet(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "renderscript kernel breakpoint set", "Sets a breakpoint on a renderscript kernel.", "renderscript kernel breakpoint set [-c x,y,z]", eCommandRequiresProcess | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options(interpreter) { } ~CommandObjectRenderScriptRuntimeKernelBreakpointSet() override = default; Options* GetOptions() override { return &m_options; } class CommandOptions : public Options { public: CommandOptions(CommandInterpreter &interpreter) : Options(interpreter) { } ~CommandOptions() override = default; Error SetOptionValue(uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'c': if (!ParseCoordinate(option_arg)) error.SetErrorStringWithFormat("Couldn't parse coordinate '%s', should be in format 'x,y,z'.", option_arg); break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } // -c takes an argument of the form 'num[,num][,num]'. // Where 'id_cstr' is this argument with the whitespace trimmed. // Missing coordinates are defaulted to zero. bool ParseCoordinate(const char* id_cstr) { RegularExpression regex; RegularExpression::Match regex_match(3); bool matched = false; if(regex.Compile("^([0-9]+),([0-9]+),([0-9]+)$") && regex.Execute(id_cstr, ®ex_match)) matched = true; else if(regex.Compile("^([0-9]+),([0-9]+)$") && regex.Execute(id_cstr, ®ex_match)) matched = true; else if(regex.Compile("^([0-9]+)$") && regex.Execute(id_cstr, ®ex_match)) matched = true; for(uint32_t i = 0; i < 3; i++) { std::string group; if(regex_match.GetMatchAtIndex(id_cstr, i + 1, group)) m_coord[i] = (uint32_t)strtoul(group.c_str(), NULL, 0); else m_coord[i] = 0; } return matched; } void OptionParsingStarting() override { // -1 means the -c option hasn't been set m_coord[0] = -1; m_coord[1] = -1; m_coord[2] = -1; } const OptionDefinition* GetDefinitions() override { return g_option_table; } static OptionDefinition g_option_table[]; std::array m_coord; }; bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc < 1) { result.AppendErrorWithFormat("'%s' takes 1 argument of kernel name, and an optional coordinate.", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } RenderScriptRuntime *runtime = (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); Error error; runtime->PlaceBreakpointOnKernel(result.GetOutputStream(), command.GetArgumentAtIndex(0), m_options.m_coord, error, m_exe_ctx.GetTargetSP()); if (error.Success()) { result.AppendMessage("Breakpoint(s) created"); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } result.SetStatus(eReturnStatusFailed); result.AppendErrorWithFormat("Error: %s", error.AsCString()); return false; } private: CommandOptions m_options; }; OptionDefinition CommandObjectRenderScriptRuntimeKernelBreakpointSet::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_1, false, "coordinate", 'c', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeValue, "Set a breakpoint on a single invocation of the kernel with specified coordinate.\n" "Coordinate takes the form 'x[,y][,z] where x,y,z are positive integers representing kernel dimensions. " "Any unset dimensions will be defaulted to zero."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; class CommandObjectRenderScriptRuntimeKernelBreakpointAll : public CommandObjectParsed { public: CommandObjectRenderScriptRuntimeKernelBreakpointAll(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "renderscript kernel breakpoint all", "Automatically sets a breakpoint on all renderscript kernels that are or will be loaded.\n" "Disabling option means breakpoints will no longer be set on any kernels loaded in the future, " "but does not remove currently set breakpoints.", "renderscript kernel breakpoint all ", eCommandRequiresProcess | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { } ~CommandObjectRenderScriptRuntimeKernelBreakpointAll() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc != 1) { result.AppendErrorWithFormat("'%s' takes 1 argument of 'enable' or 'disable'", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } RenderScriptRuntime *runtime = static_cast(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); bool do_break = false; const char* argument = command.GetArgumentAtIndex(0); if (strcmp(argument, "enable") == 0) { do_break = true; result.AppendMessage("Breakpoints will be set on all kernels."); } else if (strcmp(argument, "disable") == 0) { do_break = false; result.AppendMessage("Breakpoints will not be set on any new kernels."); } else { result.AppendErrorWithFormat("Argument must be either 'enable' or 'disable'"); result.SetStatus(eReturnStatusFailed); return false; } runtime->SetBreakAllKernels(do_break, m_exe_ctx.GetTargetSP()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } }; class CommandObjectRenderScriptRuntimeKernelBreakpoint : public CommandObjectMultiword { public: CommandObjectRenderScriptRuntimeKernelBreakpoint(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "renderscript kernel", "Commands that generate breakpoints on renderscript kernels.", nullptr) { LoadSubCommand("set", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpointSet(interpreter))); LoadSubCommand("all", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpointAll(interpreter))); } ~CommandObjectRenderScriptRuntimeKernelBreakpoint() override = default; }; class CommandObjectRenderScriptRuntimeKernel : public CommandObjectMultiword { public: CommandObjectRenderScriptRuntimeKernel(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "renderscript kernel", "Commands that deal with renderscript kernels.", NULL) { LoadSubCommand("list", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelList(interpreter))); LoadSubCommand("breakpoint", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpoint(interpreter))); } ~CommandObjectRenderScriptRuntimeKernel() override = default; }; class CommandObjectRenderScriptRuntimeContextDump : public CommandObjectParsed { public: CommandObjectRenderScriptRuntimeContextDump(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "renderscript context dump", "Dumps renderscript context information.", "renderscript context dump", eCommandRequiresProcess | eCommandProcessMustBeLaunched) { } ~CommandObjectRenderScriptRuntimeContextDump() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { RenderScriptRuntime *runtime = (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); runtime->DumpContexts(result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } }; class CommandObjectRenderScriptRuntimeContext : public CommandObjectMultiword { public: CommandObjectRenderScriptRuntimeContext(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "renderscript context", "Commands that deal with renderscript contexts.", NULL) { LoadSubCommand("dump", CommandObjectSP(new CommandObjectRenderScriptRuntimeContextDump(interpreter))); } ~CommandObjectRenderScriptRuntimeContext() override = default; }; class CommandObjectRenderScriptRuntimeAllocationDump : public CommandObjectParsed { public: CommandObjectRenderScriptRuntimeAllocationDump(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "renderscript allocation dump", "Displays the contents of a particular allocation", "renderscript allocation dump ", eCommandRequiresProcess | eCommandProcessMustBeLaunched), m_options(interpreter) { } ~CommandObjectRenderScriptRuntimeAllocationDump() override = default; Options* GetOptions() override { return &m_options; } class CommandOptions : public Options { public: CommandOptions(CommandInterpreter &interpreter) : Options(interpreter) { } ~CommandOptions() override = default; Error SetOptionValue(uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'f': m_outfile.SetFile(option_arg, true); if (m_outfile.Exists()) { m_outfile.Clear(); error.SetErrorStringWithFormat("file already exists: '%s'", option_arg); } break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting() override { m_outfile.Clear(); } const OptionDefinition* GetDefinitions() override { return g_option_table; } static OptionDefinition g_option_table[]; FileSpec m_outfile; }; bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc < 1) { result.AppendErrorWithFormat("'%s' takes 1 argument, an allocation ID. As well as an optional -f argument", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } RenderScriptRuntime *runtime = static_cast(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); const char* id_cstr = command.GetArgumentAtIndex(0); bool convert_complete = false; const uint32_t id = StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &convert_complete); if (!convert_complete) { result.AppendErrorWithFormat("invalid allocation id argument '%s'", id_cstr); result.SetStatus(eReturnStatusFailed); return false; } Stream* output_strm = nullptr; StreamFile outfile_stream; const FileSpec &outfile_spec = m_options.m_outfile; // Dump allocation to file instead if (outfile_spec) { // Open output file char path[256]; outfile_spec.GetPath(path, sizeof(path)); if (outfile_stream.GetFile().Open(path, File::eOpenOptionWrite | File::eOpenOptionCanCreate).Success()) { output_strm = &outfile_stream; result.GetOutputStream().Printf("Results written to '%s'", path); result.GetOutputStream().EOL(); } else { result.AppendErrorWithFormat("Couldn't open file '%s'", path); result.SetStatus(eReturnStatusFailed); return false; } } else output_strm = &result.GetOutputStream(); assert(output_strm != nullptr); bool success = runtime->DumpAllocation(*output_strm, m_exe_ctx.GetFramePtr(), id); if (success) result.SetStatus(eReturnStatusSuccessFinishResult); else result.SetStatus(eReturnStatusFailed); return true; } private: CommandOptions m_options; }; OptionDefinition CommandObjectRenderScriptRuntimeAllocationDump::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFilename, "Print results to specified file instead of command line."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; class CommandObjectRenderScriptRuntimeAllocationList : public CommandObjectParsed { public: CommandObjectRenderScriptRuntimeAllocationList(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "renderscript allocation list", "List renderscript allocations and their information.", "renderscript allocation list", eCommandRequiresProcess | eCommandProcessMustBeLaunched), m_options(interpreter) { } ~CommandObjectRenderScriptRuntimeAllocationList() override = default; Options* GetOptions() override { return &m_options; } class CommandOptions : public Options { public: CommandOptions(CommandInterpreter &interpreter) : Options(interpreter), m_refresh(false) { } ~CommandOptions() override = default; Error SetOptionValue(uint32_t option_idx, const char *option_arg) override { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'r': m_refresh = true; break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting() override { m_refresh = false; } const OptionDefinition* GetDefinitions() override { return g_option_table; } static OptionDefinition g_option_table[]; bool m_refresh; }; bool DoExecute(Args &command, CommandReturnObject &result) override { RenderScriptRuntime *runtime = static_cast(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); runtime->ListAllocations(result.GetOutputStream(), m_exe_ctx.GetFramePtr(), m_options.m_refresh); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } private: CommandOptions m_options; }; OptionDefinition CommandObjectRenderScriptRuntimeAllocationList::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_1, false, "refresh", 'r', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Recompute allocation details."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; class CommandObjectRenderScriptRuntimeAllocationLoad : public CommandObjectParsed { public: CommandObjectRenderScriptRuntimeAllocationLoad(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "renderscript allocation load", "Loads renderscript allocation contents from a file.", "renderscript allocation load ", eCommandRequiresProcess | eCommandProcessMustBeLaunched) { } ~CommandObjectRenderScriptRuntimeAllocationLoad() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc != 2) { result.AppendErrorWithFormat("'%s' takes 2 arguments, an allocation ID and filename to read from.", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } RenderScriptRuntime *runtime = static_cast(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); const char* id_cstr = command.GetArgumentAtIndex(0); bool convert_complete = false; const uint32_t id = StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &convert_complete); if (!convert_complete) { result.AppendErrorWithFormat ("invalid allocation id argument '%s'", id_cstr); result.SetStatus (eReturnStatusFailed); return false; } const char* filename = command.GetArgumentAtIndex(1); bool success = runtime->LoadAllocation(result.GetOutputStream(), id, filename, m_exe_ctx.GetFramePtr()); if (success) result.SetStatus(eReturnStatusSuccessFinishResult); else result.SetStatus(eReturnStatusFailed); return true; } }; class CommandObjectRenderScriptRuntimeAllocationSave : public CommandObjectParsed { public: CommandObjectRenderScriptRuntimeAllocationSave(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "renderscript allocation save", "Write renderscript allocation contents to a file.", "renderscript allocation save ", eCommandRequiresProcess | eCommandProcessMustBeLaunched) { } ~CommandObjectRenderScriptRuntimeAllocationSave() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc != 2) { result.AppendErrorWithFormat("'%s' takes 2 arguments, an allocation ID and filename to read from.", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } RenderScriptRuntime *runtime = static_cast(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); const char* id_cstr = command.GetArgumentAtIndex(0); bool convert_complete = false; const uint32_t id = StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &convert_complete); if (!convert_complete) { result.AppendErrorWithFormat ("invalid allocation id argument '%s'", id_cstr); result.SetStatus (eReturnStatusFailed); return false; } const char* filename = command.GetArgumentAtIndex(1); bool success = runtime->SaveAllocation(result.GetOutputStream(), id, filename, m_exe_ctx.GetFramePtr()); if (success) result.SetStatus(eReturnStatusSuccessFinishResult); else result.SetStatus(eReturnStatusFailed); return true; } }; class CommandObjectRenderScriptRuntimeAllocation : public CommandObjectMultiword { public: CommandObjectRenderScriptRuntimeAllocation(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "renderscript allocation", "Commands that deal with renderscript allocations.", NULL) { LoadSubCommand("list", CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocationList(interpreter))); LoadSubCommand("dump", CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocationDump(interpreter))); LoadSubCommand("save", CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocationSave(interpreter))); LoadSubCommand("load", CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocationLoad(interpreter))); } ~CommandObjectRenderScriptRuntimeAllocation() override = default; }; class CommandObjectRenderScriptRuntimeStatus : public CommandObjectParsed { public: CommandObjectRenderScriptRuntimeStatus(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "renderscript status", "Displays current renderscript runtime status.", "renderscript status", eCommandRequiresProcess | eCommandProcessMustBeLaunched) { } ~CommandObjectRenderScriptRuntimeStatus() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { RenderScriptRuntime *runtime = (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); runtime->Status(result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } }; class CommandObjectRenderScriptRuntime : public CommandObjectMultiword { public: CommandObjectRenderScriptRuntime(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "renderscript", "A set of commands for operating on renderscript.", "renderscript []") { LoadSubCommand("module", CommandObjectSP(new CommandObjectRenderScriptRuntimeModule(interpreter))); LoadSubCommand("status", CommandObjectSP(new CommandObjectRenderScriptRuntimeStatus(interpreter))); LoadSubCommand("kernel", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernel(interpreter))); LoadSubCommand("context", CommandObjectSP(new CommandObjectRenderScriptRuntimeContext(interpreter))); LoadSubCommand("allocation", CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocation(interpreter))); } ~CommandObjectRenderScriptRuntime() override = default; }; void RenderScriptRuntime::Initiate() { assert(!m_initiated); } RenderScriptRuntime::RenderScriptRuntime(Process *process) : lldb_private::CPPLanguageRuntime(process), m_initiated(false), m_debuggerPresentFlagged(false), m_breakAllKernels(false) { ModulesDidLoad(process->GetTarget().GetImages()); } lldb::CommandObjectSP RenderScriptRuntime::GetCommandObject(lldb_private::CommandInterpreter& interpreter) { static CommandObjectSP command_object; if(!command_object) { command_object.reset(new CommandObjectRenderScriptRuntime(interpreter)); } return command_object; } RenderScriptRuntime::~RenderScriptRuntime() = default; Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp (revision 293283) @@ -1,3316 +1,3316 @@ //===-- ObjectFileELF.cpp ------------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ObjectFileELF.h" #include #include #include #include "lldb/Core/ArchSpec.h" #include "lldb/Core/DataBuffer.h" #include "lldb/Core/Error.h" #include "lldb/Core/FileSpecList.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Core/Stream.h" #include "lldb/Core/Timer.h" #include "lldb/Symbol/DWARFCallFrameInfo.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/MathExtras.h" #define CASE_AND_STREAM(s, def, width) \ case def: s->Printf("%-*s", width, #def); break; using namespace lldb; using namespace lldb_private; using namespace elf; using namespace llvm::ELF; namespace { // ELF note owner definitions const char *const LLDB_NT_OWNER_FREEBSD = "FreeBSD"; const char *const LLDB_NT_OWNER_GNU = "GNU"; const char *const LLDB_NT_OWNER_NETBSD = "NetBSD"; const char *const LLDB_NT_OWNER_CSR = "csr"; const char *const LLDB_NT_OWNER_ANDROID = "Android"; const char *const LLDB_NT_OWNER_CORE = "CORE"; const char *const LLDB_NT_OWNER_LINUX = "LINUX"; // ELF note type definitions const elf_word LLDB_NT_FREEBSD_ABI_TAG = 0x01; const elf_word LLDB_NT_FREEBSD_ABI_SIZE = 4; const elf_word LLDB_NT_GNU_ABI_TAG = 0x01; const elf_word LLDB_NT_GNU_ABI_SIZE = 16; const elf_word LLDB_NT_GNU_BUILD_ID_TAG = 0x03; const elf_word LLDB_NT_NETBSD_ABI_TAG = 0x01; const elf_word LLDB_NT_NETBSD_ABI_SIZE = 4; // GNU ABI note OS constants const elf_word LLDB_NT_GNU_ABI_OS_LINUX = 0x00; const elf_word LLDB_NT_GNU_ABI_OS_HURD = 0x01; const elf_word LLDB_NT_GNU_ABI_OS_SOLARIS = 0x02; // LLDB_NT_OWNER_CORE and LLDB_NT_OWNER_LINUX note contants #define NT_PRSTATUS 1 #define NT_PRFPREG 2 #define NT_PRPSINFO 3 #define NT_TASKSTRUCT 4 #define NT_AUXV 6 #define NT_SIGINFO 0x53494749 #define NT_FILE 0x46494c45 #define NT_PRXFPREG 0x46e62b7f #define NT_PPC_VMX 0x100 #define NT_PPC_SPE 0x101 #define NT_PPC_VSX 0x102 #define NT_386_TLS 0x200 #define NT_386_IOPERM 0x201 #define NT_X86_XSTATE 0x202 #define NT_S390_HIGH_GPRS 0x300 #define NT_S390_TIMER 0x301 #define NT_S390_TODCMP 0x302 #define NT_S390_TODPREG 0x303 #define NT_S390_CTRS 0x304 #define NT_S390_PREFIX 0x305 #define NT_S390_LAST_BREAK 0x306 #define NT_S390_SYSTEM_CALL 0x307 #define NT_S390_TDB 0x308 #define NT_S390_VXRS_LOW 0x309 #define NT_S390_VXRS_HIGH 0x30a #define NT_ARM_VFP 0x400 #define NT_ARM_TLS 0x401 #define NT_ARM_HW_BREAK 0x402 #define NT_ARM_HW_WATCH 0x403 #define NT_ARM_SYSTEM_CALL 0x404 #define NT_METAG_CBUF 0x500 #define NT_METAG_RPIPE 0x501 #define NT_METAG_TLS 0x502 //===----------------------------------------------------------------------===// /// @class ELFRelocation /// @brief Generic wrapper for ELFRel and ELFRela. /// /// This helper class allows us to parse both ELFRel and ELFRela relocation /// entries in a generic manner. class ELFRelocation { public: /// Constructs an ELFRelocation entry with a personality as given by @p /// type. /// /// @param type Either DT_REL or DT_RELA. Any other value is invalid. ELFRelocation(unsigned type); - + ~ELFRelocation(); bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); static unsigned RelocType32(const ELFRelocation &rel); static unsigned RelocType64(const ELFRelocation &rel); static unsigned RelocSymbol32(const ELFRelocation &rel); static unsigned RelocSymbol64(const ELFRelocation &rel); static unsigned RelocOffset32(const ELFRelocation &rel); static unsigned RelocOffset64(const ELFRelocation &rel); static unsigned RelocAddend32(const ELFRelocation &rel); static unsigned RelocAddend64(const ELFRelocation &rel); private: typedef llvm::PointerUnion RelocUnion; RelocUnion reloc; }; ELFRelocation::ELFRelocation(unsigned type) -{ +{ if (type == DT_REL || type == SHT_REL) reloc = new ELFRel(); else if (type == DT_RELA || type == SHT_RELA) reloc = new ELFRela(); else { assert(false && "unexpected relocation type"); reloc = static_cast(NULL); } } ELFRelocation::~ELFRelocation() { if (reloc.is()) delete reloc.get(); else - delete reloc.get(); + delete reloc.get(); } bool ELFRelocation::Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset) { if (reloc.is()) return reloc.get()->Parse(data, offset); else return reloc.get()->Parse(data, offset); } unsigned ELFRelocation::RelocType32(const ELFRelocation &rel) { if (rel.reloc.is()) return ELFRel::RelocType32(*rel.reloc.get()); else return ELFRela::RelocType32(*rel.reloc.get()); } unsigned ELFRelocation::RelocType64(const ELFRelocation &rel) { if (rel.reloc.is()) return ELFRel::RelocType64(*rel.reloc.get()); else return ELFRela::RelocType64(*rel.reloc.get()); } unsigned ELFRelocation::RelocSymbol32(const ELFRelocation &rel) { if (rel.reloc.is()) return ELFRel::RelocSymbol32(*rel.reloc.get()); else return ELFRela::RelocSymbol32(*rel.reloc.get()); } unsigned ELFRelocation::RelocSymbol64(const ELFRelocation &rel) { if (rel.reloc.is()) return ELFRel::RelocSymbol64(*rel.reloc.get()); else return ELFRela::RelocSymbol64(*rel.reloc.get()); } unsigned ELFRelocation::RelocOffset32(const ELFRelocation &rel) { if (rel.reloc.is()) return rel.reloc.get()->r_offset; else return rel.reloc.get()->r_offset; } unsigned ELFRelocation::RelocOffset64(const ELFRelocation &rel) { if (rel.reloc.is()) return rel.reloc.get()->r_offset; else return rel.reloc.get()->r_offset; } unsigned ELFRelocation::RelocAddend32(const ELFRelocation &rel) { if (rel.reloc.is()) return 0; else return rel.reloc.get()->r_addend; } unsigned ELFRelocation::RelocAddend64(const ELFRelocation &rel) { if (rel.reloc.is()) return 0; else return rel.reloc.get()->r_addend; } } // end anonymous namespace bool ELFNote::Parse(const DataExtractor &data, lldb::offset_t *offset) { // Read all fields. if (data.GetU32(offset, &n_namesz, 3) == NULL) return false; // The name field is required to be nul-terminated, and n_namesz // includes the terminating nul in observed implementations (contrary // to the ELF-64 spec). A special case is needed for cores generated // by some older Linux versions, which write a note named "CORE" // without a nul terminator and n_namesz = 4. if (n_namesz == 4) { char buf[4]; if (data.ExtractBytes (*offset, 4, data.GetByteOrder(), buf) != 4) return false; if (strncmp (buf, "CORE", 4) == 0) { n_name = "CORE"; *offset += 4; return true; } } const char *cstr = data.GetCStr(offset, llvm::RoundUpToAlignment (n_namesz, 4)); if (cstr == NULL) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYMBOLS)); if (log) log->Printf("Failed to parse note name lacking nul terminator"); return false; } n_name = cstr; return true; } static uint32_t kalimbaVariantFromElfFlags(const elf::elf_word e_flags) { const uint32_t dsp_rev = e_flags & 0xFF; uint32_t kal_arch_variant = LLDB_INVALID_CPUTYPE; switch(dsp_rev) { // TODO(mg11) Support more variants case 10: kal_arch_variant = llvm::Triple::KalimbaSubArch_v3; break; case 14: kal_arch_variant = llvm::Triple::KalimbaSubArch_v4; break; case 17: case 20: kal_arch_variant = llvm::Triple::KalimbaSubArch_v5; break; default: - break; + break; } return kal_arch_variant; } static uint32_t mipsVariantFromElfFlags(const elf::elf_word e_flags, uint32_t endian) { const uint32_t mips_arch = e_flags & llvm::ELF::EF_MIPS_ARCH; uint32_t arch_variant = ArchSpec::eMIPSSubType_unknown; switch (mips_arch) { case llvm::ELF::EF_MIPS_ARCH_1: case llvm::ELF::EF_MIPS_ARCH_2: case llvm::ELF::EF_MIPS_ARCH_3: case llvm::ELF::EF_MIPS_ARCH_4: case llvm::ELF::EF_MIPS_ARCH_5: case llvm::ELF::EF_MIPS_ARCH_32: return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32el : ArchSpec::eMIPSSubType_mips32; case llvm::ELF::EF_MIPS_ARCH_32R2: return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32r2el : ArchSpec::eMIPSSubType_mips32r2; case llvm::ELF::EF_MIPS_ARCH_32R6: return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32r6el : ArchSpec::eMIPSSubType_mips32r6; case llvm::ELF::EF_MIPS_ARCH_64: return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64el : ArchSpec::eMIPSSubType_mips64; case llvm::ELF::EF_MIPS_ARCH_64R2: return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64r2el : ArchSpec::eMIPSSubType_mips64r2; case llvm::ELF::EF_MIPS_ARCH_64R6: return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64r6el : ArchSpec::eMIPSSubType_mips64r6; default: break; } return arch_variant; } static uint32_t subTypeFromElfHeader(const elf::ELFHeader& header) { if (header.e_machine == llvm::ELF::EM_MIPS) return mipsVariantFromElfFlags (header.e_flags, header.e_ident[EI_DATA]); return llvm::ELF::EM_CSR_KALIMBA == header.e_machine ? kalimbaVariantFromElfFlags(header.e_flags) : LLDB_INVALID_CPUTYPE; } //! The kalimba toolchain identifies a code section as being //! one with the SHT_PROGBITS set in the section sh_type and the top //! bit in the 32-bit address field set. static lldb::SectionType kalimbaSectionType( const elf::ELFHeader& header, const elf::ELFSectionHeader& sect_hdr) { if (llvm::ELF::EM_CSR_KALIMBA != header.e_machine) { return eSectionTypeOther; } if (llvm::ELF::SHT_NOBITS == sect_hdr.sh_type) { return eSectionTypeZeroFill; } if (llvm::ELF::SHT_PROGBITS == sect_hdr.sh_type) { const lldb::addr_t KAL_CODE_BIT = 1 << 31; return KAL_CODE_BIT & sect_hdr.sh_addr ? eSectionTypeCode : eSectionTypeData; } return eSectionTypeOther; } // Arbitrary constant used as UUID prefix for core files. const uint32_t ObjectFileELF::g_core_uuid_magic(0xE210C); //------------------------------------------------------------------ // Static methods. //------------------------------------------------------------------ void ObjectFileELF::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, CreateMemoryInstance, GetModuleSpecifications); } void ObjectFileELF::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } lldb_private::ConstString ObjectFileELF::GetPluginNameStatic() { static ConstString g_name("elf"); return g_name; } const char * ObjectFileELF::GetPluginDescriptionStatic() { return "ELF object file reader."; } ObjectFile * ObjectFileELF::CreateInstance (const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec* file, lldb::offset_t file_offset, lldb::offset_t length) { if (!data_sp) { data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); data_offset = 0; } if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT + data_offset)) { const uint8_t *magic = data_sp->GetBytes() + data_offset; if (ELFHeader::MagicBytesMatch(magic)) { // Update the data to contain the entire file if it doesn't already if (data_sp->GetByteSize() < length) { data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); data_offset = 0; magic = data_sp->GetBytes(); } unsigned address_size = ELFHeader::AddressSizeInBytes(magic); if (address_size == 4 || address_size == 8) { std::unique_ptr objfile_ap(new ObjectFileELF(module_sp, data_sp, data_offset, file, file_offset, length)); ArchSpec spec; if (objfile_ap->GetArchitecture(spec) && objfile_ap->SetModulesArchitecture(spec)) return objfile_ap.release(); } } } return NULL; } ObjectFile* -ObjectFileELF::CreateMemoryInstance (const lldb::ModuleSP &module_sp, - DataBufferSP& data_sp, - const lldb::ProcessSP &process_sp, +ObjectFileELF::CreateMemoryInstance (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT)) { const uint8_t *magic = data_sp->GetBytes(); if (ELFHeader::MagicBytesMatch(magic)) { unsigned address_size = ELFHeader::AddressSizeInBytes(magic); if (address_size == 4 || address_size == 8) { std::auto_ptr objfile_ap(new ObjectFileELF(module_sp, data_sp, process_sp, header_addr)); ArchSpec spec; if (objfile_ap->GetArchitecture(spec) && objfile_ap->SetModulesArchitecture(spec)) return objfile_ap.release(); } } } return NULL; } bool ObjectFileELF::MagicBytesMatch (DataBufferSP& data_sp, lldb::addr_t data_offset, lldb::addr_t data_length) { if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT + data_offset)) { const uint8_t *magic = data_sp->GetBytes() + data_offset; return ELFHeader::MagicBytesMatch(magic); } return false; } /* * crc function from http://svnweb.freebsd.org/base/head/sys/libkern/crc32.c * * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or * code or tables extracted from it, as desired without restriction. */ static uint32_t calc_crc32(uint32_t crc, const void *buf, size_t size) { static const uint32_t g_crc32_tab[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d - }; + }; const uint8_t *p = (const uint8_t *)buf; crc = crc ^ ~0U; while (size--) crc = g_crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); return crc ^ ~0U; } static uint32_t calc_gnu_debuglink_crc32(const void *buf, size_t size) { return calc_crc32(0U, buf, size); } uint32_t ObjectFileELF::CalculateELFNotesSegmentsCRC32 (const ProgramHeaderColl& program_headers, DataExtractor& object_data) { typedef ProgramHeaderCollConstIter Iter; uint32_t core_notes_crc = 0; for (Iter I = program_headers.begin(); I != program_headers.end(); ++I) { if (I->p_type == llvm::ELF::PT_NOTE) { const elf_off ph_offset = I->p_offset; const size_t ph_size = I->p_filesz; DataExtractor segment_data; if (segment_data.SetData(object_data, ph_offset, ph_size) != ph_size) { // The ELF program header contained incorrect data, // probably corefile is incomplete or corrupted. break; } core_notes_crc = calc_crc32(core_notes_crc, segment_data.GetDataStart(), segment_data.GetByteSize()); } } return core_notes_crc; } static const char* OSABIAsCString (unsigned char osabi_byte) { #define _MAKE_OSABI_CASE(x) case x: return #x switch (osabi_byte) { _MAKE_OSABI_CASE(ELFOSABI_NONE); _MAKE_OSABI_CASE(ELFOSABI_HPUX); _MAKE_OSABI_CASE(ELFOSABI_NETBSD); _MAKE_OSABI_CASE(ELFOSABI_GNU); _MAKE_OSABI_CASE(ELFOSABI_HURD); _MAKE_OSABI_CASE(ELFOSABI_SOLARIS); _MAKE_OSABI_CASE(ELFOSABI_AIX); _MAKE_OSABI_CASE(ELFOSABI_IRIX); _MAKE_OSABI_CASE(ELFOSABI_FREEBSD); _MAKE_OSABI_CASE(ELFOSABI_TRU64); _MAKE_OSABI_CASE(ELFOSABI_MODESTO); _MAKE_OSABI_CASE(ELFOSABI_OPENBSD); _MAKE_OSABI_CASE(ELFOSABI_OPENVMS); _MAKE_OSABI_CASE(ELFOSABI_NSK); _MAKE_OSABI_CASE(ELFOSABI_AROS); _MAKE_OSABI_CASE(ELFOSABI_FENIXOS); _MAKE_OSABI_CASE(ELFOSABI_C6000_ELFABI); _MAKE_OSABI_CASE(ELFOSABI_C6000_LINUX); _MAKE_OSABI_CASE(ELFOSABI_ARM); _MAKE_OSABI_CASE(ELFOSABI_STANDALONE); default: return ""; } #undef _MAKE_OSABI_CASE } // // WARNING : This function is being deprecated // It's functionality has moved to ArchSpec::SetArchitecture // This function is only being kept to validate the move. // // TODO : Remove this function static bool GetOsFromOSABI (unsigned char osabi_byte, llvm::Triple::OSType &ostype) { switch (osabi_byte) { case ELFOSABI_AIX: ostype = llvm::Triple::OSType::AIX; break; case ELFOSABI_FREEBSD: ostype = llvm::Triple::OSType::FreeBSD; break; case ELFOSABI_GNU: ostype = llvm::Triple::OSType::Linux; break; case ELFOSABI_NETBSD: ostype = llvm::Triple::OSType::NetBSD; break; case ELFOSABI_OPENBSD: ostype = llvm::Triple::OSType::OpenBSD; break; case ELFOSABI_SOLARIS: ostype = llvm::Triple::OSType::Solaris; break; default: ostype = llvm::Triple::OSType::UnknownOS; } return ostype != llvm::Triple::OSType::UnknownOS; } size_t ObjectFileELF::GetModuleSpecifications (const lldb_private::FileSpec& file, lldb::DataBufferSP& data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MODULES)); const size_t initial_count = specs.GetSize(); if (ObjectFileELF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) { DataExtractor data; data.SetData(data_sp); elf::ELFHeader header; if (header.Parse(data, &data_offset)) { if (data_sp) { ModuleSpec spec (file); const uint32_t sub_type = subTypeFromElfHeader(header); spec.GetArchitecture().SetArchitecture(eArchTypeELF, header.e_machine, sub_type, header.e_ident[EI_OSABI]); if (spec.GetArchitecture().IsValid()) { llvm::Triple::OSType ostype; llvm::Triple::VendorType vendor; llvm::Triple::OSType spec_ostype = spec.GetArchitecture ().GetTriple ().getOS (); if (log) log->Printf ("ObjectFileELF::%s file '%s' module OSABI: %s", __FUNCTION__, file.GetPath ().c_str (), OSABIAsCString (header.e_ident[EI_OSABI])); // SetArchitecture should have set the vendor to unknown vendor = spec.GetArchitecture ().GetTriple ().getVendor (); assert(vendor == llvm::Triple::UnknownVendor); // // Validate it is ok to remove GetOsFromOSABI GetOsFromOSABI (header.e_ident[EI_OSABI], ostype); assert(spec_ostype == ostype); if (spec_ostype != llvm::Triple::OSType::UnknownOS) { if (log) log->Printf ("ObjectFileELF::%s file '%s' set ELF module OS type from ELF header OSABI.", __FUNCTION__, file.GetPath ().c_str ()); } // Try to get the UUID from the section list. Usually that's at the end, so // map the file in if we don't have it already. size_t section_header_end = header.e_shoff + header.e_shnum * header.e_shentsize; if (section_header_end > data_sp->GetByteSize()) { data_sp = file.MemoryMapFileContentsIfLocal (file_offset, section_header_end); data.SetData(data_sp); } uint32_t gnu_debuglink_crc = 0; std::string gnu_debuglink_file; SectionHeaderColl section_headers; lldb_private::UUID &uuid = spec.GetUUID(); GetSectionHeaderInfo(section_headers, data, header, uuid, gnu_debuglink_file, gnu_debuglink_crc, spec.GetArchitecture ()); llvm::Triple &spec_triple = spec.GetArchitecture ().GetTriple (); if (log) log->Printf ("ObjectFileELF::%s file '%s' module set to triple: %s (architecture %s)", __FUNCTION__, file.GetPath ().c_str (), spec_triple.getTriple ().c_str (), spec.GetArchitecture ().GetArchitectureName ()); if (!uuid.IsValid()) { uint32_t core_notes_crc = 0; if (!gnu_debuglink_crc) { lldb_private::Timer scoped_timer (__PRETTY_FUNCTION__, "Calculating module crc32 %s with size %" PRIu64 " KiB", file.GetLastPathComponent().AsCString(), (file.GetByteSize()-file_offset)/1024); // For core files - which usually don't happen to have a gnu_debuglink, // and are pretty bulky - calculating whole contents crc32 would be too much of luxury. // Thus we will need to fallback to something simpler. if (header.e_type == llvm::ELF::ET_CORE) { size_t program_headers_end = header.e_phoff + header.e_phnum * header.e_phentsize; if (program_headers_end > data_sp->GetByteSize()) { data_sp = file.MemoryMapFileContentsIfLocal(file_offset, program_headers_end); data.SetData(data_sp); } ProgramHeaderColl program_headers; GetProgramHeaderInfo(program_headers, data, header); size_t segment_data_end = 0; for (ProgramHeaderCollConstIter I = program_headers.begin(); I != program_headers.end(); ++I) { segment_data_end = std::max (I->p_offset + I->p_filesz, segment_data_end); } if (segment_data_end > data_sp->GetByteSize()) { data_sp = file.MemoryMapFileContentsIfLocal(file_offset, segment_data_end); data.SetData(data_sp); } core_notes_crc = CalculateELFNotesSegmentsCRC32 (program_headers, data); } else { // Need to map entire file into memory to calculate the crc. data_sp = file.MemoryMapFileContentsIfLocal (file_offset, SIZE_MAX); data.SetData(data_sp); gnu_debuglink_crc = calc_gnu_debuglink_crc32 (data.GetDataStart(), data.GetByteSize()); } } if (gnu_debuglink_crc) { // Use 4 bytes of crc from the .gnu_debuglink section. uint32_t uuidt[4] = { gnu_debuglink_crc, 0, 0, 0 }; uuid.SetBytes (uuidt, sizeof(uuidt)); } else if (core_notes_crc) { // Use 8 bytes - first 4 bytes for *magic* prefix, mainly to make it look different form // .gnu_debuglink crc followed by 4 bytes of note segments crc. uint32_t uuidt[4] = { g_core_uuid_magic, core_notes_crc, 0, 0 }; uuid.SetBytes (uuidt, sizeof(uuidt)); } } specs.Append(spec); } } } } return specs.GetSize() - initial_count; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ lldb_private::ConstString ObjectFileELF::GetPluginName() { return GetPluginNameStatic(); } uint32_t ObjectFileELF::GetPluginVersion() { return m_plugin_version; } //------------------------------------------------------------------ // ObjectFile protocol //------------------------------------------------------------------ -ObjectFileELF::ObjectFileELF (const lldb::ModuleSP &module_sp, +ObjectFileELF::ObjectFileELF (const lldb::ModuleSP &module_sp, DataBufferSP& data_sp, lldb::offset_t data_offset, - const FileSpec* file, + const FileSpec* file, lldb::offset_t file_offset, - lldb::offset_t length) : + lldb::offset_t length) : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset), m_header(), m_uuid(), m_gnu_debuglink_file(), m_gnu_debuglink_crc(0), m_program_headers(), m_section_headers(), m_dynamic_symbols(), m_filespec_ap(), m_entry_point_address(), m_arch_spec() { if (file) m_file = *file; ::memset(&m_header, 0, sizeof(m_header)); } ObjectFileELF::ObjectFileELF (const lldb::ModuleSP &module_sp, DataBufferSP& header_data_sp, const lldb::ProcessSP &process_sp, addr_t header_addr) : ObjectFile(module_sp, process_sp, header_addr, header_data_sp), m_header(), m_uuid(), m_gnu_debuglink_file(), m_gnu_debuglink_crc(0), m_program_headers(), m_section_headers(), m_dynamic_symbols(), m_filespec_ap(), m_entry_point_address(), m_arch_spec() { ::memset(&m_header, 0, sizeof(m_header)); } ObjectFileELF::~ObjectFileELF() { } bool ObjectFileELF::IsExecutable() const { return ((m_header.e_type & ET_EXEC) != 0) || (m_header.e_entry != 0); } bool ObjectFileELF::SetLoadAddress (Target &target, lldb::addr_t value, bool value_is_offset) { ModuleSP module_sp = GetModule(); if (module_sp) { size_t num_loaded_sections = 0; SectionList *section_list = GetSectionList (); if (section_list) { if (!value_is_offset) { bool found_offset = false; for (size_t i = 0, count = GetProgramHeaderCount(); i < count; ++i) { const elf::ELFProgramHeader* header = GetProgramHeaderByIndex(i); if (header == nullptr) continue; if (header->p_type != PT_LOAD || header->p_offset != 0) continue; - + value = value - header->p_vaddr; found_offset = true; break; } if (!found_offset) return false; } const size_t num_sections = section_list->GetSize(); size_t sect_idx = 0; for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { // Iterate through the object file sections to find all // of the sections that have SHF_ALLOC in their flag bits. SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); // if (section_sp && !section_sp->IsThreadSpecific()) if (section_sp && section_sp->Test(SHF_ALLOC)) { lldb::addr_t load_addr = section_sp->GetFileAddress() + value; // On 32-bit systems the load address have to fit into 4 bytes. The rest of // the bytes are the overflow from the addition. if (GetAddressByteSize() == 4) load_addr &= 0xFFFFFFFF; if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, load_addr)) ++num_loaded_sections; } } return num_loaded_sections > 0; } } return false; } ByteOrder ObjectFileELF::GetByteOrder() const { if (m_header.e_ident[EI_DATA] == ELFDATA2MSB) return eByteOrderBig; if (m_header.e_ident[EI_DATA] == ELFDATA2LSB) return eByteOrderLittle; return eByteOrderInvalid; } uint32_t ObjectFileELF::GetAddressByteSize() const { return m_data.GetAddressByteSize(); } // Top 16 bits of the `Symbol` flags are available. #define ARM_ELF_SYM_IS_THUMB (1 << 16) AddressClass ObjectFileELF::GetAddressClass (addr_t file_addr) { Symtab* symtab = GetSymtab(); if (!symtab) return eAddressClassUnknown; // The address class is determined based on the symtab. Ask it from the object file what // contains the symtab information. ObjectFile* symtab_objfile = symtab->GetObjectFile(); if (symtab_objfile != nullptr && symtab_objfile != this) return symtab_objfile->GetAddressClass(file_addr); auto res = ObjectFile::GetAddressClass (file_addr); if (res != eAddressClassCode) return res; auto ub = m_address_class_map.upper_bound(file_addr); if (ub == m_address_class_map.begin()) { // No entry in the address class map before the address. Return // default address class for an address in a code section. return eAddressClassCode; } // Move iterator to the address class entry preceding address --ub; return ub->second; } size_t ObjectFileELF::SectionIndex(const SectionHeaderCollIter &I) { return std::distance(m_section_headers.begin(), I) + 1u; } size_t ObjectFileELF::SectionIndex(const SectionHeaderCollConstIter &I) const { return std::distance(m_section_headers.begin(), I) + 1u; } bool ObjectFileELF::ParseHeader() { lldb::offset_t offset = 0; if (!m_header.Parse(m_data, &offset)) return false; if (!IsInMemory()) return true; // For in memory object files m_data might not contain the full object file. Try to load it // until the end of the "Section header table" what is at the end of the ELF file. addr_t file_size = m_header.e_shoff + m_header.e_shnum * m_header.e_shentsize; if (m_data.GetByteSize() < file_size) { ProcessSP process_sp (m_process_wp.lock()); if (!process_sp) return false; DataBufferSP data_sp = ReadMemory(process_sp, m_memory_addr, file_size); if (!data_sp) return false; m_data.SetData(data_sp, 0, file_size); } return true; } bool ObjectFileELF::GetUUID(lldb_private::UUID* uuid) { // Need to parse the section list to get the UUIDs, so make sure that's been done. if (!ParseSectionHeaders() && GetType() != ObjectFile::eTypeCoreFile) return false; if (m_uuid.IsValid()) { // We have the full build id uuid. *uuid = m_uuid; return true; } else if (GetType() == ObjectFile::eTypeCoreFile) { uint32_t core_notes_crc = 0; if (!ParseProgramHeaders()) return false; core_notes_crc = CalculateELFNotesSegmentsCRC32(m_program_headers, m_data); if (core_notes_crc) { // Use 8 bytes - first 4 bytes for *magic* prefix, mainly to make it // look different form .gnu_debuglink crc - followed by 4 bytes of note // segments crc. uint32_t uuidt[4] = { g_core_uuid_magic, core_notes_crc, 0, 0 }; m_uuid.SetBytes (uuidt, sizeof(uuidt)); } } else { if (!m_gnu_debuglink_crc) m_gnu_debuglink_crc = calc_gnu_debuglink_crc32 (m_data.GetDataStart(), m_data.GetByteSize()); if (m_gnu_debuglink_crc) { // Use 4 bytes of crc from the .gnu_debuglink section. uint32_t uuidt[4] = { m_gnu_debuglink_crc, 0, 0, 0 }; m_uuid.SetBytes (uuidt, sizeof(uuidt)); } } if (m_uuid.IsValid()) { *uuid = m_uuid; return true; } return false; } lldb_private::FileSpecList ObjectFileELF::GetDebugSymbolFilePaths() { FileSpecList file_spec_list; if (!m_gnu_debuglink_file.empty()) { FileSpec file_spec (m_gnu_debuglink_file.c_str(), false); file_spec_list.Append (file_spec); } return file_spec_list; } uint32_t ObjectFileELF::GetDependentModules(FileSpecList &files) { size_t num_modules = ParseDependentModules(); uint32_t num_specs = 0; for (unsigned i = 0; i < num_modules; ++i) { if (files.AppendIfUnique(m_filespec_ap->GetFileSpecAtIndex(i))) num_specs++; } return num_specs; } Address ObjectFileELF::GetImageInfoAddress(Target *target) { if (!ParseDynamicSymbols()) return Address(); SectionList *section_list = GetSectionList(); if (!section_list) return Address(); // Find the SHT_DYNAMIC (.dynamic) section. SectionSP dynsym_section_sp (section_list->FindSectionByType (eSectionTypeELFDynamicLinkInfo, true)); if (!dynsym_section_sp) return Address(); assert (dynsym_section_sp->GetObjectFile() == this); user_id_t dynsym_id = dynsym_section_sp->GetID(); const ELFSectionHeaderInfo *dynsym_hdr = GetSectionHeaderByIndex(dynsym_id); if (!dynsym_hdr) return Address(); for (size_t i = 0; i < m_dynamic_symbols.size(); ++i) { ELFDynamic &symbol = m_dynamic_symbols[i]; if (symbol.d_tag == DT_DEBUG) { // Compute the offset as the number of previous entries plus the // size of d_tag. addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize(); return Address(dynsym_section_sp, offset); } // MIPS executables uses DT_MIPS_RLD_MAP_REL to support PIE. DT_MIPS_RLD_MAP exists in non-PIE. else if ((symbol.d_tag == DT_MIPS_RLD_MAP || symbol.d_tag == DT_MIPS_RLD_MAP_REL) && target) { addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize(); addr_t dyn_base = dynsym_section_sp->GetLoadBaseAddress(target); if (dyn_base == LLDB_INVALID_ADDRESS) return Address(); Error error; if (symbol.d_tag == DT_MIPS_RLD_MAP) { // DT_MIPS_RLD_MAP tag stores an absolute address of the debug pointer. Address addr; if (target->ReadPointerFromMemory(dyn_base + offset, false, error, addr)) return addr; } if (symbol.d_tag == DT_MIPS_RLD_MAP_REL) { // DT_MIPS_RLD_MAP_REL tag stores the offset to the debug pointer, relative to the address of the tag. uint64_t rel_offset; rel_offset = target->ReadUnsignedIntegerFromMemory(dyn_base + offset, false, GetAddressByteSize(), UINT64_MAX, error); if (error.Success() && rel_offset != UINT64_MAX) { Address addr; addr_t debug_ptr_address = dyn_base + (offset - GetAddressByteSize()) + rel_offset; addr.SetOffset (debug_ptr_address); return addr; } } } } return Address(); } lldb_private::Address -ObjectFileELF::GetEntryPointAddress () +ObjectFileELF::GetEntryPointAddress () { if (m_entry_point_address.IsValid()) return m_entry_point_address; if (!ParseHeader() || !IsExecutable()) return m_entry_point_address; SectionList *section_list = GetSectionList(); addr_t offset = m_header.e_entry; - if (!section_list) + if (!section_list) m_entry_point_address.SetOffset(offset); else m_entry_point_address.ResolveAddressUsingFileSections(offset, section_list); return m_entry_point_address; } //---------------------------------------------------------------------- // ParseDependentModules //---------------------------------------------------------------------- size_t ObjectFileELF::ParseDependentModules() { if (m_filespec_ap.get()) return m_filespec_ap->GetSize(); m_filespec_ap.reset(new FileSpecList()); if (!ParseSectionHeaders()) return 0; SectionList *section_list = GetSectionList(); if (!section_list) return 0; // Find the SHT_DYNAMIC section. Section *dynsym = section_list->FindSectionByType (eSectionTypeELFDynamicLinkInfo, true).get(); if (!dynsym) return 0; assert (dynsym->GetObjectFile() == this); const ELFSectionHeaderInfo *header = GetSectionHeaderByIndex (dynsym->GetID()); if (!header) return 0; // sh_link: section header index of string table used by entries in the section. Section *dynstr = section_list->FindSectionByID (header->sh_link + 1).get(); if (!dynstr) return 0; DataExtractor dynsym_data; DataExtractor dynstr_data; if (ReadSectionData(dynsym, dynsym_data) && ReadSectionData(dynstr, dynstr_data)) { ELFDynamic symbol; const lldb::offset_t section_size = dynsym_data.GetByteSize(); lldb::offset_t offset = 0; // The only type of entries we are concerned with are tagged DT_NEEDED, // yielding the name of a required library. while (offset < section_size) { if (!symbol.Parse(dynsym_data, &offset)) break; if (symbol.d_tag != DT_NEEDED) continue; uint32_t str_index = static_cast(symbol.d_val); const char *lib_name = dynstr_data.PeekCStr(str_index); m_filespec_ap->Append(FileSpec(lib_name, true)); } } return m_filespec_ap->GetSize(); } //---------------------------------------------------------------------- // GetProgramHeaderInfo //---------------------------------------------------------------------- size_t ObjectFileELF::GetProgramHeaderInfo(ProgramHeaderColl &program_headers, DataExtractor &object_data, const ELFHeader &header) { // We have already parsed the program headers if (!program_headers.empty()) return program_headers.size(); // If there are no program headers to read we are done. if (header.e_phnum == 0) return 0; program_headers.resize(header.e_phnum); if (program_headers.size() != header.e_phnum) return 0; const size_t ph_size = header.e_phnum * header.e_phentsize; const elf_off ph_offset = header.e_phoff; DataExtractor data; if (data.SetData(object_data, ph_offset, ph_size) != ph_size) return 0; uint32_t idx; lldb::offset_t offset; for (idx = 0, offset = 0; idx < header.e_phnum; ++idx) { if (program_headers[idx].Parse(data, &offset) == false) break; } if (idx < program_headers.size()) program_headers.resize(idx); return program_headers.size(); } //---------------------------------------------------------------------- // ParseProgramHeaders //---------------------------------------------------------------------- size_t ObjectFileELF::ParseProgramHeaders() { return GetProgramHeaderInfo(m_program_headers, m_data, m_header); } lldb_private::Error ObjectFileELF::RefineModuleDetailsFromNote (lldb_private::DataExtractor &data, lldb_private::ArchSpec &arch_spec, lldb_private::UUID &uuid) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MODULES)); Error error; lldb::offset_t offset = 0; while (true) { // Parse the note header. If this fails, bail out. const lldb::offset_t note_offset = offset; ELFNote note = ELFNote(); if (!note.Parse(data, &offset)) { // We're done. return error; } if (log) log->Printf ("ObjectFileELF::%s parsing note name='%s', type=%" PRIu32, __FUNCTION__, note.n_name.c_str (), note.n_type); // Process FreeBSD ELF notes. if ((note.n_name == LLDB_NT_OWNER_FREEBSD) && (note.n_type == LLDB_NT_FREEBSD_ABI_TAG) && (note.n_descsz == LLDB_NT_FREEBSD_ABI_SIZE)) { // Pull out the min version info. uint32_t version_info; if (data.GetU32 (&offset, &version_info, 1) == nullptr) { error.SetErrorString ("failed to read FreeBSD ABI note payload"); return error; } // Convert the version info into a major/minor number. const uint32_t version_major = version_info / 100000; const uint32_t version_minor = (version_info / 1000) % 100; char os_name[32]; snprintf (os_name, sizeof (os_name), "freebsd%" PRIu32 ".%" PRIu32, version_major, version_minor); // Set the elf OS version to FreeBSD. Also clear the vendor. arch_spec.GetTriple ().setOSName (os_name); arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); if (log) log->Printf ("ObjectFileELF::%s detected FreeBSD %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_major, version_minor, static_cast (version_info % 1000)); } // Process GNU ELF notes. else if (note.n_name == LLDB_NT_OWNER_GNU) { switch (note.n_type) { case LLDB_NT_GNU_ABI_TAG: if (note.n_descsz == LLDB_NT_GNU_ABI_SIZE) { // Pull out the min OS version supporting the ABI. uint32_t version_info[4]; if (data.GetU32 (&offset, &version_info[0], note.n_descsz / 4) == nullptr) { error.SetErrorString ("failed to read GNU ABI note payload"); return error; } // Set the OS per the OS field. switch (version_info[0]) { case LLDB_NT_GNU_ABI_OS_LINUX: arch_spec.GetTriple ().setOS (llvm::Triple::OSType::Linux); arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); if (log) log->Printf ("ObjectFileELF::%s detected Linux, min version %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_info[1], version_info[2], version_info[3]); // FIXME we have the minimal version number, we could be propagating that. version_info[1] = OS Major, version_info[2] = OS Minor, version_info[3] = Revision. break; case LLDB_NT_GNU_ABI_OS_HURD: arch_spec.GetTriple ().setOS (llvm::Triple::OSType::UnknownOS); arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); if (log) log->Printf ("ObjectFileELF::%s detected Hurd (unsupported), min version %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_info[1], version_info[2], version_info[3]); break; case LLDB_NT_GNU_ABI_OS_SOLARIS: arch_spec.GetTriple ().setOS (llvm::Triple::OSType::Solaris); arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); if (log) log->Printf ("ObjectFileELF::%s detected Solaris, min version %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_info[1], version_info[2], version_info[3]); break; default: if (log) log->Printf ("ObjectFileELF::%s unrecognized OS in note, id %" PRIu32 ", min version %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_info[0], version_info[1], version_info[2], version_info[3]); break; } } break; case LLDB_NT_GNU_BUILD_ID_TAG: // Only bother processing this if we don't already have the uuid set. if (!uuid.IsValid()) { // 16 bytes is UUID|MD5, 20 bytes is SHA1 if ((note.n_descsz == 16 || note.n_descsz == 20)) { uint8_t uuidbuf[20]; if (data.GetU8 (&offset, &uuidbuf, note.n_descsz) == nullptr) { error.SetErrorString ("failed to read GNU_BUILD_ID note payload"); return error; } // Save the build id as the UUID for the module. uuid.SetBytes (uuidbuf, note.n_descsz); } } break; } } // Process NetBSD ELF notes. else if ((note.n_name == LLDB_NT_OWNER_NETBSD) && (note.n_type == LLDB_NT_NETBSD_ABI_TAG) && (note.n_descsz == LLDB_NT_NETBSD_ABI_SIZE)) { // Pull out the min version info. uint32_t version_info; if (data.GetU32 (&offset, &version_info, 1) == nullptr) { error.SetErrorString ("failed to read NetBSD ABI note payload"); return error; } // Set the elf OS version to NetBSD. Also clear the vendor. arch_spec.GetTriple ().setOS (llvm::Triple::OSType::NetBSD); arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); if (log) log->Printf ("ObjectFileELF::%s detected NetBSD, min version constant %" PRIu32, __FUNCTION__, version_info); } // Process CSR kalimba notes else if ((note.n_type == LLDB_NT_GNU_ABI_TAG) && (note.n_name == LLDB_NT_OWNER_CSR)) { arch_spec.GetTriple().setOS(llvm::Triple::OSType::UnknownOS); arch_spec.GetTriple().setVendor(llvm::Triple::VendorType::CSR); // TODO At some point the description string could be processed. // It could provide a steer towards the kalimba variant which // this ELF targets. if(note.n_descsz) { const char *cstr = data.GetCStr(&offset, llvm::RoundUpToAlignment (note.n_descsz, 4)); (void)cstr; } } else if (note.n_name == LLDB_NT_OWNER_ANDROID) { arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux); arch_spec.GetTriple().setEnvironment(llvm::Triple::EnvironmentType::Android); } else if (note.n_name == LLDB_NT_OWNER_LINUX) { // This is sometimes found in core files and usually contains extended register info arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux); } else if (note.n_name == LLDB_NT_OWNER_CORE) { // Parse the NT_FILE to look for stuff in paths to shared libraries // As the contents look like: // count = 0x000000000000000a (10) // page_size = 0x0000000000001000 (4096) // Index start end file_ofs path // ===== ------------------ ------------------ ------------------ ------------------------------------- // [ 0] 0x0000000000400000 0x0000000000401000 0x0000000000000000 /tmp/a.out // [ 1] 0x0000000000600000 0x0000000000601000 0x0000000000000000 /tmp/a.out // [ 2] 0x0000000000601000 0x0000000000602000 0x0000000000000001 /tmp/a.out // [ 3] 0x00007fa79c9ed000 0x00007fa79cba8000 0x0000000000000000 /lib/x86_64-linux-gnu/libc-2.19.so // [ 4] 0x00007fa79cba8000 0x00007fa79cda7000 0x00000000000001bb /lib/x86_64-linux-gnu/libc-2.19.so // [ 5] 0x00007fa79cda7000 0x00007fa79cdab000 0x00000000000001ba /lib/x86_64-linux-gnu/libc-2.19.so // [ 6] 0x00007fa79cdab000 0x00007fa79cdad000 0x00000000000001be /lib/x86_64-linux-gnu/libc-2.19.so // [ 7] 0x00007fa79cdb2000 0x00007fa79cdd5000 0x0000000000000000 /lib/x86_64-linux-gnu/ld-2.19.so // [ 8] 0x00007fa79cfd4000 0x00007fa79cfd5000 0x0000000000000022 /lib/x86_64-linux-gnu/ld-2.19.so // [ 9] 0x00007fa79cfd5000 0x00007fa79cfd6000 0x0000000000000023 /lib/x86_64-linux-gnu/ld-2.19.so if (note.n_type == NT_FILE) { uint64_t count = data.GetU64(&offset); offset += 8 + 3*8*count; // Skip page size and all start/end/file_ofs for (size_t i=0; ish_name)); I->section_name = name; if (arch_spec.GetMachine() == llvm::Triple::mips || arch_spec.GetMachine() == llvm::Triple::mipsel || arch_spec.GetMachine() == llvm::Triple::mips64 || arch_spec.GetMachine() == llvm::Triple::mips64el) { uint32_t arch_flags = arch_spec.GetFlags (); DataExtractor data; if (sheader.sh_type == SHT_MIPS_ABIFLAGS) { - + if (section_size && (data.SetData (object_data, sheader.sh_offset, section_size) == section_size)) { lldb::offset_t ase_offset = 12; // MIPS ABI Flags Version: 0 arch_flags |= data.GetU32 (&ase_offset); } } // Settings appropriate ArchSpec ABI Flags if (header.e_flags & llvm::ELF::EF_MIPS_ABI2) - { + { arch_flags |= lldb_private::ArchSpec::eMIPSABI_N32; } else if (header.e_flags & llvm::ELF::EF_MIPS_ABI_O32) { - arch_flags |= lldb_private::ArchSpec::eMIPSABI_O32; + arch_flags |= lldb_private::ArchSpec::eMIPSABI_O32; } arch_spec.SetFlags (arch_flags); } if (name == g_sect_name_gnu_debuglink) { DataExtractor data; if (section_size && (data.SetData (object_data, sheader.sh_offset, section_size) == section_size)) { lldb::offset_t gnu_debuglink_offset = 0; gnu_debuglink_file = data.GetCStr (&gnu_debuglink_offset); gnu_debuglink_offset = llvm::RoundUpToAlignment (gnu_debuglink_offset, 4); data.GetU32 (&gnu_debuglink_offset, &gnu_debuglink_crc, 1); } } // Process ELF note section entries. bool is_note_header = (sheader.sh_type == SHT_NOTE); // The section header ".note.android.ident" is stored as a // PROGBITS type header but it is actually a note header. static ConstString g_sect_name_android_ident (".note.android.ident"); if (!is_note_header && name == g_sect_name_android_ident) is_note_header = true; if (is_note_header) { // Allow notes to refine module info. DataExtractor data; if (section_size && (data.SetData (object_data, sheader.sh_offset, section_size) == section_size)) { Error error = RefineModuleDetailsFromNote (data, arch_spec, uuid); if (error.Fail ()) { if (log) log->Printf ("ObjectFileELF::%s ELF note processing failed: %s", __FUNCTION__, error.AsCString ()); } } } } // Make any unknown triple components to be unspecified unknowns. if (arch_spec.GetTriple().getVendor() == llvm::Triple::UnknownVendor) arch_spec.GetTriple().setVendorName (llvm::StringRef()); if (arch_spec.GetTriple().getOS() == llvm::Triple::UnknownOS) arch_spec.GetTriple().setOSName (llvm::StringRef()); return section_headers.size(); } } section_headers.clear(); return 0; } size_t ObjectFileELF::GetProgramHeaderCount() { return ParseProgramHeaders(); } const elf::ELFProgramHeader * ObjectFileELF::GetProgramHeaderByIndex(lldb::user_id_t id) { if (!id || !ParseProgramHeaders()) return NULL; if (--id < m_program_headers.size()) return &m_program_headers[id]; return NULL; } -DataExtractor +DataExtractor ObjectFileELF::GetSegmentDataByIndex(lldb::user_id_t id) { const elf::ELFProgramHeader *segment_header = GetProgramHeaderByIndex(id); if (segment_header == NULL) return DataExtractor(); return DataExtractor(m_data, segment_header->p_offset, segment_header->p_filesz); } std::string ObjectFileELF::StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const { size_t pos = symbol_name.find('@'); return symbol_name.substr(0, pos).str(); } //---------------------------------------------------------------------- // ParseSectionHeaders //---------------------------------------------------------------------- size_t ObjectFileELF::ParseSectionHeaders() { return GetSectionHeaderInfo(m_section_headers, m_data, m_header, m_uuid, m_gnu_debuglink_file, m_gnu_debuglink_crc, m_arch_spec); } const ObjectFileELF::ELFSectionHeaderInfo * ObjectFileELF::GetSectionHeaderByIndex(lldb::user_id_t id) { if (!id || !ParseSectionHeaders()) return NULL; if (--id < m_section_headers.size()) return &m_section_headers[id]; return NULL; } lldb::user_id_t ObjectFileELF::GetSectionIndexByName(const char* name) { if (!name || !name[0] || !ParseSectionHeaders()) return 0; for (size_t i = 1; i < m_section_headers.size(); ++i) if (m_section_headers[i].section_name == ConstString(name)) return i; return 0; } void ObjectFileELF::CreateSections(SectionList &unified_section_list) { if (!m_sections_ap.get() && ParseSectionHeaders()) { m_sections_ap.reset(new SectionList()); for (SectionHeaderCollIter I = m_section_headers.begin(); I != m_section_headers.end(); ++I) { const ELFSectionHeaderInfo &header = *I; ConstString& name = I->section_name; const uint64_t file_size = header.sh_type == SHT_NOBITS ? 0 : header.sh_size; const uint64_t vm_size = header.sh_flags & SHF_ALLOC ? header.sh_size : 0; static ConstString g_sect_name_text (".text"); static ConstString g_sect_name_data (".data"); static ConstString g_sect_name_bss (".bss"); static ConstString g_sect_name_tdata (".tdata"); static ConstString g_sect_name_tbss (".tbss"); static ConstString g_sect_name_dwarf_debug_abbrev (".debug_abbrev"); static ConstString g_sect_name_dwarf_debug_addr (".debug_addr"); static ConstString g_sect_name_dwarf_debug_aranges (".debug_aranges"); static ConstString g_sect_name_dwarf_debug_frame (".debug_frame"); static ConstString g_sect_name_dwarf_debug_info (".debug_info"); static ConstString g_sect_name_dwarf_debug_line (".debug_line"); static ConstString g_sect_name_dwarf_debug_loc (".debug_loc"); static ConstString g_sect_name_dwarf_debug_macinfo (".debug_macinfo"); static ConstString g_sect_name_dwarf_debug_macro (".debug_macro"); static ConstString g_sect_name_dwarf_debug_pubnames (".debug_pubnames"); static ConstString g_sect_name_dwarf_debug_pubtypes (".debug_pubtypes"); static ConstString g_sect_name_dwarf_debug_ranges (".debug_ranges"); static ConstString g_sect_name_dwarf_debug_str (".debug_str"); static ConstString g_sect_name_dwarf_debug_str_offsets (".debug_str_offsets"); static ConstString g_sect_name_dwarf_debug_abbrev_dwo (".debug_abbrev.dwo"); static ConstString g_sect_name_dwarf_debug_info_dwo (".debug_info.dwo"); static ConstString g_sect_name_dwarf_debug_line_dwo (".debug_line.dwo"); static ConstString g_sect_name_dwarf_debug_macro_dwo (".debug_macro.dwo"); static ConstString g_sect_name_dwarf_debug_loc_dwo (".debug_loc.dwo"); static ConstString g_sect_name_dwarf_debug_str_dwo (".debug_str.dwo"); static ConstString g_sect_name_dwarf_debug_str_offsets_dwo (".debug_str_offsets.dwo"); static ConstString g_sect_name_eh_frame (".eh_frame"); static ConstString g_sect_name_arm_exidx (".ARM.exidx"); static ConstString g_sect_name_arm_extab (".ARM.extab"); static ConstString g_sect_name_go_symtab (".gosymtab"); SectionType sect_type = eSectionTypeOther; bool is_thread_specific = false; if (name == g_sect_name_text) sect_type = eSectionTypeCode; else if (name == g_sect_name_data) sect_type = eSectionTypeData; else if (name == g_sect_name_bss) sect_type = eSectionTypeZeroFill; else if (name == g_sect_name_tdata) { sect_type = eSectionTypeData; - is_thread_specific = true; + is_thread_specific = true; } else if (name == g_sect_name_tbss) { - sect_type = eSectionTypeZeroFill; - is_thread_specific = true; + sect_type = eSectionTypeZeroFill; + is_thread_specific = true; } // .debug_abbrev – Abbreviations used in the .debug_info section // .debug_aranges – Lookup table for mapping addresses to compilation units // .debug_frame – Call frame information // .debug_info – The core DWARF information section // .debug_line – Line number information // .debug_loc – Location lists used in DW_AT_location attributes // .debug_macinfo – Macro information // .debug_pubnames – Lookup table for mapping object and function names to compilation units // .debug_pubtypes – Lookup table for mapping type names to compilation units // .debug_ranges – Address ranges used in DW_AT_ranges attributes // .debug_str – String table used in .debug_info // MISSING? .gnu_debugdata - "mini debuginfo / MiniDebugInfo" section, http://sourceware.org/gdb/onlinedocs/gdb/MiniDebugInfo.html // MISSING? .debug-index - http://src.chromium.org/viewvc/chrome/trunk/src/build/gdb-add-index?pathrev=144644 // MISSING? .debug_types - Type descriptions from DWARF 4? See http://gcc.gnu.org/wiki/DwarfSeparateTypeInfo else if (name == g_sect_name_dwarf_debug_abbrev) sect_type = eSectionTypeDWARFDebugAbbrev; else if (name == g_sect_name_dwarf_debug_addr) sect_type = eSectionTypeDWARFDebugAddr; else if (name == g_sect_name_dwarf_debug_aranges) sect_type = eSectionTypeDWARFDebugAranges; else if (name == g_sect_name_dwarf_debug_frame) sect_type = eSectionTypeDWARFDebugFrame; else if (name == g_sect_name_dwarf_debug_info) sect_type = eSectionTypeDWARFDebugInfo; else if (name == g_sect_name_dwarf_debug_line) sect_type = eSectionTypeDWARFDebugLine; else if (name == g_sect_name_dwarf_debug_loc) sect_type = eSectionTypeDWARFDebugLoc; else if (name == g_sect_name_dwarf_debug_macinfo) sect_type = eSectionTypeDWARFDebugMacInfo; else if (name == g_sect_name_dwarf_debug_macro) sect_type = eSectionTypeDWARFDebugMacro; else if (name == g_sect_name_dwarf_debug_pubnames) sect_type = eSectionTypeDWARFDebugPubNames; else if (name == g_sect_name_dwarf_debug_pubtypes) sect_type = eSectionTypeDWARFDebugPubTypes; else if (name == g_sect_name_dwarf_debug_ranges) sect_type = eSectionTypeDWARFDebugRanges; else if (name == g_sect_name_dwarf_debug_str) sect_type = eSectionTypeDWARFDebugStr; else if (name == g_sect_name_dwarf_debug_str_offsets) sect_type = eSectionTypeDWARFDebugStrOffsets; else if (name == g_sect_name_dwarf_debug_abbrev_dwo) sect_type = eSectionTypeDWARFDebugAbbrev; else if (name == g_sect_name_dwarf_debug_info_dwo) sect_type = eSectionTypeDWARFDebugInfo; else if (name == g_sect_name_dwarf_debug_line_dwo) sect_type = eSectionTypeDWARFDebugLine; else if (name == g_sect_name_dwarf_debug_macro_dwo) sect_type = eSectionTypeDWARFDebugMacro; else if (name == g_sect_name_dwarf_debug_loc_dwo) sect_type = eSectionTypeDWARFDebugLoc; else if (name == g_sect_name_dwarf_debug_str_dwo) sect_type = eSectionTypeDWARFDebugStr; else if (name == g_sect_name_dwarf_debug_str_offsets_dwo) sect_type = eSectionTypeDWARFDebugStrOffsets; else if (name == g_sect_name_eh_frame) sect_type = eSectionTypeEHFrame; else if (name == g_sect_name_arm_exidx) sect_type = eSectionTypeARMexidx; else if (name == g_sect_name_arm_extab) sect_type = eSectionTypeARMextab; else if (name == g_sect_name_go_symtab) sect_type = eSectionTypeGoSymtab; switch (header.sh_type) { case SHT_SYMTAB: assert (sect_type == eSectionTypeOther); sect_type = eSectionTypeELFSymbolTable; break; case SHT_DYNSYM: assert (sect_type == eSectionTypeOther); sect_type = eSectionTypeELFDynamicSymbols; break; case SHT_RELA: case SHT_REL: assert (sect_type == eSectionTypeOther); sect_type = eSectionTypeELFRelocationEntries; break; case SHT_DYNAMIC: assert (sect_type == eSectionTypeOther); sect_type = eSectionTypeELFDynamicLinkInfo; break; } if (eSectionTypeOther == sect_type) { // the kalimba toolchain assumes that ELF section names are free-form. It does // support linkscripts which (can) give rise to various arbitrarily named - // sections being "Code" or "Data". + // sections being "Code" or "Data". sect_type = kalimbaSectionType(m_header, header); } const uint32_t target_bytes_size = - (eSectionTypeData == sect_type || eSectionTypeZeroFill == sect_type) ? + (eSectionTypeData == sect_type || eSectionTypeZeroFill == sect_type) ? m_arch_spec.GetDataByteSize() : eSectionTypeCode == sect_type ? m_arch_spec.GetCodeByteSize() : 1; elf::elf_xword log2align = (header.sh_addralign==0) ? 0 : llvm::Log2_64(header.sh_addralign); SectionSP section_sp (new Section(GetModule(), // Module to which this section belongs. this, // ObjectFile to which this section belongs and should read section data from. SectionIndex(I), // Section ID. name, // Section name. sect_type, // Section type. header.sh_addr, // VM address. vm_size, // VM size in bytes of this section. header.sh_offset, // Offset of this section in the file. file_size, // Size of the section as found in the file. log2align, // Alignment of the section header.sh_flags, // Flags for this section. target_bytes_size));// Number of host bytes per target byte if (is_thread_specific) section_sp->SetIsThreadSpecific (is_thread_specific); m_sections_ap->AddSection(section_sp); } } if (m_sections_ap.get()) { if (GetType() == eTypeDebugInfo) { static const SectionType g_sections[] = { eSectionTypeDWARFDebugAbbrev, eSectionTypeDWARFDebugAddr, eSectionTypeDWARFDebugAranges, eSectionTypeDWARFDebugFrame, eSectionTypeDWARFDebugInfo, eSectionTypeDWARFDebugLine, eSectionTypeDWARFDebugLoc, eSectionTypeDWARFDebugMacInfo, eSectionTypeDWARFDebugPubNames, eSectionTypeDWARFDebugPubTypes, eSectionTypeDWARFDebugRanges, eSectionTypeDWARFDebugStr, eSectionTypeDWARFDebugStrOffsets, eSectionTypeELFSymbolTable, }; SectionList *elf_section_list = m_sections_ap.get(); for (size_t idx = 0; idx < sizeof(g_sections) / sizeof(g_sections[0]); ++idx) { SectionType section_type = g_sections[idx]; SectionSP section_sp (elf_section_list->FindSectionByType (section_type, true)); if (section_sp) { SectionSP module_section_sp (unified_section_list.FindSectionByType (section_type, true)); if (module_section_sp) unified_section_list.ReplaceSection (module_section_sp->GetID(), section_sp); else unified_section_list.AddSection (section_sp); } } } else { unified_section_list = *m_sections_ap; } } } // Find the arm/aarch64 mapping symbol character in the given symbol name. Mapping symbols have the // form of "$[.]*". Additionally we recognize cases when the mapping symbol prefixed by // an arbitrary string because if a symbol prefix added to each symbol in the object file with // objcopy then the mapping symbols are also prefixed. static char FindArmAarch64MappingSymbol(const char* symbol_name) { if (!symbol_name) return '\0'; const char* dollar_pos = ::strchr(symbol_name, '$'); if (!dollar_pos || dollar_pos[1] == '\0') return '\0'; if (dollar_pos[2] == '\0' || dollar_pos[2] == '.') return dollar_pos[1]; return '\0'; } #define STO_MIPS_ISA (3 << 6) #define STO_MICROMIPS (2 << 6) #define IS_MICROMIPS(ST_OTHER) (((ST_OTHER) & STO_MIPS_ISA) == STO_MICROMIPS) // private unsigned ObjectFileELF::ParseSymbols (Symtab *symtab, user_id_t start_id, SectionList *section_list, const size_t num_symbols, const DataExtractor &symtab_data, const DataExtractor &strtab_data) { ELFSymbol symbol; lldb::offset_t offset = 0; static ConstString text_section_name(".text"); static ConstString init_section_name(".init"); static ConstString fini_section_name(".fini"); static ConstString ctors_section_name(".ctors"); static ConstString dtors_section_name(".dtors"); static ConstString data_section_name(".data"); static ConstString rodata_section_name(".rodata"); static ConstString rodata1_section_name(".rodata1"); static ConstString data2_section_name(".data1"); static ConstString bss_section_name(".bss"); static ConstString opd_section_name(".opd"); // For ppc64 // On Android the oatdata and the oatexec symbols in system@framework@boot.oat covers the full // .text section what causes issues with displaying unusable symbol name to the user and very // slow unwinding speed because the instruction emulation based unwind plans try to emulate all // instructions in these symbols. Don't add these symbols to the symbol list as they have no // use for the debugger and they are causing a lot of trouble. // Filtering can't be restricted to Android because this special object file don't contain the // note section specifying the environment to Android but the custom extension and file name // makes it highly unlikely that this will collide with anything else. bool skip_oatdata_oatexec = m_file.GetFilename() == ConstString("system@framework@boot.oat"); ArchSpec arch; GetArchitecture(arch); // Local cache to avoid doing a FindSectionByName for each symbol. The "const char*" key must // came from a ConstString object so they can be compared by pointer std::unordered_map section_name_to_section; unsigned i; for (i = 0; i < num_symbols; ++i) { if (symbol.Parse(symtab_data, &offset) == false) break; - + const char *symbol_name = strtab_data.PeekCStr(symbol.st_name); // No need to add non-section symbols that have no names if (symbol.getType() != STT_SECTION && (symbol_name == NULL || symbol_name[0] == '\0')) continue; // Skipping oatdata and oatexec sections if it is requested. See details above the // definition of skip_oatdata_oatexec for the reasons. if (skip_oatdata_oatexec && (::strcmp(symbol_name, "oatdata") == 0 || ::strcmp(symbol_name, "oatexec") == 0)) continue; SectionSP symbol_section_sp; SymbolType symbol_type = eSymbolTypeInvalid; Elf64_Half symbol_idx = symbol.st_shndx; switch (symbol_idx) { case SHN_ABS: symbol_type = eSymbolTypeAbsolute; break; case SHN_UNDEF: symbol_type = eSymbolTypeUndefined; break; default: symbol_section_sp = section_list->GetSectionAtIndex(symbol_idx); break; } // If a symbol is undefined do not process it further even if it has a STT type if (symbol_type != eSymbolTypeUndefined) { switch (symbol.getType()) { default: case STT_NOTYPE: // The symbol's type is not specified. break; case STT_OBJECT: // The symbol is associated with a data object, such as a variable, // an array, etc. symbol_type = eSymbolTypeData; break; case STT_FUNC: // The symbol is associated with a function or other executable code. symbol_type = eSymbolTypeCode; break; case STT_SECTION: // The symbol is associated with a section. Symbol table entries of // this type exist primarily for relocation and normally have // STB_LOCAL binding. break; case STT_FILE: // Conventionally, the symbol's name gives the name of the source // file associated with the object file. A file symbol has STB_LOCAL // binding, its section index is SHN_ABS, and it precedes the other // STB_LOCAL symbols for the file, if it is present. symbol_type = eSymbolTypeSourceFile; break; case STT_GNU_IFUNC: // The symbol is associated with an indirect function. The actual // function will be resolved if it is referenced. symbol_type = eSymbolTypeResolver; break; } } if (symbol_type == eSymbolTypeInvalid) { if (symbol_section_sp) { const ConstString §_name = symbol_section_sp->GetName(); if (sect_name == text_section_name || sect_name == init_section_name || sect_name == fini_section_name || sect_name == ctors_section_name || sect_name == dtors_section_name) { symbol_type = eSymbolTypeCode; } else if (sect_name == data_section_name || sect_name == data2_section_name || sect_name == rodata_section_name || sect_name == rodata1_section_name || sect_name == bss_section_name) { symbol_type = eSymbolTypeData; } } } int64_t symbol_value_offset = 0; uint32_t additional_flags = 0; if (arch.IsValid()) { if (arch.GetMachine() == llvm::Triple::arm) { if (symbol.getBinding() == STB_LOCAL) { char mapping_symbol = FindArmAarch64MappingSymbol(symbol_name); if (symbol_type == eSymbolTypeCode) { switch (mapping_symbol) { case 'a': // $a[.]* - marks an ARM instruction sequence m_address_class_map[symbol.st_value] = eAddressClassCode; break; case 'b': case 't': // $b[.]* - marks a THUMB BL instruction sequence // $t[.]* - marks a THUMB instruction sequence m_address_class_map[symbol.st_value] = eAddressClassCodeAlternateISA; break; case 'd': // $d[.]* - marks a data item sequence (e.g. lit pool) m_address_class_map[symbol.st_value] = eAddressClassData; break; } } if (mapping_symbol) continue; } } else if (arch.GetMachine() == llvm::Triple::aarch64) { if (symbol.getBinding() == STB_LOCAL) { char mapping_symbol = FindArmAarch64MappingSymbol(symbol_name); if (symbol_type == eSymbolTypeCode) { switch (mapping_symbol) { case 'x': // $x[.]* - marks an A64 instruction sequence m_address_class_map[symbol.st_value] = eAddressClassCode; break; case 'd': // $d[.]* - marks a data item sequence (e.g. lit pool) m_address_class_map[symbol.st_value] = eAddressClassData; break; } } if (mapping_symbol) continue; } } if (arch.GetMachine() == llvm::Triple::arm) { if (symbol_type == eSymbolTypeCode) { if (symbol.st_value & 1) { // Subtracting 1 from the address effectively unsets // the low order bit, which results in the address // actually pointing to the beginning of the symbol. // This delta will be used below in conjunction with // symbol.st_value to produce the final symbol_value // that we store in the symtab. symbol_value_offset = -1; additional_flags = ARM_ELF_SYM_IS_THUMB; m_address_class_map[symbol.st_value^1] = eAddressClassCodeAlternateISA; } else { // This address is ARM m_address_class_map[symbol.st_value] = eAddressClassCode; } } } /* * MIPS: * The bit #0 of an address is used for ISA mode (1 for microMIPS, 0 for MIPS). * This allows processer to switch between microMIPS and MIPS without any need * for special mode-control register. However, apart from .debug_line, none of * the ELF/DWARF sections set the ISA bit (for symbol or section). Use st_other * flag to check whether the symbol is microMIPS and then set the address class * accordingly. */ const llvm::Triple::ArchType llvm_arch = arch.GetMachine(); if (llvm_arch == llvm::Triple::mips || llvm_arch == llvm::Triple::mipsel || llvm_arch == llvm::Triple::mips64 || llvm_arch == llvm::Triple::mips64el) { if (IS_MICROMIPS(symbol.st_other)) m_address_class_map[symbol.st_value] = eAddressClassCodeAlternateISA; else if ((symbol.st_value & 1) && (symbol_type == eSymbolTypeCode)) { symbol.st_value = symbol.st_value & (~1ull); m_address_class_map[symbol.st_value] = eAddressClassCodeAlternateISA; } else { if (symbol_type == eSymbolTypeCode) m_address_class_map[symbol.st_value] = eAddressClassCode; else if (symbol_type == eSymbolTypeData) m_address_class_map[symbol.st_value] = eAddressClassData; else m_address_class_map[symbol.st_value] = eAddressClassUnknown; } } } // symbol_value_offset may contain 0 for ARM symbols or -1 for // THUMB symbols. See above for more details. uint64_t symbol_value = symbol.st_value + symbol_value_offset; if (symbol_section_sp && CalculateType() != ObjectFile::Type::eTypeObjectFile) symbol_value -= symbol_section_sp->GetFileAddress(); if (symbol_section_sp) { ModuleSP module_sp(GetModule()); if (module_sp) { SectionList *module_section_list = module_sp->GetSectionList(); if (module_section_list && module_section_list != section_list) { const ConstString §_name = symbol_section_sp->GetName(); auto section_it = section_name_to_section.find(sect_name.GetCString()); if (section_it == section_name_to_section.end()) section_it = section_name_to_section.emplace( sect_name.GetCString(), module_section_list->FindSectionByName (sect_name)).first; if (section_it->second && section_it->second->GetFileSize()) symbol_section_sp = section_it->second; } } } bool is_global = symbol.getBinding() == STB_GLOBAL; uint32_t flags = symbol.st_other << 8 | symbol.st_info | additional_flags; bool is_mangled = symbol_name ? (symbol_name[0] == '_' && symbol_name[1] == 'Z') : false; llvm::StringRef symbol_ref(symbol_name); // Symbol names may contain @VERSION suffixes. Find those and strip them temporarily. size_t version_pos = symbol_ref.find('@'); bool has_suffix = version_pos != llvm::StringRef::npos; llvm::StringRef symbol_bare = symbol_ref.substr(0, version_pos); Mangled mangled(ConstString(symbol_bare), is_mangled); // Now append the suffix back to mangled and unmangled names. Only do it if the // demangling was successful (string is not empty). if (has_suffix) { llvm::StringRef suffix = symbol_ref.substr(version_pos); llvm::StringRef mangled_name = mangled.GetMangledName().GetStringRef(); if (! mangled_name.empty()) mangled.SetMangledName( ConstString((mangled_name + suffix).str()) ); ConstString demangled = mangled.GetDemangledName(lldb::eLanguageTypeUnknown); llvm::StringRef demangled_name = demangled.GetStringRef(); if (!demangled_name.empty()) mangled.SetDemangledName( ConstString((demangled_name + suffix).str()) ); } Symbol dc_symbol( i + start_id, // ID is the original symbol table index. mangled, symbol_type, // Type of this symbol is_global, // Is this globally visible? false, // Is this symbol debug info? false, // Is this symbol a trampoline? false, // Is this symbol artificial? AddressRange( symbol_section_sp, // Section in which this symbol is defined or null. symbol_value, // Offset in section or symbol value. symbol.st_size), // Size in bytes of this symbol. symbol.st_size != 0, // Size is valid if it is not 0 has_suffix, // Contains linker annotations? flags); // Symbol flags. symtab->AddSymbol(dc_symbol); } return i; } unsigned ObjectFileELF::ParseSymbolTable(Symtab *symbol_table, user_id_t start_id, lldb_private::Section *symtab) { if (symtab->GetObjectFile() != this) { // If the symbol table section is owned by a different object file, have it do the // parsing. ObjectFileELF *obj_file_elf = static_cast(symtab->GetObjectFile()); return obj_file_elf->ParseSymbolTable (symbol_table, start_id, symtab); } // Get section list for this object file. SectionList *section_list = m_sections_ap.get(); if (!section_list) return 0; user_id_t symtab_id = symtab->GetID(); const ELFSectionHeaderInfo *symtab_hdr = GetSectionHeaderByIndex(symtab_id); - assert(symtab_hdr->sh_type == SHT_SYMTAB || + assert(symtab_hdr->sh_type == SHT_SYMTAB || symtab_hdr->sh_type == SHT_DYNSYM); // sh_link: section header index of associated string table. // Section ID's are ones based. user_id_t strtab_id = symtab_hdr->sh_link + 1; Section *strtab = section_list->FindSectionByID(strtab_id).get(); if (symtab && strtab) { assert (symtab->GetObjectFile() == this); assert (strtab->GetObjectFile() == this); DataExtractor symtab_data; DataExtractor strtab_data; if (ReadSectionData(symtab, symtab_data) && ReadSectionData(strtab, strtab_data)) { size_t num_symbols = symtab_data.GetByteSize() / symtab_hdr->sh_entsize; return ParseSymbols(symbol_table, start_id, section_list, num_symbols, symtab_data, strtab_data); } } return 0; } size_t ObjectFileELF::ParseDynamicSymbols() { if (m_dynamic_symbols.size()) return m_dynamic_symbols.size(); SectionList *section_list = GetSectionList(); if (!section_list) return 0; // Find the SHT_DYNAMIC section. Section *dynsym = section_list->FindSectionByType (eSectionTypeELFDynamicLinkInfo, true).get(); if (!dynsym) return 0; assert (dynsym->GetObjectFile() == this); ELFDynamic symbol; DataExtractor dynsym_data; if (ReadSectionData(dynsym, dynsym_data)) { const lldb::offset_t section_size = dynsym_data.GetByteSize(); lldb::offset_t cursor = 0; while (cursor < section_size) { if (!symbol.Parse(dynsym_data, &cursor)) break; m_dynamic_symbols.push_back(symbol); } } return m_dynamic_symbols.size(); } const ELFDynamic * ObjectFileELF::FindDynamicSymbol(unsigned tag) { if (!ParseDynamicSymbols()) return NULL; DynamicSymbolCollIter I = m_dynamic_symbols.begin(); DynamicSymbolCollIter E = m_dynamic_symbols.end(); for ( ; I != E; ++I) { ELFDynamic *symbol = &*I; if (symbol->d_tag == tag) return symbol; } return NULL; } unsigned ObjectFileELF::PLTRelocationType() { // DT_PLTREL // This member specifies the type of relocation entry to which the // procedure linkage table refers. The d_val member holds DT_REL or // DT_RELA, as appropriate. All relocations in a procedure linkage table // must use the same relocation. const ELFDynamic *symbol = FindDynamicSymbol(DT_PLTREL); if (symbol) return symbol->d_val; return 0; } // Returns the size of the normal plt entries and the offset of the first normal plt entry. The // 0th entry in the plt table is usually a resolution entry which have different size in some // architectures then the rest of the plt entries. static std::pair GetPltEntrySizeAndOffset(const ELFSectionHeader* rel_hdr, const ELFSectionHeader* plt_hdr) { const elf_xword num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize; // Clang 3.3 sets entsize to 4 for 32-bit binaries, but the plt entries are 16 bytes. // So round the entsize up by the alignment if addralign is set. elf_xword plt_entsize = plt_hdr->sh_addralign ? llvm::RoundUpToAlignment (plt_hdr->sh_entsize, plt_hdr->sh_addralign) : plt_hdr->sh_entsize; if (plt_entsize == 0) { // The linker haven't set the plt_hdr->sh_entsize field. Try to guess the size of the plt // entries based on the number of entries and the size of the plt section with the // assumption that the size of the 0th entry is at least as big as the size of the normal // entries and it isn't much bigger then that. if (plt_hdr->sh_addralign) plt_entsize = plt_hdr->sh_size / plt_hdr->sh_addralign / (num_relocations + 1) * plt_hdr->sh_addralign; else plt_entsize = plt_hdr->sh_size / (num_relocations + 1); } elf_xword plt_offset = plt_hdr->sh_size - num_relocations * plt_entsize; return std::make_pair(plt_entsize, plt_offset); } static unsigned ParsePLTRelocations(Symtab *symbol_table, user_id_t start_id, unsigned rel_type, const ELFHeader *hdr, const ELFSectionHeader *rel_hdr, const ELFSectionHeader *plt_hdr, const ELFSectionHeader *sym_hdr, const lldb::SectionSP &plt_section_sp, DataExtractor &rel_data, DataExtractor &symtab_data, DataExtractor &strtab_data) { ELFRelocation rel(rel_type); ELFSymbol symbol; lldb::offset_t offset = 0; uint64_t plt_offset, plt_entsize; std::tie(plt_entsize, plt_offset) = GetPltEntrySizeAndOffset(rel_hdr, plt_hdr); const elf_xword num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize; typedef unsigned (*reloc_info_fn)(const ELFRelocation &rel); reloc_info_fn reloc_type; reloc_info_fn reloc_symbol; if (hdr->Is32Bit()) { reloc_type = ELFRelocation::RelocType32; reloc_symbol = ELFRelocation::RelocSymbol32; } else { reloc_type = ELFRelocation::RelocType64; reloc_symbol = ELFRelocation::RelocSymbol64; } unsigned slot_type = hdr->GetRelocationJumpSlotType(); unsigned i; for (i = 0; i < num_relocations; ++i) { if (rel.Parse(rel_data, &offset) == false) break; if (reloc_type(rel) != slot_type) continue; lldb::offset_t symbol_offset = reloc_symbol(rel) * sym_hdr->sh_entsize; if (!symbol.Parse(symtab_data, &symbol_offset)) break; const char *symbol_name = strtab_data.PeekCStr(symbol.st_name); bool is_mangled = symbol_name ? (symbol_name[0] == '_' && symbol_name[1] == 'Z') : false; uint64_t plt_index = plt_offset + i * plt_entsize; Symbol jump_symbol( i + start_id, // Symbol table index symbol_name, // symbol name. is_mangled, // is the symbol name mangled? eSymbolTypeTrampoline, // Type of this symbol false, // Is this globally visible? false, // Is this symbol debug info? true, // Is this symbol a trampoline? true, // Is this symbol artificial? plt_section_sp, // Section in which this symbol is defined or null. plt_index, // Offset in section or symbol value. plt_entsize, // Size in bytes of this symbol. true, // Size is valid false, // Contains linker annotations? 0); // Symbol flags. symbol_table->AddSymbol(jump_symbol); } return i; } unsigned ObjectFileELF::ParseTrampolineSymbols(Symtab *symbol_table, user_id_t start_id, const ELFSectionHeaderInfo *rel_hdr, user_id_t rel_id) { assert(rel_hdr->sh_type == SHT_RELA || rel_hdr->sh_type == SHT_REL); // The link field points to the associated symbol table. The info field // points to the section holding the plt. user_id_t symtab_id = rel_hdr->sh_link; user_id_t plt_id = rel_hdr->sh_info; // If the link field doesn't point to the appropriate symbol name table then // try to find it by name as some compiler don't fill in the link fields. if (!symtab_id) symtab_id = GetSectionIndexByName(".dynsym"); if (!plt_id) plt_id = GetSectionIndexByName(".plt"); if (!symtab_id || !plt_id) return 0; // Section ID's are ones based; symtab_id++; plt_id++; const ELFSectionHeaderInfo *plt_hdr = GetSectionHeaderByIndex(plt_id); if (!plt_hdr) return 0; const ELFSectionHeaderInfo *sym_hdr = GetSectionHeaderByIndex(symtab_id); if (!sym_hdr) return 0; SectionList *section_list = m_sections_ap.get(); if (!section_list) return 0; Section *rel_section = section_list->FindSectionByID(rel_id).get(); if (!rel_section) return 0; SectionSP plt_section_sp (section_list->FindSectionByID(plt_id)); if (!plt_section_sp) return 0; Section *symtab = section_list->FindSectionByID(symtab_id).get(); if (!symtab) return 0; // sh_link points to associated string table. Section *strtab = section_list->FindSectionByID(sym_hdr->sh_link + 1).get(); if (!strtab) return 0; DataExtractor rel_data; if (!ReadSectionData(rel_section, rel_data)) return 0; DataExtractor symtab_data; if (!ReadSectionData(symtab, symtab_data)) return 0; DataExtractor strtab_data; if (!ReadSectionData(strtab, strtab_data)) return 0; unsigned rel_type = PLTRelocationType(); if (!rel_type) return 0; - return ParsePLTRelocations (symbol_table, - start_id, + return ParsePLTRelocations (symbol_table, + start_id, rel_type, - &m_header, - rel_hdr, - plt_hdr, + &m_header, + rel_hdr, + plt_hdr, sym_hdr, - plt_section_sp, - rel_data, - symtab_data, + plt_section_sp, + rel_data, + symtab_data, strtab_data); } unsigned ObjectFileELF::RelocateSection(Symtab* symtab, const ELFHeader *hdr, const ELFSectionHeader *rel_hdr, const ELFSectionHeader *symtab_hdr, const ELFSectionHeader *debug_hdr, DataExtractor &rel_data, DataExtractor &symtab_data, DataExtractor &debug_data, Section* rel_section) { ELFRelocation rel(rel_hdr->sh_type); lldb::addr_t offset = 0; const unsigned num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize; typedef unsigned (*reloc_info_fn)(const ELFRelocation &rel); reloc_info_fn reloc_type; reloc_info_fn reloc_symbol; if (hdr->Is32Bit()) { reloc_type = ELFRelocation::RelocType32; reloc_symbol = ELFRelocation::RelocSymbol32; } else { reloc_type = ELFRelocation::RelocType64; reloc_symbol = ELFRelocation::RelocSymbol64; } for (unsigned i = 0; i < num_relocations; ++i) { if (rel.Parse(rel_data, &offset) == false) break; Symbol* symbol = NULL; if (hdr->Is32Bit()) { switch (reloc_type(rel)) { case R_386_32: case R_386_PC32: default: assert(false && "unexpected relocation type"); } } else { switch (reloc_type(rel)) { case R_X86_64_64: { symbol = symtab->FindSymbolByID(reloc_symbol(rel)); if (symbol) { addr_t value = symbol->GetAddressRef().GetFileAddress(); DataBufferSP& data_buffer_sp = debug_data.GetSharedDataBuffer(); uint64_t* dst = reinterpret_cast(data_buffer_sp->GetBytes() + rel_section->GetFileOffset() + ELFRelocation::RelocOffset64(rel)); *dst = value + ELFRelocation::RelocAddend64(rel); } break; } case R_X86_64_32: case R_X86_64_32S: { symbol = symtab->FindSymbolByID(reloc_symbol(rel)); if (symbol) { addr_t value = symbol->GetAddressRef().GetFileAddress(); value += ELFRelocation::RelocAddend32(rel); assert((reloc_type(rel) == R_X86_64_32 && (value <= UINT32_MAX)) || (reloc_type(rel) == R_X86_64_32S && ((int64_t)value <= INT32_MAX && (int64_t)value >= INT32_MIN))); uint32_t truncated_addr = (value & 0xFFFFFFFF); DataBufferSP& data_buffer_sp = debug_data.GetSharedDataBuffer(); uint32_t* dst = reinterpret_cast(data_buffer_sp->GetBytes() + rel_section->GetFileOffset() + ELFRelocation::RelocOffset32(rel)); *dst = truncated_addr; } break; } case R_X86_64_PC32: default: assert(false && "unexpected relocation type"); } } } return 0; } unsigned ObjectFileELF::RelocateDebugSections(const ELFSectionHeader *rel_hdr, user_id_t rel_id) { assert(rel_hdr->sh_type == SHT_RELA || rel_hdr->sh_type == SHT_REL); // Parse in the section list if needed. SectionList *section_list = GetSectionList(); if (!section_list) return 0; // Section ID's are ones based. user_id_t symtab_id = rel_hdr->sh_link + 1; user_id_t debug_id = rel_hdr->sh_info + 1; const ELFSectionHeader *symtab_hdr = GetSectionHeaderByIndex(symtab_id); if (!symtab_hdr) return 0; const ELFSectionHeader *debug_hdr = GetSectionHeaderByIndex(debug_id); if (!debug_hdr) return 0; Section *rel = section_list->FindSectionByID(rel_id).get(); if (!rel) return 0; Section *symtab = section_list->FindSectionByID(symtab_id).get(); if (!symtab) return 0; Section *debug = section_list->FindSectionByID(debug_id).get(); if (!debug) return 0; DataExtractor rel_data; DataExtractor symtab_data; DataExtractor debug_data; if (ReadSectionData(rel, rel_data) && ReadSectionData(symtab, symtab_data) && ReadSectionData(debug, debug_data)) { RelocateSection(m_symtab_ap.get(), &m_header, rel_hdr, symtab_hdr, debug_hdr, rel_data, symtab_data, debug_data, debug); } return 0; } Symtab * ObjectFileELF::GetSymtab() { ModuleSP module_sp(GetModule()); if (!module_sp) return NULL; // We always want to use the main object file so we (hopefully) only have one cached copy // of our symtab, dynamic sections, etc. ObjectFile *module_obj_file = module_sp->GetObjectFile(); if (module_obj_file && module_obj_file != this) return module_obj_file->GetSymtab(); if (m_symtab_ap.get() == NULL) { SectionList *section_list = module_sp->GetSectionList(); if (!section_list) return NULL; uint64_t symbol_id = 0; lldb_private::Mutex::Locker locker(module_sp->GetMutex()); // Sharable objects and dynamic executables usually have 2 distinct symbol // tables, one named ".symtab", and the other ".dynsym". The dynsym is a smaller // version of the symtab that only contains global symbols. The information found // in the dynsym is therefore also found in the symtab, while the reverse is not // necessarily true. Section *symtab = section_list->FindSectionByType (eSectionTypeELFSymbolTable, true).get(); if (!symtab) { // The symtab section is non-allocable and can be stripped, so if it doesn't exist // then use the dynsym section which should always be there. symtab = section_list->FindSectionByType (eSectionTypeELFDynamicSymbols, true).get(); } if (symtab) { m_symtab_ap.reset(new Symtab(symtab->GetObjectFile())); symbol_id += ParseSymbolTable (m_symtab_ap.get(), symbol_id, symtab); } // DT_JMPREL // If present, this entry's d_ptr member holds the address of relocation // entries associated solely with the procedure linkage table. Separating // these relocation entries lets the dynamic linker ignore them during // process initialization, if lazy binding is enabled. If this entry is // present, the related entries of types DT_PLTRELSZ and DT_PLTREL must // also be present. const ELFDynamic *symbol = FindDynamicSymbol(DT_JMPREL); if (symbol) { // Synthesize trampoline symbols to help navigate the PLT. addr_t addr = symbol->d_ptr; Section *reloc_section = section_list->FindSectionContainingFileAddress(addr).get(); - if (reloc_section) + if (reloc_section) { user_id_t reloc_id = reloc_section->GetID(); const ELFSectionHeaderInfo *reloc_header = GetSectionHeaderByIndex(reloc_id); assert(reloc_header); - + if (m_symtab_ap == nullptr) m_symtab_ap.reset(new Symtab(reloc_section->GetObjectFile())); ParseTrampolineSymbols (m_symtab_ap.get(), symbol_id, reloc_header, reloc_id); } } - + // If we still don't have any symtab then create an empty instance to avoid do the section // lookup next time. if (m_symtab_ap == nullptr) m_symtab_ap.reset(new Symtab(this)); - + m_symtab_ap->CalculateSymbolSizes(); } for (SectionHeaderCollIter I = m_section_headers.begin(); I != m_section_headers.end(); ++I) { if (I->sh_type == SHT_RELA || I->sh_type == SHT_REL) { if (CalculateType() == eTypeObjectFile) { const char *section_name = I->section_name.AsCString(""); if (strstr(section_name, ".rela.debug") || strstr(section_name, ".rel.debug")) { const ELFSectionHeader &reloc_header = *I; user_id_t reloc_id = SectionIndex(I); RelocateDebugSections(&reloc_header, reloc_id); } } } } return m_symtab_ap.get(); } Symbol * ObjectFileELF::ResolveSymbolForAddress(const Address& so_addr, bool verify_unique) { if (!m_symtab_ap.get()) return nullptr; // GetSymtab() should be called first. const SectionList *section_list = GetSectionList(); if (!section_list) return nullptr; if (DWARFCallFrameInfo *eh_frame = GetUnwindTable().GetEHFrameInfo()) { AddressRange range; if (eh_frame->GetAddressRange (so_addr, range)) { const addr_t file_addr = range.GetBaseAddress().GetFileAddress(); Symbol * symbol = verify_unique ? m_symtab_ap->FindSymbolContainingFileAddress(file_addr) : nullptr; if (symbol) return symbol; // Note that a (stripped) symbol won't be found by GetSymtab()... lldb::SectionSP eh_sym_section_sp = section_list->FindSectionContainingFileAddress(file_addr); if (eh_sym_section_sp.get()) { addr_t section_base = eh_sym_section_sp->GetFileAddress(); addr_t offset = file_addr - section_base; uint64_t symbol_id = m_symtab_ap->GetNumSymbols(); Symbol eh_symbol( symbol_id, // Symbol table index. "???", // Symbol name. false, // Is the symbol name mangled? eSymbolTypeCode, // Type of this symbol. true, // Is this globally visible? false, // Is this symbol debug info? false, // Is this symbol a trampoline? true, // Is this symbol artificial? eh_sym_section_sp, // Section in which this symbol is defined or null. offset, // Offset in section or symbol value. range.GetByteSize(), // Size in bytes of this symbol. true, // Size is valid. false, // Contains linker annotations? 0); // Symbol flags. if (symbol_id == m_symtab_ap->AddSymbol(eh_symbol)) return m_symtab_ap->SymbolAtIndex(symbol_id); } } } return nullptr; } bool ObjectFileELF::IsStripped () { // TODO: determine this for ELF return false; } //===----------------------------------------------------------------------===// // Dump // // Dump the specifics of the runtime file container (such as any headers // segments, sections, etc). //---------------------------------------------------------------------- void ObjectFileELF::Dump(Stream *s) { DumpELFHeader(s, m_header); s->EOL(); DumpELFProgramHeaders(s); s->EOL(); DumpELFSectionHeaders(s); s->EOL(); SectionList *section_list = GetSectionList(); if (section_list) section_list->Dump(s, NULL, true, UINT32_MAX); Symtab *symtab = GetSymtab(); if (symtab) symtab->Dump(s, NULL, eSortOrderNone); s->EOL(); DumpDependentModules(s); s->EOL(); } //---------------------------------------------------------------------- // DumpELFHeader // // Dump the ELF header to the specified output stream //---------------------------------------------------------------------- void ObjectFileELF::DumpELFHeader(Stream *s, const ELFHeader &header) { s->PutCString("ELF Header\n"); s->Printf("e_ident[EI_MAG0 ] = 0x%2.2x\n", header.e_ident[EI_MAG0]); s->Printf("e_ident[EI_MAG1 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG1], header.e_ident[EI_MAG1]); s->Printf("e_ident[EI_MAG2 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG2], header.e_ident[EI_MAG2]); s->Printf("e_ident[EI_MAG3 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG3], header.e_ident[EI_MAG3]); s->Printf("e_ident[EI_CLASS ] = 0x%2.2x\n", header.e_ident[EI_CLASS]); s->Printf("e_ident[EI_DATA ] = 0x%2.2x ", header.e_ident[EI_DATA]); DumpELFHeader_e_ident_EI_DATA(s, header.e_ident[EI_DATA]); s->Printf ("\ne_ident[EI_VERSION] = 0x%2.2x\n", header.e_ident[EI_VERSION]); s->Printf ("e_ident[EI_PAD ] = 0x%2.2x\n", header.e_ident[EI_PAD]); s->Printf("e_type = 0x%4.4x ", header.e_type); DumpELFHeader_e_type(s, header.e_type); s->Printf("\ne_machine = 0x%4.4x\n", header.e_machine); s->Printf("e_version = 0x%8.8x\n", header.e_version); s->Printf("e_entry = 0x%8.8" PRIx64 "\n", header.e_entry); s->Printf("e_phoff = 0x%8.8" PRIx64 "\n", header.e_phoff); s->Printf("e_shoff = 0x%8.8" PRIx64 "\n", header.e_shoff); s->Printf("e_flags = 0x%8.8x\n", header.e_flags); s->Printf("e_ehsize = 0x%4.4x\n", header.e_ehsize); s->Printf("e_phentsize = 0x%4.4x\n", header.e_phentsize); s->Printf("e_phnum = 0x%4.4x\n", header.e_phnum); s->Printf("e_shentsize = 0x%4.4x\n", header.e_shentsize); s->Printf("e_shnum = 0x%4.4x\n", header.e_shnum); s->Printf("e_shstrndx = 0x%4.4x\n", header.e_shstrndx); } //---------------------------------------------------------------------- // DumpELFHeader_e_type // // Dump an token value for the ELF header member e_type //---------------------------------------------------------------------- void ObjectFileELF::DumpELFHeader_e_type(Stream *s, elf_half e_type) { switch (e_type) { case ET_NONE: *s << "ET_NONE"; break; case ET_REL: *s << "ET_REL"; break; case ET_EXEC: *s << "ET_EXEC"; break; case ET_DYN: *s << "ET_DYN"; break; case ET_CORE: *s << "ET_CORE"; break; default: break; } } //---------------------------------------------------------------------- // DumpELFHeader_e_ident_EI_DATA // // Dump an token value for the ELF header member e_ident[EI_DATA] //---------------------------------------------------------------------- void ObjectFileELF::DumpELFHeader_e_ident_EI_DATA(Stream *s, unsigned char ei_data) { switch (ei_data) { case ELFDATANONE: *s << "ELFDATANONE"; break; case ELFDATA2LSB: *s << "ELFDATA2LSB - Little Endian"; break; case ELFDATA2MSB: *s << "ELFDATA2MSB - Big Endian"; break; default: break; } } //---------------------------------------------------------------------- // DumpELFProgramHeader // // Dump a single ELF program header to the specified output stream //---------------------------------------------------------------------- void ObjectFileELF::DumpELFProgramHeader(Stream *s, const ELFProgramHeader &ph) { DumpELFProgramHeader_p_type(s, ph.p_type); s->Printf(" %8.8" PRIx64 " %8.8" PRIx64 " %8.8" PRIx64, ph.p_offset, ph.p_vaddr, ph.p_paddr); s->Printf(" %8.8" PRIx64 " %8.8" PRIx64 " %8.8x (", ph.p_filesz, ph.p_memsz, ph.p_flags); DumpELFProgramHeader_p_flags(s, ph.p_flags); s->Printf(") %8.8" PRIx64, ph.p_align); } //---------------------------------------------------------------------- // DumpELFProgramHeader_p_type // // Dump an token value for the ELF program header member p_type which // describes the type of the program header // ---------------------------------------------------------------------- void ObjectFileELF::DumpELFProgramHeader_p_type(Stream *s, elf_word p_type) { const int kStrWidth = 15; switch (p_type) { CASE_AND_STREAM(s, PT_NULL , kStrWidth); CASE_AND_STREAM(s, PT_LOAD , kStrWidth); CASE_AND_STREAM(s, PT_DYNAMIC , kStrWidth); CASE_AND_STREAM(s, PT_INTERP , kStrWidth); CASE_AND_STREAM(s, PT_NOTE , kStrWidth); CASE_AND_STREAM(s, PT_SHLIB , kStrWidth); CASE_AND_STREAM(s, PT_PHDR , kStrWidth); CASE_AND_STREAM(s, PT_TLS , kStrWidth); CASE_AND_STREAM(s, PT_GNU_EH_FRAME, kStrWidth); default: s->Printf("0x%8.8x%*s", p_type, kStrWidth - 10, ""); break; } } //---------------------------------------------------------------------- // DumpELFProgramHeader_p_flags // // Dump an token value for the ELF program header member p_flags //---------------------------------------------------------------------- void ObjectFileELF::DumpELFProgramHeader_p_flags(Stream *s, elf_word p_flags) { *s << ((p_flags & PF_X) ? "PF_X" : " ") << (((p_flags & PF_X) && (p_flags & PF_W)) ? '+' : ' ') << ((p_flags & PF_W) ? "PF_W" : " ") << (((p_flags & PF_W) && (p_flags & PF_R)) ? '+' : ' ') << ((p_flags & PF_R) ? "PF_R" : " "); } //---------------------------------------------------------------------- // DumpELFProgramHeaders // // Dump all of the ELF program header to the specified output stream //---------------------------------------------------------------------- void ObjectFileELF::DumpELFProgramHeaders(Stream *s) { if (!ParseProgramHeaders()) return; s->PutCString("Program Headers\n"); s->PutCString("IDX p_type p_offset p_vaddr p_paddr " "p_filesz p_memsz p_flags p_align\n"); s->PutCString("==== --------------- -------- -------- -------- " "-------- -------- ------------------------- --------\n"); uint32_t idx = 0; for (ProgramHeaderCollConstIter I = m_program_headers.begin(); I != m_program_headers.end(); ++I, ++idx) { s->Printf("[%2u] ", idx); ObjectFileELF::DumpELFProgramHeader(s, *I); s->EOL(); } } //---------------------------------------------------------------------- // DumpELFSectionHeader // // Dump a single ELF section header to the specified output stream //---------------------------------------------------------------------- void ObjectFileELF::DumpELFSectionHeader(Stream *s, const ELFSectionHeaderInfo &sh) { s->Printf("%8.8x ", sh.sh_name); DumpELFSectionHeader_sh_type(s, sh.sh_type); s->Printf(" %8.8" PRIx64 " (", sh.sh_flags); DumpELFSectionHeader_sh_flags(s, sh.sh_flags); s->Printf(") %8.8" PRIx64 " %8.8" PRIx64 " %8.8" PRIx64, sh.sh_addr, sh.sh_offset, sh.sh_size); s->Printf(" %8.8x %8.8x", sh.sh_link, sh.sh_info); s->Printf(" %8.8" PRIx64 " %8.8" PRIx64, sh.sh_addralign, sh.sh_entsize); } //---------------------------------------------------------------------- // DumpELFSectionHeader_sh_type // // Dump an token value for the ELF section header member sh_type which // describes the type of the section //---------------------------------------------------------------------- void ObjectFileELF::DumpELFSectionHeader_sh_type(Stream *s, elf_word sh_type) { const int kStrWidth = 12; switch (sh_type) { CASE_AND_STREAM(s, SHT_NULL , kStrWidth); CASE_AND_STREAM(s, SHT_PROGBITS , kStrWidth); CASE_AND_STREAM(s, SHT_SYMTAB , kStrWidth); CASE_AND_STREAM(s, SHT_STRTAB , kStrWidth); CASE_AND_STREAM(s, SHT_RELA , kStrWidth); CASE_AND_STREAM(s, SHT_HASH , kStrWidth); CASE_AND_STREAM(s, SHT_DYNAMIC , kStrWidth); CASE_AND_STREAM(s, SHT_NOTE , kStrWidth); CASE_AND_STREAM(s, SHT_NOBITS , kStrWidth); CASE_AND_STREAM(s, SHT_REL , kStrWidth); CASE_AND_STREAM(s, SHT_SHLIB , kStrWidth); CASE_AND_STREAM(s, SHT_DYNSYM , kStrWidth); CASE_AND_STREAM(s, SHT_LOPROC , kStrWidth); CASE_AND_STREAM(s, SHT_HIPROC , kStrWidth); CASE_AND_STREAM(s, SHT_LOUSER , kStrWidth); CASE_AND_STREAM(s, SHT_HIUSER , kStrWidth); default: s->Printf("0x%8.8x%*s", sh_type, kStrWidth - 10, ""); break; } } //---------------------------------------------------------------------- // DumpELFSectionHeader_sh_flags // // Dump an token value for the ELF section header member sh_flags //---------------------------------------------------------------------- void ObjectFileELF::DumpELFSectionHeader_sh_flags(Stream *s, elf_xword sh_flags) { *s << ((sh_flags & SHF_WRITE) ? "WRITE" : " ") << (((sh_flags & SHF_WRITE) && (sh_flags & SHF_ALLOC)) ? '+' : ' ') << ((sh_flags & SHF_ALLOC) ? "ALLOC" : " ") << (((sh_flags & SHF_ALLOC) && (sh_flags & SHF_EXECINSTR)) ? '+' : ' ') << ((sh_flags & SHF_EXECINSTR) ? "EXECINSTR" : " "); } //---------------------------------------------------------------------- // DumpELFSectionHeaders // // Dump all of the ELF section header to the specified output stream //---------------------------------------------------------------------- void ObjectFileELF::DumpELFSectionHeaders(Stream *s) { if (!ParseSectionHeaders()) return; s->PutCString("Section Headers\n"); s->PutCString("IDX name type flags " "addr offset size link info addralgn " "entsize Name\n"); s->PutCString("==== -------- ------------ -------------------------------- " "-------- -------- -------- -------- -------- -------- " "-------- ====================\n"); uint32_t idx = 0; for (SectionHeaderCollConstIter I = m_section_headers.begin(); I != m_section_headers.end(); ++I, ++idx) { s->Printf("[%2u] ", idx); ObjectFileELF::DumpELFSectionHeader(s, *I); const char* section_name = I->section_name.AsCString(""); if (section_name) *s << ' ' << section_name << "\n"; } } void ObjectFileELF::DumpDependentModules(lldb_private::Stream *s) { size_t num_modules = ParseDependentModules(); if (num_modules > 0) { s->PutCString("Dependent Modules:\n"); for (unsigned i = 0; i < num_modules; ++i) { const FileSpec &spec = m_filespec_ap->GetFileSpecAtIndex(i); s->Printf(" %s\n", spec.GetFilename().GetCString()); } } } bool ObjectFileELF::GetArchitecture (ArchSpec &arch) { if (!ParseHeader()) return false; if (m_section_headers.empty()) { // Allow elf notes to be parsed which may affect the detected architecture. ParseSectionHeaders(); } if (CalculateType() == eTypeCoreFile && m_arch_spec.TripleOSIsUnspecifiedUnknown()) { // Core files don't have section headers yet they have PT_NOTE program headers // that might shed more light on the architecture if (ParseProgramHeaders()) { for (size_t i = 0, count = GetProgramHeaderCount(); i < count; ++i) { const elf::ELFProgramHeader* header = GetProgramHeaderByIndex(i); if (header && header->p_type == PT_NOTE && header->p_offset != 0 && header->p_filesz > 0) { DataExtractor data; if (data.SetData (m_data, header->p_offset, header->p_filesz) == header->p_filesz) { lldb_private::UUID uuid; RefineModuleDetailsFromNote (data, m_arch_spec, uuid); } } } } } arch = m_arch_spec; return true; } ObjectFile::Type ObjectFileELF::CalculateType() { switch (m_header.e_type) { case llvm::ELF::ET_NONE: // 0 - No file type return eTypeUnknown; case llvm::ELF::ET_REL: // 1 - Relocatable file return eTypeObjectFile; case llvm::ELF::ET_EXEC: // 2 - Executable file return eTypeExecutable; case llvm::ELF::ET_DYN: // 3 - Shared object file return eTypeSharedLibrary; case ET_CORE: // 4 - Core file return eTypeCoreFile; default: break; } return eTypeUnknown; } ObjectFile::Strata ObjectFileELF::CalculateStrata() { switch (m_header.e_type) { - case llvm::ELF::ET_NONE: + case llvm::ELF::ET_NONE: // 0 - No file type return eStrataUnknown; case llvm::ELF::ET_REL: // 1 - Relocatable file return eStrataUnknown; case llvm::ELF::ET_EXEC: // 2 - Executable file // TODO: is there any way to detect that an executable is a kernel - // related executable by inspecting the program headers, section + // related executable by inspecting the program headers, section // headers, symbols, or any other flag bits??? return eStrataUser; case llvm::ELF::ET_DYN: // 3 - Shared object file // TODO: is there any way to detect that an shared library is a kernel - // related executable by inspecting the program headers, section + // related executable by inspecting the program headers, section // headers, symbols, or any other flag bits??? return eStrataUnknown; case ET_CORE: // 4 - Core file // TODO: is there any way to detect that an core file is a kernel - // related executable by inspecting the program headers, section + // related executable by inspecting the program headers, section // headers, symbols, or any other flag bits??? return eStrataUnknown; default: break; } return eStrataUnknown; } Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h (revision 293283) @@ -1,249 +1,249 @@ //===-- ProcessFreeBSD.h ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_ProcessFreeBSD_H_ #define liblldb_ProcessFreeBSD_H_ // C Includes // C++ Includes #include #include // Other libraries and framework includes #include "lldb/Target/Process.h" #include "lldb/Target/ThreadList.h" #include "Plugins/Process/POSIX/ProcessMessage.h" #include "ProcessFreeBSD.h" class ProcessMonitor; class FreeBSDThread; class ProcessFreeBSD : public lldb_private::Process { public: //------------------------------------------------------------------ // Static functions. //------------------------------------------------------------------ static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, lldb_private::Listener &listener, const lldb_private::FileSpec *crash_file_path); static void Initialize(); static void Terminate(); static lldb_private::ConstString GetPluginNameStatic(); static const char * GetPluginDescriptionStatic(); //------------------------------------------------------------------ // Constructors and destructors //------------------------------------------------------------------ ProcessFreeBSD(lldb::TargetSP target_sp, lldb_private::Listener &listener, lldb::UnixSignalsSP &unix_signals_sp); ~ProcessFreeBSD(); virtual lldb_private::Error WillResume() override; //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ virtual lldb_private::ConstString GetPluginName() override; virtual uint32_t GetPluginVersion() override; public: //------------------------------------------------------------------ // Process protocol. //------------------------------------------------------------------ void Finalize() override; bool CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override; lldb_private::Error WillLaunch(lldb_private::Module *module) override; lldb_private::Error DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info) override; lldb_private::Error - DoLaunch (lldb_private::Module *exe_module, + DoLaunch (lldb_private::Module *exe_module, lldb_private::ProcessLaunchInfo &launch_info) override; void DidLaunch() override; lldb_private::Error DoResume() override; lldb_private::Error DoHalt(bool &caused_stop) override; lldb_private::Error DoDetach(bool keep_stopped) override; lldb_private::Error DoSignal(int signal) override; lldb_private::Error DoDestroy() override; void DoDidExec() override; void RefreshStateAfterStop() override; bool IsAlive() override; size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, lldb_private::Error &error) override; size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, lldb_private::Error &error) override; lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions, lldb_private::Error &error) override; lldb_private::Error DoDeallocateMemory(lldb::addr_t ptr) override; virtual size_t GetSoftwareBreakpointTrapOpcode(lldb_private::BreakpointSite* bp_site); lldb_private::Error EnableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; lldb_private::Error DisableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; lldb_private::Error EnableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true) override; lldb_private::Error DisableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true) override; lldb_private::Error GetWatchpointSupportInfo(uint32_t &num) override; lldb_private::Error GetWatchpointSupportInfo(uint32_t &num, bool &after) override; virtual uint32_t UpdateThreadListIfNeeded(); bool - UpdateThreadList(lldb_private::ThreadList &old_thread_list, + UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) override; virtual lldb::ByteOrder GetByteOrder() const; lldb::addr_t GetImageInfoAddress() override; size_t PutSTDIN(const char *buf, size_t len, lldb_private::Error &error) override; const lldb::DataBufferSP GetAuxvData () override; //-------------------------------------------------------------------------- // ProcessFreeBSD internal API. /// Registers the given message with this process. virtual void SendMessage(const ProcessMessage &message); ProcessMonitor & GetMonitor() { assert(m_monitor); return *m_monitor; } lldb_private::FileSpec GetFileSpec(const lldb_private::FileAction *file_action, const lldb_private::FileSpec &default_file_spec, const lldb_private::FileSpec &dbg_pts_file_spec); /// Adds the thread to the list of threads for which we have received the initial stopping signal. /// The \p stop_tid parameter indicates the thread which the stop happened for. bool AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid); bool WaitingForInitialStop(lldb::tid_t stop_tid); virtual FreeBSDThread * CreateNewFreeBSDThread(lldb_private::Process &process, lldb::tid_t tid); protected: /// Target byte order. lldb::ByteOrder m_byte_order; /// Process monitor; ProcessMonitor *m_monitor; /// The module we are executing. lldb_private::Module *m_module; /// Message queue notifying this instance of inferior process state changes. lldb_private::Mutex m_message_mutex; std::queue m_message_queue; /// Drive any exit events to completion. bool m_exit_now; /// Returns true if the process has exited. bool HasExited(); /// Returns true if the process is stopped. bool IsStopped(); /// Returns true if at least one running is currently running bool IsAThreadRunning(); typedef std::map MMapMap; MMapMap m_addr_to_mmap_size; typedef std::set ThreadStopSet; /// Every thread begins with a stop signal. This keeps track /// of the threads for which we have received the stop signal. ThreadStopSet m_seen_initial_stop; friend class FreeBSDThread; typedef std::vector tid_collection; tid_collection m_suspend_tids; tid_collection m_run_tids; tid_collection m_step_tids; int m_resume_signo; }; #endif // liblldb_ProcessFreeBSD_H_ Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp (revision 293283) @@ -1,1626 +1,1626 @@ //===-- ProcessMonitor.cpp ------------------------------------ -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes #include #include #include #include #include #include #include #include #include #include // C++ Includes // Other libraries and framework includes #include "lldb/Core/Error.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Scalar.h" #include "lldb/Host/Host.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Target/Thread.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/PseudoTerminal.h" #include "Plugins/Process/POSIX/CrashReason.h" #include "FreeBSDThread.h" #include "ProcessFreeBSD.h" #include "ProcessPOSIXLog.h" #include "ProcessMonitor.h" extern "C" { extern char ** environ; } using namespace lldb; using namespace lldb_private; // We disable the tracing of ptrace calls for integration builds to // avoid the additional indirection and checks. #ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION // Wrapper for ptrace to catch errors and log calls. const char * Get_PT_IO_OP(int op) { switch (op) { case PIOD_READ_D: return "READ_D"; case PIOD_WRITE_D: return "WRITE_D"; case PIOD_READ_I: return "READ_I"; case PIOD_WRITE_I: return "WRITE_I"; default: return "Unknown op"; } } // Wrapper for ptrace to catch errors and log calls. // Note that ptrace sets errno on error because -1 is reserved as a valid result. extern long PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data, const char* reqName, const char* file, int line) { long int result; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); if (log) { log->Printf("ptrace(%s, %" PRIu64 ", %p, %x) called from file %s line %d", reqName, pid, addr, data, file, line); if (req == PT_IO) { struct ptrace_io_desc *pi = (struct ptrace_io_desc *) addr; log->Printf("PT_IO: op=%s offs=%zx size=%zu", Get_PT_IO_OP(pi->piod_op), (size_t)pi->piod_offs, pi->piod_len); } } //PtraceDisplayBytes(req, data); errno = 0; result = ptrace(req, pid, (caddr_t) addr, data); //PtraceDisplayBytes(req, data); if (log && errno != 0) { const char* str; switch (errno) { case ESRCH: str = "ESRCH"; break; case EINVAL: str = "EINVAL"; break; case EBUSY: str = "EBUSY"; break; case EPERM: str = "EPERM"; break; default: str = ""; } log->Printf("ptrace() failed; errno=%d (%s)", errno, str); } if (log) { #ifdef __amd64__ if (req == PT_GETREGS) { struct reg *r = (struct reg *) addr; log->Printf("PT_GETREGS: rip=0x%lx rsp=0x%lx rbp=0x%lx rax=0x%lx", r->r_rip, r->r_rsp, r->r_rbp, r->r_rax); } if (req == PT_GETDBREGS || req == PT_SETDBREGS) { struct dbreg *r = (struct dbreg *) addr; char setget = (req == PT_GETDBREGS) ? 'G' : 'S'; for (int i = 0; i <= 7; i++) log->Printf("PT_%cETDBREGS: dr[%d]=0x%lx", setget, i, r->dr[i]); } #endif } return result; } // Wrapper for ptrace when logging is not required. // Sets errno to 0 prior to calling ptrace. extern long PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data) { long result = 0; errno = 0; result = ptrace(req, pid, (caddr_t)addr, data); return result; } #define PTRACE(req, pid, addr, data) \ PtraceWrapper((req), (pid), (addr), (data), #req, __FILE__, __LINE__) #else PtraceWrapper((req), (pid), (addr), (data)) #endif //------------------------------------------------------------------------------ // Static implementations of ProcessMonitor::ReadMemory and // ProcessMonitor::WriteMemory. This enables mutual recursion between these // functions without needed to go thru the thread funnel. static size_t DoReadMemory(lldb::pid_t pid, lldb::addr_t vm_addr, void *buf, size_t size, Error &error) { struct ptrace_io_desc pi_desc; pi_desc.piod_op = PIOD_READ_D; pi_desc.piod_offs = (void *)vm_addr; pi_desc.piod_addr = buf; pi_desc.piod_len = size; if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) error.SetErrorToErrno(); return pi_desc.piod_len; } static size_t DoWriteMemory(lldb::pid_t pid, lldb::addr_t vm_addr, const void *buf, size_t size, Error &error) { struct ptrace_io_desc pi_desc; pi_desc.piod_op = PIOD_WRITE_D; pi_desc.piod_offs = (void *)vm_addr; pi_desc.piod_addr = (void *)buf; pi_desc.piod_len = size; if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) error.SetErrorToErrno(); return pi_desc.piod_len; } // Simple helper function to ensure flags are enabled on the given file // descriptor. static bool EnsureFDFlags(int fd, int flags, Error &error) { int status; if ((status = fcntl(fd, F_GETFL)) == -1) { error.SetErrorToErrno(); return false; } if (fcntl(fd, F_SETFL, status | flags) == -1) { error.SetErrorToErrno(); return false; } return true; } //------------------------------------------------------------------------------ /// @class Operation /// @brief Represents a ProcessMonitor operation. /// /// Under FreeBSD, it is not possible to ptrace() from any other thread but the /// one that spawned or attached to the process from the start. Therefore, when /// a ProcessMonitor is asked to deliver or change the state of an inferior /// process the operation must be "funneled" to a specific thread to perform the /// task. The Operation class provides an abstract base for all services the /// ProcessMonitor must perform via the single virtual function Execute, thus /// encapsulating the code that needs to run in the privileged context. class Operation { public: virtual ~Operation() {} virtual void Execute(ProcessMonitor *monitor) = 0; }; //------------------------------------------------------------------------------ /// @class ReadOperation /// @brief Implements ProcessMonitor::ReadMemory. class ReadOperation : public Operation { public: ReadOperation(lldb::addr_t addr, void *buff, size_t size, Error &error, size_t &result) : m_addr(addr), m_buff(buff), m_size(size), m_error(error), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::addr_t m_addr; void *m_buff; size_t m_size; Error &m_error; size_t &m_result; }; void ReadOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); m_result = DoReadMemory(pid, m_addr, m_buff, m_size, m_error); } //------------------------------------------------------------------------------ /// @class WriteOperation /// @brief Implements ProcessMonitor::WriteMemory. class WriteOperation : public Operation { public: WriteOperation(lldb::addr_t addr, const void *buff, size_t size, Error &error, size_t &result) : m_addr(addr), m_buff(buff), m_size(size), m_error(error), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::addr_t m_addr; const void *m_buff; size_t m_size; Error &m_error; size_t &m_result; }; void WriteOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); m_result = DoWriteMemory(pid, m_addr, m_buff, m_size, m_error); } //------------------------------------------------------------------------------ /// @class ReadRegOperation /// @brief Implements ProcessMonitor::ReadRegisterValue. class ReadRegOperation : public Operation { public: ReadRegOperation(lldb::tid_t tid, unsigned offset, unsigned size, RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_size(size), m_value(value), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; unsigned m_size; RegisterValue &m_value; bool &m_result; }; void ReadRegOperation::Execute(ProcessMonitor *monitor) { struct reg regs; int rc; if ((rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)®s, 0)) < 0) { m_result = false; } else { // 'struct reg' contains only 32- or 64-bit register values. Punt on // others. Also, not all entries may be uintptr_t sized, such as 32-bit // processes on powerpc64 (probably the same for i386 on amd64) if (m_size == sizeof(uint32_t)) m_value = *(uint32_t *)(((caddr_t)®s) + m_offset); else if (m_size == sizeof(uint64_t)) m_value = *(uint64_t *)(((caddr_t)®s) + m_offset); else - memcpy(&m_value, (((caddr_t)®s) + m_offset), m_size); + memcpy((void *)&m_value, (((caddr_t)®s) + m_offset), m_size); m_result = true; } } //------------------------------------------------------------------------------ /// @class WriteRegOperation /// @brief Implements ProcessMonitor::WriteRegisterValue. class WriteRegOperation : public Operation { public: WriteRegOperation(lldb::tid_t tid, unsigned offset, const RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_value(value), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; const RegisterValue &m_value; bool &m_result; }; void WriteRegOperation::Execute(ProcessMonitor *monitor) { struct reg regs; if (PTRACE(PT_GETREGS, m_tid, (caddr_t)®s, 0) < 0) { m_result = false; return; } *(uintptr_t *)(((caddr_t)®s) + m_offset) = (uintptr_t)m_value.GetAsUInt64(); if (PTRACE(PT_SETREGS, m_tid, (caddr_t)®s, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ReadDebugRegOperation /// @brief Implements ProcessMonitor::ReadDebugRegisterValue. class ReadDebugRegOperation : public Operation { public: ReadDebugRegOperation(lldb::tid_t tid, unsigned offset, unsigned size, RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_size(size), m_value(value), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; unsigned m_size; RegisterValue &m_value; bool &m_result; }; void ReadDebugRegOperation::Execute(ProcessMonitor *monitor) { struct dbreg regs; int rc; if ((rc = PTRACE(PT_GETDBREGS, m_tid, (caddr_t)®s, 0)) < 0) { m_result = false; } else { if (m_size == sizeof(uintptr_t)) m_value = *(uintptr_t *)(((caddr_t)®s) + m_offset); else - memcpy(&m_value, (((caddr_t)®s) + m_offset), m_size); + memcpy((void *)&m_value, (((caddr_t)®s) + m_offset), m_size); m_result = true; } } //------------------------------------------------------------------------------ /// @class WriteDebugRegOperation /// @brief Implements ProcessMonitor::WriteDebugRegisterValue. class WriteDebugRegOperation : public Operation { public: WriteDebugRegOperation(lldb::tid_t tid, unsigned offset, const RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_value(value), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; const RegisterValue &m_value; bool &m_result; }; void WriteDebugRegOperation::Execute(ProcessMonitor *monitor) { struct dbreg regs; if (PTRACE(PT_GETDBREGS, m_tid, (caddr_t)®s, 0) < 0) { m_result = false; return; } *(uintptr_t *)(((caddr_t)®s) + m_offset) = (uintptr_t)m_value.GetAsUInt64(); if (PTRACE(PT_SETDBREGS, m_tid, (caddr_t)®s, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ReadGPROperation /// @brief Implements ProcessMonitor::ReadGPR. class ReadGPROperation : public Operation { public: ReadGPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void ReadGPROperation::Execute(ProcessMonitor *monitor) { int rc; errno = 0; rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)m_buf, 0); if (errno != 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ReadFPROperation /// @brief Implements ProcessMonitor::ReadFPR. class ReadFPROperation : public Operation { public: ReadFPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void ReadFPROperation::Execute(ProcessMonitor *monitor) { if (PTRACE(PT_GETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class WriteGPROperation /// @brief Implements ProcessMonitor::WriteGPR. class WriteGPROperation : public Operation { public: WriteGPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void WriteGPROperation::Execute(ProcessMonitor *monitor) { if (PTRACE(PT_SETREGS, m_tid, (caddr_t)m_buf, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class WriteFPROperation /// @brief Implements ProcessMonitor::WriteFPR. class WriteFPROperation : public Operation { public: WriteFPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void WriteFPROperation::Execute(ProcessMonitor *monitor) { if (PTRACE(PT_SETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ResumeOperation /// @brief Implements ProcessMonitor::Resume. class ResumeOperation : public Operation { public: ResumeOperation(uint32_t signo, bool &result) : m_signo(signo), m_result(result) { } void Execute(ProcessMonitor *monitor); private: uint32_t m_signo; bool &m_result; }; void ResumeOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); int data = 0; if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) data = m_signo; if (PTRACE(PT_CONTINUE, pid, (caddr_t)1, data)) { Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); if (log) log->Printf ("ResumeOperation (%" PRIu64 ") failed: %s", pid, strerror(errno)); m_result = false; } else m_result = true; } //------------------------------------------------------------------------------ /// @class SingleStepOperation /// @brief Implements ProcessMonitor::SingleStep. class SingleStepOperation : public Operation { public: SingleStepOperation(uint32_t signo, bool &result) : m_signo(signo), m_result(result) { } void Execute(ProcessMonitor *monitor); private: uint32_t m_signo; bool &m_result; }; void SingleStepOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); int data = 0; if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) data = m_signo; if (PTRACE(PT_STEP, pid, NULL, data)) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class LwpInfoOperation /// @brief Implements ProcessMonitor::GetLwpInfo. class LwpInfoOperation : public Operation { public: LwpInfoOperation(lldb::tid_t tid, void *info, bool &result, int &ptrace_err) : m_tid(tid), m_info(info), m_result(result), m_err(ptrace_err) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_info; bool &m_result; int &m_err; }; void LwpInfoOperation::Execute(ProcessMonitor *monitor) { struct ptrace_lwpinfo plwp; if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) { m_result = false; m_err = errno; } else { memcpy(m_info, &plwp, sizeof(plwp)); m_result = true; } } //------------------------------------------------------------------------------ /// @class ThreadSuspendOperation /// @brief Implements ProcessMonitor::ThreadSuspend. class ThreadSuspendOperation : public Operation { public: ThreadSuspendOperation(lldb::tid_t tid, bool suspend, bool &result) : m_tid(tid), m_suspend(suspend), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; bool m_suspend; bool &m_result; } ; void ThreadSuspendOperation::Execute(ProcessMonitor *monitor) { m_result = !PTRACE(m_suspend ? PT_SUSPEND : PT_RESUME, m_tid, NULL, 0); } //------------------------------------------------------------------------------ /// @class EventMessageOperation /// @brief Implements ProcessMonitor::GetEventMessage. class EventMessageOperation : public Operation { public: EventMessageOperation(lldb::tid_t tid, unsigned long *message, bool &result) : m_tid(tid), m_message(message), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned long *m_message; bool &m_result; }; void EventMessageOperation::Execute(ProcessMonitor *monitor) { struct ptrace_lwpinfo plwp; if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) m_result = false; else { if (plwp.pl_flags & PL_FLAG_FORKED) { *m_message = plwp.pl_child_pid; m_result = true; } else m_result = false; } } //------------------------------------------------------------------------------ /// @class KillOperation /// @brief Implements ProcessMonitor::Kill. class KillOperation : public Operation { public: KillOperation(bool &result) : m_result(result) { } void Execute(ProcessMonitor *monitor); private: bool &m_result; }; void KillOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); if (PTRACE(PT_KILL, pid, NULL, 0)) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class DetachOperation /// @brief Implements ProcessMonitor::Detach. class DetachOperation : public Operation { public: DetachOperation(Error &result) : m_error(result) { } void Execute(ProcessMonitor *monitor); private: Error &m_error; }; void DetachOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); if (PTRACE(PT_DETACH, pid, NULL, 0) < 0) m_error.SetErrorToErrno(); } ProcessMonitor::OperationArgs::OperationArgs(ProcessMonitor *monitor) : m_monitor(monitor) { sem_init(&m_semaphore, 0, 0); } ProcessMonitor::OperationArgs::~OperationArgs() { sem_destroy(&m_semaphore); } ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor, lldb_private::Module *module, char const **argv, char const **envp, const FileSpec &stdin_file_spec, const FileSpec &stdout_file_spec, const FileSpec &stderr_file_spec, const FileSpec &working_dir) : OperationArgs(monitor), m_module(module), m_argv(argv), m_envp(envp), m_stdin_file_spec(stdin_file_spec), m_stdout_file_spec(stdout_file_spec), m_stderr_file_spec(stderr_file_spec), m_working_dir(working_dir) { } ProcessMonitor::LaunchArgs::~LaunchArgs() { } ProcessMonitor::AttachArgs::AttachArgs(ProcessMonitor *monitor, lldb::pid_t pid) : OperationArgs(monitor), m_pid(pid) { } ProcessMonitor::AttachArgs::~AttachArgs() { } //------------------------------------------------------------------------------ /// The basic design of the ProcessMonitor is built around two threads. /// /// One thread (@see SignalThread) simply blocks on a call to waitpid() looking /// for changes in the debugee state. When a change is detected a /// ProcessMessage is sent to the associated ProcessFreeBSD instance. This thread /// "drives" state changes in the debugger. /// /// The second thread (@see OperationThread) is responsible for two things 1) /// launching or attaching to the inferior process, and then 2) servicing /// operations such as register reads/writes, stepping, etc. See the comments /// on the Operation class for more info as to why this is needed. ProcessMonitor::ProcessMonitor(ProcessFreeBSD *process, Module *module, const char *argv[], const char *envp[], const FileSpec &stdin_file_spec, const FileSpec &stdout_file_spec, const FileSpec &stderr_file_spec, const FileSpec &working_dir, const lldb_private::ProcessLaunchInfo & /* launch_info */, lldb_private::Error &error) : m_process(static_cast(process)), m_pid(LLDB_INVALID_PROCESS_ID), m_terminal_fd(-1), m_operation(0) { std::unique_ptr args(new LaunchArgs(this, module, argv, envp, stdin_file_spec, stdout_file_spec, stderr_file_spec, working_dir)); sem_init(&m_operation_pending, 0, 0); sem_init(&m_operation_done, 0, 0); StartLaunchOpThread(args.get(), error); if (!error.Success()) return; WAIT_AGAIN: // Wait for the operation thread to initialize. if (sem_wait(&args->m_semaphore)) { if (errno == EINTR) goto WAIT_AGAIN; else { error.SetErrorToErrno(); return; } } // Check that the launch was a success. if (!args->m_error.Success()) { StopOpThread(); error = args->m_error; return; } // Finally, start monitoring the child process for change in state. m_monitor_thread = Host::StartMonitoringChildProcess( ProcessMonitor::MonitorCallback, this, GetPID(), true); if (!m_monitor_thread.IsJoinable()) { error.SetErrorToGenericError(); error.SetErrorString("Process launch failed."); return; } } ProcessMonitor::ProcessMonitor(ProcessFreeBSD *process, lldb::pid_t pid, lldb_private::Error &error) : m_process(static_cast(process)), m_pid(pid), m_terminal_fd(-1), m_operation(0) { sem_init(&m_operation_pending, 0, 0); sem_init(&m_operation_done, 0, 0); std::unique_ptr args(new AttachArgs(this, pid)); StartAttachOpThread(args.get(), error); if (!error.Success()) return; WAIT_AGAIN: // Wait for the operation thread to initialize. if (sem_wait(&args->m_semaphore)) { if (errno == EINTR) goto WAIT_AGAIN; else { error.SetErrorToErrno(); return; } } // Check that the attach was a success. if (!args->m_error.Success()) { StopOpThread(); error = args->m_error; return; } // Finally, start monitoring the child process for change in state. m_monitor_thread = Host::StartMonitoringChildProcess( ProcessMonitor::MonitorCallback, this, GetPID(), true); if (!m_monitor_thread.IsJoinable()) { error.SetErrorToGenericError(); error.SetErrorString("Process attach failed."); return; } } ProcessMonitor::~ProcessMonitor() { StopMonitor(); } //------------------------------------------------------------------------------ // Thread setup and tear down. void ProcessMonitor::StartLaunchOpThread(LaunchArgs *args, Error &error) { static const char *g_thread_name = "lldb.process.freebsd.operation"; if (m_operation_thread.IsJoinable()) return; m_operation_thread = ThreadLauncher::LaunchThread(g_thread_name, LaunchOpThread, args, &error); } void * ProcessMonitor::LaunchOpThread(void *arg) { LaunchArgs *args = static_cast(arg); if (!Launch(args)) { sem_post(&args->m_semaphore); return NULL; } ServeOperation(args); return NULL; } bool ProcessMonitor::Launch(LaunchArgs *args) { ProcessMonitor *monitor = args->m_monitor; ProcessFreeBSD &process = monitor->GetProcess(); const char **argv = args->m_argv; const char **envp = args->m_envp; const FileSpec &stdin_file_spec = args->m_stdin_file_spec; const FileSpec &stdout_file_spec = args->m_stdout_file_spec; const FileSpec &stderr_file_spec = args->m_stderr_file_spec; const FileSpec &working_dir = args->m_working_dir; lldb_utility::PseudoTerminal terminal; const size_t err_len = 1024; char err_str[err_len]; ::pid_t pid; // Propagate the environment if one is not supplied. if (envp == NULL || envp[0] == NULL) envp = const_cast(environ); if ((pid = terminal.Fork(err_str, err_len)) == -1) { args->m_error.SetErrorToGenericError(); args->m_error.SetErrorString("Process fork failed."); goto FINISH; } // Recognized child exit status codes. enum { ePtraceFailed = 1, eDupStdinFailed, eDupStdoutFailed, eDupStderrFailed, eChdirFailed, eExecFailed, eSetGidFailed }; // Child process. if (pid == 0) { // Trace this process. if (PTRACE(PT_TRACE_ME, 0, NULL, 0) < 0) exit(ePtraceFailed); // terminal has already dupped the tty descriptors to stdin/out/err. // This closes original fd from which they were copied (and avoids // leaking descriptors to the debugged process. terminal.CloseSlaveFileDescriptor(); // Do not inherit setgid powers. if (setgid(getgid()) != 0) exit(eSetGidFailed); // Let us have our own process group. setpgid(0, 0); // Dup file descriptors if needed. // // FIXME: If two or more of the paths are the same we needlessly open // the same file multiple times. if (stdin_file_spec) if (!DupDescriptor(stdin_file_spec, STDIN_FILENO, O_RDONLY)) exit(eDupStdinFailed); if (stdout_file_spec) if (!DupDescriptor(stdout_file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT)) exit(eDupStdoutFailed); if (stderr_file_spec) if (!DupDescriptor(stderr_file_spec, STDERR_FILENO, O_WRONLY | O_CREAT)) exit(eDupStderrFailed); // Change working directory if (working_dir && 0 != ::chdir(working_dir.GetCString())) exit(eChdirFailed); // Execute. We should never return. execve(argv[0], const_cast(argv), const_cast(envp)); exit(eExecFailed); } // Wait for the child process to to trap on its call to execve. ::pid_t wpid; int status; if ((wpid = waitpid(pid, &status, 0)) < 0) { args->m_error.SetErrorToErrno(); goto FINISH; } else if (WIFEXITED(status)) { // open, dup or execve likely failed for some reason. args->m_error.SetErrorToGenericError(); switch (WEXITSTATUS(status)) { case ePtraceFailed: args->m_error.SetErrorString("Child ptrace failed."); break; case eDupStdinFailed: args->m_error.SetErrorString("Child open stdin failed."); break; case eDupStdoutFailed: args->m_error.SetErrorString("Child open stdout failed."); break; case eDupStderrFailed: args->m_error.SetErrorString("Child open stderr failed."); break; case eChdirFailed: args->m_error.SetErrorString("Child failed to set working directory."); break; case eExecFailed: args->m_error.SetErrorString("Child exec failed."); break; case eSetGidFailed: args->m_error.SetErrorString("Child setgid failed."); break; default: args->m_error.SetErrorString("Child returned unknown exit status."); break; } goto FINISH; } assert(WIFSTOPPED(status) && wpid == (::pid_t)pid && "Could not sync with inferior process."); #ifdef notyet // Have the child raise an event on exit. This is used to keep the child in // limbo until it is destroyed. if (PTRACE(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEEXIT) < 0) { args->m_error.SetErrorToErrno(); goto FINISH; } #endif // Release the master terminal descriptor and pass it off to the // ProcessMonitor instance. Similarly stash the inferior pid. monitor->m_terminal_fd = terminal.ReleaseMasterFileDescriptor(); monitor->m_pid = pid; // Set the terminal fd to be in non blocking mode (it simplifies the // implementation of ProcessFreeBSD::GetSTDOUT to have a non-blocking // descriptor to read from). if (!EnsureFDFlags(monitor->m_terminal_fd, O_NONBLOCK, args->m_error)) goto FINISH; process.SendMessage(ProcessMessage::Attach(pid)); FINISH: return args->m_error.Success(); } void ProcessMonitor::StartAttachOpThread(AttachArgs *args, lldb_private::Error &error) { static const char *g_thread_name = "lldb.process.freebsd.operation"; if (m_operation_thread.IsJoinable()) return; m_operation_thread = ThreadLauncher::LaunchThread(g_thread_name, AttachOpThread, args, &error); } void * ProcessMonitor::AttachOpThread(void *arg) { AttachArgs *args = static_cast(arg); Attach(args); ServeOperation(args); return NULL; } void ProcessMonitor::Attach(AttachArgs *args) { lldb::pid_t pid = args->m_pid; ProcessMonitor *monitor = args->m_monitor; ProcessFreeBSD &process = monitor->GetProcess(); if (pid <= 1) { args->m_error.SetErrorToGenericError(); args->m_error.SetErrorString("Attaching to process 1 is not allowed."); return; } // Attach to the requested process. if (PTRACE(PT_ATTACH, pid, NULL, 0) < 0) { args->m_error.SetErrorToErrno(); return; } int status; if ((status = waitpid(pid, NULL, 0)) < 0) { args->m_error.SetErrorToErrno(); return; } process.SendMessage(ProcessMessage::Attach(pid)); } size_t ProcessMonitor::GetCurrentThreadIDs(std::vector&thread_ids) { lwpid_t *tids; int tdcnt; thread_ids.clear(); tdcnt = PTRACE(PT_GETNUMLWPS, m_pid, NULL, 0); if (tdcnt <= 0) return 0; tids = (lwpid_t *)malloc(tdcnt * sizeof(*tids)); if (tids == NULL) return 0; if (PTRACE(PT_GETLWPLIST, m_pid, (void *)tids, tdcnt) < 0) { free(tids); return 0; } thread_ids = std::vector(tids, tids + tdcnt); free(tids); return thread_ids.size(); } bool ProcessMonitor::MonitorCallback(void *callback_baton, lldb::pid_t pid, bool exited, int signal, int status) { ProcessMessage message; ProcessMonitor *monitor = static_cast(callback_baton); ProcessFreeBSD *process = monitor->m_process; assert(process); bool stop_monitoring; struct ptrace_lwpinfo plwp; int ptrace_err; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); if (exited) { if (log) log->Printf ("ProcessMonitor::%s() got exit signal, tid = %" PRIu64, __FUNCTION__, pid); message = ProcessMessage::Exit(pid, status); process->SendMessage(message); return pid == process->GetID(); } if (!monitor->GetLwpInfo(pid, &plwp, ptrace_err)) stop_monitoring = true; // pid is gone. Bail. else { switch (plwp.pl_siginfo.si_signo) { case SIGTRAP: message = MonitorSIGTRAP(monitor, &plwp.pl_siginfo, plwp.pl_lwpid); break; default: message = MonitorSignal(monitor, &plwp.pl_siginfo, plwp.pl_lwpid); break; } process->SendMessage(message); stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage; } return stop_monitoring; } ProcessMessage ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, const siginfo_t *info, lldb::tid_t tid) { ProcessMessage message; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); assert(monitor); assert(info && info->si_signo == SIGTRAP && "Unexpected child signal!"); switch (info->si_code) { default: assert(false && "Unexpected SIGTRAP code!"); break; case (SIGTRAP /* | (PTRACE_EVENT_EXIT << 8) */): { // The inferior process is about to exit. Maintain the process in a // state of "limbo" until we are explicitly commanded to detach, // destroy, resume, etc. unsigned long data = 0; if (!monitor->GetEventMessage(tid, &data)) data = -1; if (log) log->Printf ("ProcessMonitor::%s() received exit? event, data = %lx, tid = %" PRIu64, __FUNCTION__, data, tid); message = ProcessMessage::Limbo(tid, (data >> 8)); break; } case 0: case TRAP_TRACE: if (log) log->Printf ("ProcessMonitor::%s() received trace event, tid = %" PRIu64 " : si_code = %d", __FUNCTION__, tid, info->si_code); message = ProcessMessage::Trace(tid); break; case SI_KERNEL: case TRAP_BRKPT: if (log) log->Printf ("ProcessMonitor::%s() received breakpoint event, tid = %" PRIu64, __FUNCTION__, tid); message = ProcessMessage::Break(tid); break; } return message; } ProcessMessage ProcessMonitor::MonitorSignal(ProcessMonitor *monitor, const siginfo_t *info, lldb::tid_t tid) { ProcessMessage message; int signo = info->si_signo; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); // POSIX says that process behaviour is undefined after it ignores a SIGFPE, // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a // kill(2) or raise(3). Similarly for tgkill(2) on FreeBSD. // // IOW, user generated signals never generate what we consider to be a // "crash". // // Similarly, ACK signals generated by this monitor. if (info->si_code == SI_USER) { if (log) log->Printf ("ProcessMonitor::%s() received signal %s with code %s, pid = %d", __FUNCTION__, monitor->m_process->GetUnixSignals()->GetSignalAsCString (signo), "SI_USER", info->si_pid); if (info->si_pid == getpid()) return ProcessMessage::SignalDelivered(tid, signo); else return ProcessMessage::Signal(tid, signo); } if (log) log->Printf ("ProcessMonitor::%s() received signal %s", __FUNCTION__, monitor->m_process->GetUnixSignals()->GetSignalAsCString (signo)); switch (signo) { case SIGSEGV: case SIGILL: case SIGFPE: case SIGBUS: lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); const auto reason = GetCrashReason(*info); return ProcessMessage::Crash(tid, reason, signo, fault_addr); } // Everything else is "normal" and does not require any special action on // our part. return ProcessMessage::Signal(tid, signo); } void ProcessMonitor::ServeOperation(OperationArgs *args) { ProcessMonitor *monitor = args->m_monitor; // We are finised with the arguments and are ready to go. Sync with the // parent thread and start serving operations on the inferior. sem_post(&args->m_semaphore); for (;;) { // wait for next pending operation sem_wait(&monitor->m_operation_pending); monitor->m_operation->Execute(monitor); // notify calling thread that operation is complete sem_post(&monitor->m_operation_done); } } void ProcessMonitor::DoOperation(Operation *op) { Mutex::Locker lock(m_operation_mutex); m_operation = op; // notify operation thread that an operation is ready to be processed sem_post(&m_operation_pending); // wait for operation to complete sem_wait(&m_operation_done); } size_t ProcessMonitor::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Error &error) { size_t result; ReadOperation op(vm_addr, buf, size, error, result); DoOperation(&op); return result; } size_t ProcessMonitor::WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, lldb_private::Error &error) { size_t result; WriteOperation op(vm_addr, buf, size, error, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char* reg_name, unsigned size, RegisterValue &value) { bool result; ReadRegOperation op(tid, offset, size, value, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteRegisterValue(lldb::tid_t tid, unsigned offset, const char* reg_name, const RegisterValue &value) { bool result; WriteRegOperation op(tid, offset, value, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadDebugRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, unsigned size, lldb_private::RegisterValue &value) { bool result; ReadDebugRegOperation op(tid, offset, size, value, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteDebugRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, const lldb_private::RegisterValue &value) { bool result; WriteDebugRegOperation op(tid, offset, value, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; ReadGPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; ReadFPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) { return false; } bool ProcessMonitor::WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; WriteGPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; WriteFPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) { return false; } bool ProcessMonitor::ReadThreadPointer(lldb::tid_t tid, lldb::addr_t &value) { return false; } bool ProcessMonitor::Resume(lldb::tid_t unused, uint32_t signo) { bool result; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); if (log) { const char *signame = m_process->GetUnixSignals()->GetSignalAsCString (signo); if (signame == nullptr) signame = ""; log->Printf("ProcessMonitor::%s() resuming pid %" PRIu64 " with signal %s", __FUNCTION__, GetPID(), signame); } ResumeOperation op(signo, result); DoOperation(&op); if (log) log->Printf ("ProcessMonitor::%s() resuming result = %s", __FUNCTION__, result ? "true" : "false"); return result; } bool ProcessMonitor::SingleStep(lldb::tid_t unused, uint32_t signo) { bool result; SingleStepOperation op(signo, result); DoOperation(&op); return result; } bool ProcessMonitor::Kill() { bool result; KillOperation op(result); DoOperation(&op); return result; } bool ProcessMonitor::GetLwpInfo(lldb::tid_t tid, void *lwpinfo, int &ptrace_err) { bool result; LwpInfoOperation op(tid, lwpinfo, result, ptrace_err); DoOperation(&op); return result; } bool ProcessMonitor::ThreadSuspend(lldb::tid_t tid, bool suspend) { bool result; ThreadSuspendOperation op(tid, suspend, result); DoOperation(&op); return result; } bool ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message) { bool result; EventMessageOperation op(tid, message, result); DoOperation(&op); return result; } lldb_private::Error ProcessMonitor::Detach(lldb::tid_t tid) { lldb_private::Error error; if (tid != LLDB_INVALID_THREAD_ID) { DetachOperation op(error); DoOperation(&op); } return error; } bool ProcessMonitor::DupDescriptor(const FileSpec &file_spec, int fd, int flags) { int target_fd = open(file_spec.GetCString(), flags, 0666); if (target_fd == -1) return false; if (dup2(target_fd, fd) == -1) return false; return (close(target_fd) == -1) ? false : true; } void ProcessMonitor::StopMonitoringChildProcess() { if (m_monitor_thread.IsJoinable()) { m_monitor_thread.Cancel(); m_monitor_thread.Join(nullptr); m_monitor_thread.Reset(); } } void ProcessMonitor::StopMonitor() { StopMonitoringChildProcess(); StopOpThread(); sem_destroy(&m_operation_pending); sem_destroy(&m_operation_done); if (m_terminal_fd >= 0) { close(m_terminal_fd); m_terminal_fd = -1; } } // FIXME: On Linux, when a new thread is created, we receive to notifications, // (1) a SIGTRAP|PTRACE_EVENT_CLONE from the main process thread with the // child thread id as additional information, and (2) a SIGSTOP|SI_USER from // the new child thread indicating that it has is stopped because we attached. // We have no guarantee of the order in which these arrive, but we need both // before we are ready to proceed. We currently keep a list of threads which // have sent the initial SIGSTOP|SI_USER event. Then when we receive the // SIGTRAP|PTRACE_EVENT_CLONE notification, if the initial stop has not occurred // we call ProcessMonitor::WaitForInitialTIDStop() to wait for it. // // Right now, the above logic is in ProcessPOSIX, so we need a definition of // this function in the FreeBSD ProcessMonitor implementation even if it isn't // logically needed. // // We really should figure out what actually happens on FreeBSD and move the // Linux-specific logic out of ProcessPOSIX as needed. bool ProcessMonitor::WaitForInitialTIDStop(lldb::tid_t tid) { return true; } void ProcessMonitor::StopOpThread() { if (!m_operation_thread.IsJoinable()) return; m_operation_thread.Cancel(); m_operation_thread.Join(nullptr); m_operation_thread.Reset(); } Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (revision 293283) @@ -1,5320 +1,5320 @@ //===-- ProcessGDBRemote.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/Config.h" // C Includes #include #include #ifndef LLDB_DISABLE_POSIX #include #include // for mmap #endif #include #include #include // C++ Includes #include #include #include #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Interpreter/Args.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Debugger.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/FileSpec.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/Timer.h" #include "lldb/Core/Value.h" #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/Symbols.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/TimeValue.h" #include "lldb/Host/XML.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/OptionGroupBoolean.h" #include "lldb/Interpreter/OptionGroupUInt64.h" #include "lldb/Interpreter/Property.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Utility/PseudoTerminal.h" // Project includes #include "lldb/Host/Host.h" #include "Plugins/Process/Utility/GDBRemoteSignals.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "Plugins/Process/Utility/StopInfoMachException.h" #include "Utility/StringExtractorGDBRemote.h" #include "GDBRemoteRegisterContext.h" #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "ThreadGDBRemote.h" #define DEBUGSERVER_BASENAME "debugserver" using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_gdb_remote; namespace lldb { // Provide a function that can easily dump the packet history if we know a // ProcessGDBRemote * value (which we can get from logs or from debugging). // We need the function in the lldb namespace so it makes it into the final // executable since the LLDB shared library only exports stuff in the lldb // namespace. This allows you to attach with a debugger and call this // function and get the packet history dumped to a file. void DumpProcessGDBRemotePacketHistory (void *p, const char *path) { StreamFile strm; Error error (strm.GetFile().Open(path, File::eOpenOptionWrite | File::eOpenOptionCanCreate)); if (error.Success()) ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory (strm); } } namespace { static PropertyDefinition g_properties[] = { { "packet-timeout" , OptionValue::eTypeUInt64 , true , 1, NULL, NULL, "Specify the default packet timeout in seconds." }, { "target-definition-file" , OptionValue::eTypeFileSpec , true, 0 , NULL, NULL, "The file that provides the description for remote target registers." }, { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } }; - + enum { ePropertyPacketTimeout, ePropertyTargetDefinitionFile }; - + class PluginProperties : public Properties { public: - + static ConstString GetSettingName () { return ProcessGDBRemote::GetPluginNameStatic(); } - + PluginProperties() : Properties () { m_collection_sp.reset (new OptionValueProperties(GetSettingName())); m_collection_sp->Initialize(g_properties); } - + virtual ~PluginProperties() { } - + uint64_t GetPacketTimeout() { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->GetPropertyAtIndexAsUInt64(NULL, idx, g_properties[idx].default_uint_value); } bool SetPacketTimeout(uint64_t timeout) { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->SetPropertyAtIndexAsUInt64(NULL, idx, timeout); } FileSpec GetTargetDefinitionFile () const { const uint32_t idx = ePropertyTargetDefinitionFile; return m_collection_sp->GetPropertyAtIndexAsFileSpec (NULL, idx); } }; - + typedef std::shared_ptr ProcessKDPPropertiesSP; - + static const ProcessKDPPropertiesSP & GetGlobalPluginProperties() { static ProcessKDPPropertiesSP g_settings_sp; if (!g_settings_sp) g_settings_sp.reset (new PluginProperties ()); return g_settings_sp; } - + } // anonymous namespace end class ProcessGDBRemote::GDBLoadedModuleInfoList { public: class LoadedModuleInfo { public: enum e_data_point { e_has_name = 0, e_has_base , e_has_dynamic , e_has_link_map , e_num }; LoadedModuleInfo () { for (uint32_t i = 0; i < e_num; ++i) m_has[i] = false; } void set_name (const std::string & name) { m_name = name; m_has[e_has_name] = true; } bool get_name (std::string & out) const { out = m_name; return m_has[e_has_name]; } void set_base (const lldb::addr_t base) { m_base = base; m_has[e_has_base] = true; } bool get_base (lldb::addr_t & out) const { out = m_base; return m_has[e_has_base]; } void set_base_is_offset (bool is_offset) { m_base_is_offset = is_offset; } bool get_base_is_offset(bool & out) const { out = m_base_is_offset; return m_has[e_has_base]; } void set_link_map (const lldb::addr_t addr) { m_link_map = addr; m_has[e_has_link_map] = true; } bool get_link_map (lldb::addr_t & out) const { out = m_link_map; return m_has[e_has_link_map]; } void set_dynamic (const lldb::addr_t addr) { m_dynamic = addr; m_has[e_has_dynamic] = true; } bool get_dynamic (lldb::addr_t & out) const { out = m_dynamic; return m_has[e_has_dynamic]; } bool has_info (e_data_point datum) { assert (datum < e_num); return m_has[datum]; } protected: bool m_has[e_num]; std::string m_name; lldb::addr_t m_link_map; lldb::addr_t m_base; bool m_base_is_offset; lldb::addr_t m_dynamic; }; GDBLoadedModuleInfoList () : m_list () , m_link_map (LLDB_INVALID_ADDRESS) {} void add (const LoadedModuleInfo & mod) { m_list.push_back (mod); } void clear () { m_list.clear (); } std::vector m_list; lldb::addr_t m_link_map; }; // TODO Randomly assigning a port is unsafe. We should get an unused // ephemeral port from the kernel and make sure we reserve it before passing // it to debugserver. #if defined (__APPLE__) #define LOW_PORT (IPPORT_RESERVED) #define HIGH_PORT (IPPORT_HIFIRSTAUTO) #else #define LOW_PORT (1024u) #define HIGH_PORT (49151u) #endif #if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__) || defined(__aarch64__)) static bool rand_initialized = false; static inline uint16_t get_random_port () { if (!rand_initialized) { time_t seed = time(NULL); rand_initialized = true; srand(seed); } return (rand() % (HIGH_PORT - LOW_PORT)) + LOW_PORT; } #endif ConstString ProcessGDBRemote::GetPluginNameStatic() { static ConstString g_name("gdb-remote"); return g_name; } const char * ProcessGDBRemote::GetPluginDescriptionStatic() { return "GDB Remote protocol based debugging plug-in."; } void ProcessGDBRemote::Terminate() { PluginManager::UnregisterPlugin (ProcessGDBRemote::CreateInstance); } lldb::ProcessSP ProcessGDBRemote::CreateInstance (lldb::TargetSP target_sp, Listener &listener, const FileSpec *crash_file_path) { lldb::ProcessSP process_sp; if (crash_file_path == NULL) process_sp.reset (new ProcessGDBRemote (target_sp, listener)); return process_sp; } bool ProcessGDBRemote::CanDebug (lldb::TargetSP target_sp, bool plugin_specified_by_name) { if (plugin_specified_by_name) return true; // For now we are just making sure the file exists for a given module Module *exe_module = target_sp->GetExecutableModulePointer(); if (exe_module) { ObjectFile *exe_objfile = exe_module->GetObjectFile(); // We can't debug core files... switch (exe_objfile->GetType()) { case ObjectFile::eTypeInvalid: case ObjectFile::eTypeCoreFile: case ObjectFile::eTypeDebugInfo: case ObjectFile::eTypeObjectFile: case ObjectFile::eTypeSharedLibrary: case ObjectFile::eTypeStubLibrary: case ObjectFile::eTypeJIT: return false; case ObjectFile::eTypeExecutable: case ObjectFile::eTypeDynamicLinker: case ObjectFile::eTypeUnknown: break; } return exe_module->GetFileSpec().Exists(); } // However, if there is no executable module, we return true since we might be preparing to attach. return true; } //---------------------------------------------------------------------- // ProcessGDBRemote constructor //---------------------------------------------------------------------- ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, Listener &listener) : Process (target_sp, listener), m_flags (0), m_gdb_comm (), m_debugserver_pid (LLDB_INVALID_PROCESS_ID), m_last_stop_packet_mutex (Mutex::eMutexTypeRecursive), m_register_info (), m_async_broadcaster (NULL, "lldb.process.gdb-remote.async-broadcaster"), m_async_listener("lldb.process.gdb-remote.async-listener"), m_async_thread_state_mutex(Mutex::eMutexTypeRecursive), m_thread_ids (), m_thread_pcs (), m_jstopinfo_sp (), m_jthreadsinfo_sp (), m_continue_c_tids (), m_continue_C_tids (), m_continue_s_tids (), m_continue_S_tids (), m_max_memory_size (0), m_remote_stub_max_memory_size (0), m_addr_to_mmap_size (), m_thread_create_bp_sp (), m_waiting_for_attach (false), m_destroy_tried_resuming (false), m_command_sp (), m_breakpoint_pc_offset (0), m_initial_tid (LLDB_INVALID_THREAD_ID) { m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue"); m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadDidExit, "async thread did exit"); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_ASYNC)); const uint32_t async_event_mask = eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; if (m_async_listener.StartListeningForEvents(&m_async_broadcaster, async_event_mask) != async_event_mask) { if (log) log->Printf("ProcessGDBRemote::%s failed to listen for m_async_broadcaster events", __FUNCTION__); } const uint32_t gdb_event_mask = Communication::eBroadcastBitReadThreadDidExit | GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify; if (m_async_listener.StartListeningForEvents(&m_gdb_comm, gdb_event_mask) != gdb_event_mask) { if (log) log->Printf("ProcessGDBRemote::%s failed to listen for m_gdb_comm events", __FUNCTION__); } const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout(); if (timeout_seconds > 0) m_gdb_comm.SetPacketTimeout(timeout_seconds); } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- ProcessGDBRemote::~ProcessGDBRemote() { // m_mach_process.UnregisterNotificationCallbacks (this); Clear(); // We need to call finalize on the process before destroying ourselves // to make sure all of the broadcaster cleanup goes as planned. If we // destruct this class, then Process::~Process() might have problems // trying to fully destroy the broadcaster. Finalize(); - + // The general Finalize is going to try to destroy the process and that SHOULD // shut down the async thread. However, if we don't kill it it will get stranded and // its connection will go away so when it wakes up it will crash. So kill it for sure here. StopAsyncThread(); KillDebugserverProcess(); } //---------------------------------------------------------------------- // PluginInterface //---------------------------------------------------------------------- ConstString ProcessGDBRemote::GetPluginName() { return GetPluginNameStatic(); } uint32_t ProcessGDBRemote::GetPluginVersion() { return 1; } bool ProcessGDBRemote::ParsePythonTargetDefinition(const FileSpec &target_definition_fspec) { ScriptInterpreter *interpreter = GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); Error error; StructuredData::ObjectSP module_object_sp(interpreter->LoadPluginModule(target_definition_fspec, error)); if (module_object_sp) { StructuredData::DictionarySP target_definition_sp( interpreter->GetDynamicSettings(module_object_sp, &GetTarget(), "gdb-server-target-definition", error)); if (target_definition_sp) { StructuredData::ObjectSP target_object(target_definition_sp->GetValueForKey("host-info")); if (target_object) { if (auto host_info_dict = target_object->GetAsDictionary()) { StructuredData::ObjectSP triple_value = host_info_dict->GetValueForKey("triple"); if (auto triple_string_value = triple_value->GetAsString()) { std::string triple_string = triple_string_value->GetValue(); ArchSpec host_arch(triple_string.c_str()); if (!host_arch.IsCompatibleMatch(GetTarget().GetArchitecture())) { GetTarget().SetArchitecture(host_arch); } } } } m_breakpoint_pc_offset = 0; StructuredData::ObjectSP breakpoint_pc_offset_value = target_definition_sp->GetValueForKey("breakpoint-pc-offset"); if (breakpoint_pc_offset_value) { if (auto breakpoint_pc_int_value = breakpoint_pc_offset_value->GetAsInteger()) m_breakpoint_pc_offset = breakpoint_pc_int_value->GetValue(); } if (m_register_info.SetRegisterInfo(*target_definition_sp, GetTarget().GetArchitecture()) > 0) { return true; } } } return false; } // If the remote stub didn't give us eh_frame or DWARF register numbers for a register, // see if the ABI can provide them. // DWARF and eh_frame register numbers are defined as a part of the ABI. static void AugmentRegisterInfoViaABI (RegisterInfo ®_info, ConstString reg_name, ABISP abi_sp) { if (reg_info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM || reg_info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM) { if (abi_sp) { RegisterInfo abi_reg_info; if (abi_sp->GetRegisterInfoByName (reg_name, abi_reg_info)) { if (reg_info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM && abi_reg_info.kinds[eRegisterKindEHFrame] != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindEHFrame] = abi_reg_info.kinds[eRegisterKindEHFrame]; } if (reg_info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM && abi_reg_info.kinds[eRegisterKindDWARF] != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindDWARF] = abi_reg_info.kinds[eRegisterKindDWARF]; } if (reg_info.kinds[eRegisterKindGeneric] == LLDB_INVALID_REGNUM && abi_reg_info.kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindGeneric] = abi_reg_info.kinds[eRegisterKindGeneric]; } } } } } static size_t SplitCommaSeparatedRegisterNumberString(const llvm::StringRef &comma_separated_regiter_numbers, std::vector ®nums, int base) { regnums.clear(); std::pair value_pair; value_pair.second = comma_separated_regiter_numbers; do { value_pair = value_pair.second.split(','); if (!value_pair.first.empty()) { uint32_t reg = StringConvert::ToUInt32 (value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, base); if (reg != LLDB_INVALID_REGNUM) regnums.push_back (reg); } } while (!value_pair.second.empty()); return regnums.size(); } void ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) { if (!force && m_register_info.GetNumRegisters() > 0) return; m_register_info.Clear(); // Check if qHostInfo specified a specific packet timeout for this connection. // If so then lets update our setting so the user knows what the timeout is // and can see it. const uint32_t host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout(); if (host_packet_timeout) { GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout); } - // Register info search order: + // Register info search order: // 1 - Use the target definition python file if one is specified. // 2 - If the target definition doesn't have any of the info from the target.xml (registers) then proceed to read the target.xml. // 3 - Fall back on the qRegisterInfo packets. FileSpec target_definition_fspec = GetGlobalPluginProperties()->GetTargetDefinitionFile (); if (!target_definition_fspec.Exists()) { // If the filename doesn't exist, it may be a ~ not having been expanded - try to resolve it. target_definition_fspec.ResolvePath(); } if (target_definition_fspec) { // See if we can get register definitions from a python file if (ParsePythonTargetDefinition (target_definition_fspec)) { return; } else { StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream(); stream_sp->Printf ("ERROR: target description file %s failed to parse.\n", target_definition_fspec.GetPath().c_str()); } } if (GetGDBServerRegisterInfo ()) return; - + char packet[128]; uint32_t reg_offset = 0; uint32_t reg_num = 0; for (StringExtractorGDBRemote::ResponseType response_type = StringExtractorGDBRemote::eResponse; - response_type == StringExtractorGDBRemote::eResponse; + response_type == StringExtractorGDBRemote::eResponse; ++reg_num) { const int packet_len = ::snprintf (packet, sizeof(packet), "qRegisterInfo%x", reg_num); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, false) == GDBRemoteCommunication::PacketResult::Success) { response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { std::string name; std::string value; ConstString reg_name; ConstString alt_name; ConstString set_name; std::vector value_regs; std::vector invalidate_regs; RegisterInfo reg_info = { NULL, // Name NULL, // Alt name 0, // byte size reg_offset, // offset eEncodingUint, // encoding eFormatHex, // format { LLDB_INVALID_REGNUM, // eh_frame reg num LLDB_INVALID_REGNUM, // DWARF reg num LLDB_INVALID_REGNUM, // generic reg num reg_num, // process plugin reg num reg_num // native register number }, NULL, NULL }; while (response.GetNameColonValue(name, value)) { if (name.compare("name") == 0) { reg_name.SetCString(value.c_str()); } else if (name.compare("alt-name") == 0) { alt_name.SetCString(value.c_str()); } else if (name.compare("bitsize") == 0) { reg_info.byte_size = StringConvert::ToUInt32(value.c_str(), 0, 0) / CHAR_BIT; } else if (name.compare("offset") == 0) { uint32_t offset = StringConvert::ToUInt32(value.c_str(), UINT32_MAX, 0); if (reg_offset != offset) { reg_offset = offset; } } else if (name.compare("encoding") == 0) { const Encoding encoding = Args::StringToEncoding (value.c_str()); if (encoding != eEncodingInvalid) reg_info.encoding = encoding; } else if (name.compare("format") == 0) { Format format = eFormatInvalid; if (Args::StringToFormat (value.c_str(), format, NULL).Success()) reg_info.format = format; else if (value.compare("binary") == 0) reg_info.format = eFormatBinary; else if (value.compare("decimal") == 0) reg_info.format = eFormatDecimal; else if (value.compare("hex") == 0) reg_info.format = eFormatHex; else if (value.compare("float") == 0) reg_info.format = eFormatFloat; else if (value.compare("vector-sint8") == 0) reg_info.format = eFormatVectorOfSInt8; else if (value.compare("vector-uint8") == 0) reg_info.format = eFormatVectorOfUInt8; else if (value.compare("vector-sint16") == 0) reg_info.format = eFormatVectorOfSInt16; else if (value.compare("vector-uint16") == 0) reg_info.format = eFormatVectorOfUInt16; else if (value.compare("vector-sint32") == 0) reg_info.format = eFormatVectorOfSInt32; else if (value.compare("vector-uint32") == 0) reg_info.format = eFormatVectorOfUInt32; else if (value.compare("vector-float32") == 0) reg_info.format = eFormatVectorOfFloat32; else if (value.compare("vector-uint128") == 0) reg_info.format = eFormatVectorOfUInt128; } else if (name.compare("set") == 0) { set_name.SetCString(value.c_str()); } else if (name.compare("gcc") == 0 || name.compare("ehframe") == 0) { reg_info.kinds[eRegisterKindEHFrame] = StringConvert::ToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0); } else if (name.compare("dwarf") == 0) { reg_info.kinds[eRegisterKindDWARF] = StringConvert::ToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0); } else if (name.compare("generic") == 0) { reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister (value.c_str()); } else if (name.compare("container-regs") == 0) { SplitCommaSeparatedRegisterNumberString(value, value_regs, 16); } else if (name.compare("invalidate-regs") == 0) { SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 16); } } reg_info.byte_offset = reg_offset; assert (reg_info.byte_size != 0); reg_offset += reg_info.byte_size; if (!value_regs.empty()) { value_regs.push_back(LLDB_INVALID_REGNUM); reg_info.value_regs = value_regs.data(); } if (!invalidate_regs.empty()) { invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } AugmentRegisterInfoViaABI (reg_info, reg_name, GetABI ()); m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name); } else { break; // ensure exit before reg_num is incremented } } else { break; } } if (m_register_info.GetNumRegisters() > 0) { m_register_info.Finalize(GetTarget().GetArchitecture()); return; } // We didn't get anything if the accumulated reg_num is zero. See if we are // debugging ARM and fill with a hard coded register set until we can get an // updated debugserver down on the devices. // On the other hand, if the accumulated reg_num is positive, see if we can // add composite registers to the existing primordial ones. bool from_scratch = (m_register_info.GetNumRegisters() == 0); const ArchSpec &target_arch = GetTarget().GetArchitecture(); const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture(); const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); // Use the process' architecture instead of the host arch, if available ArchSpec remote_arch; if (remote_process_arch.IsValid ()) remote_arch = remote_process_arch; else remote_arch = remote_host_arch; if (!target_arch.IsValid()) { if (remote_arch.IsValid() && (remote_arch.GetMachine() == llvm::Triple::arm || remote_arch.GetMachine() == llvm::Triple::thumb) && remote_arch.GetTriple().getVendor() == llvm::Triple::Apple) m_register_info.HardcodeARMRegisters(from_scratch); } else if (target_arch.GetMachine() == llvm::Triple::arm || target_arch.GetMachine() == llvm::Triple::thumb) { m_register_info.HardcodeARMRegisters(from_scratch); } // At this point, we can finalize our register info. m_register_info.Finalize (GetTarget().GetArchitecture()); } Error ProcessGDBRemote::WillLaunch (Module* module) { return WillLaunchOrAttach (); } Error ProcessGDBRemote::WillAttachToProcessWithID (lldb::pid_t pid) { return WillLaunchOrAttach (); } Error ProcessGDBRemote::WillAttachToProcessWithName (const char *process_name, bool wait_for_launch) { return WillLaunchOrAttach (); } Error ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); Error error (WillLaunchOrAttach ()); - + if (error.Fail()) return error; error = ConnectToDebugserver (remote_url); if (error.Fail()) return error; StartAsyncThread (); lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID (); if (pid == LLDB_INVALID_PROCESS_ID) { // We don't have a valid process ID, so note that we are connected - // and could now request to launch or attach, or get remote process + // and could now request to launch or attach, or get remote process // listings... SetPrivateState (eStateConnected); } else { // We have a valid process SetID (pid); GetThreadList(); StringExtractorGDBRemote response; if (m_gdb_comm.GetStopReply(response)) { SetLastStopPacket(response); // '?' Packets must be handled differently in non-stop mode if (GetTarget().GetNonStopModeEnabled()) HandleStopReplySequence(); Target &target = GetTarget(); - if (!target.GetArchitecture().IsValid()) + if (!target.GetArchitecture().IsValid()) { if (m_gdb_comm.GetProcessArchitecture().IsValid()) { target.SetArchitecture(m_gdb_comm.GetProcessArchitecture()); } else { target.SetArchitecture(m_gdb_comm.GetHostArchitecture()); } } const StateType state = SetThreadStopInfo (response); if (state != eStateInvalid) { SetPrivateState (state); } else error.SetErrorStringWithFormat ("Process %" PRIu64 " was reported after connecting to '%s', but state was not stopped: %s", pid, remote_url, StateAsCString (state)); } else error.SetErrorStringWithFormat ("Process %" PRIu64 " was reported after connecting to '%s', but no stop reply packet was received", pid, remote_url); } if (log) log->Printf ("ProcessGDBRemote::%s pid %" PRIu64 ": normalizing target architecture initial triple: %s (GetTarget().GetArchitecture().IsValid() %s, m_gdb_comm.GetHostArchitecture().IsValid(): %s)", __FUNCTION__, GetID (), GetTarget ().GetArchitecture ().GetTriple ().getTriple ().c_str (), GetTarget ().GetArchitecture ().IsValid () ? "true" : "false", m_gdb_comm.GetHostArchitecture ().IsValid () ? "true" : "false"); if (error.Success() && !GetTarget().GetArchitecture().IsValid() && m_gdb_comm.GetHostArchitecture().IsValid()) { // Prefer the *process'* architecture over that of the *host*, if available. if (m_gdb_comm.GetProcessArchitecture().IsValid()) GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture()); else GetTarget().SetArchitecture(m_gdb_comm.GetHostArchitecture()); } if (log) log->Printf ("ProcessGDBRemote::%s pid %" PRIu64 ": normalized target architecture triple: %s", __FUNCTION__, GetID (), GetTarget ().GetArchitecture ().GetTriple ().getTriple ().c_str ()); if (error.Success()) { PlatformSP platform_sp = GetTarget().GetPlatform(); if (platform_sp && platform_sp->IsConnected()) SetUnixSignals(platform_sp->GetUnixSignals()); else SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture())); } return error; } Error ProcessGDBRemote::WillLaunchOrAttach () { Error error; m_stdio_communication.Clear (); return error; } //---------------------------------------------------------------------- // Process Control //---------------------------------------------------------------------- Error ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); Error error; if (log) log->Printf ("ProcessGDBRemote::%s() entered", __FUNCTION__); uint32_t launch_flags = launch_info.GetFlags().Get(); FileSpec stdin_file_spec{}; FileSpec stdout_file_spec{}; FileSpec stderr_file_spec{}; FileSpec working_dir = launch_info.GetWorkingDirectory(); const FileAction *file_action; file_action = launch_info.GetFileActionForFD (STDIN_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stdin_file_spec = file_action->GetFileSpec(); } file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stdout_file_spec = file_action->GetFileSpec(); } file_action = launch_info.GetFileActionForFD (STDERR_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stderr_file_spec = file_action->GetFileSpec(); } if (log) { if (stdin_file_spec || stdout_file_spec || stderr_file_spec) log->Printf ("ProcessGDBRemote::%s provided with STDIO paths via launch_info: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); else log->Printf ("ProcessGDBRemote::%s no STDIO paths given via launch_info", __FUNCTION__); } const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; if (stdin_file_spec || disable_stdio) { // the inferior will be reading stdin from the specified file // or stdio is completely disabled m_stdin_forward = false; } else { m_stdin_forward = true; } // ::LogSetBitMask (GDBR_LOG_DEFAULT); // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); // ::LogSetLogFile ("/dev/stdout"); ObjectFile * object_file = exe_module->GetObjectFile(); if (object_file) { error = EstablishConnectionIfNeeded (launch_info); if (error.Success()) { lldb_utility::PseudoTerminal pty; const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; PlatformSP platform_sp (GetTarget().GetPlatform()); if (disable_stdio) { // set to /dev/null unless redirected to a file above if (!stdin_file_spec) stdin_file_spec.SetFile(FileSystem::DEV_NULL, false); if (!stdout_file_spec) stdout_file_spec.SetFile(FileSystem::DEV_NULL, false); if (!stderr_file_spec) stderr_file_spec.SetFile(FileSystem::DEV_NULL, false); } else if (platform_sp && platform_sp->IsHost()) { // If the debugserver is local and we aren't disabling STDIO, lets use // a pseudo terminal to instead of relying on the 'O' packets for stdio // since 'O' packets can really slow down debugging if the inferior // does a lot of output. if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) && pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, NULL, 0)) { FileSpec slave_name{pty.GetSlaveName(NULL, 0), false}; if (!stdin_file_spec) stdin_file_spec = slave_name; if (!stdout_file_spec) stdout_file_spec = slave_name; if (!stderr_file_spec) stderr_file_spec = slave_name; } if (log) log->Printf ("ProcessGDBRemote::%s adjusted STDIO paths for local platform (IsHost() is true) using slave: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); } if (log) log->Printf ("ProcessGDBRemote::%s final STDIO paths after all adjustments: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); if (stdin_file_spec) m_gdb_comm.SetSTDIN(stdin_file_spec); if (stdout_file_spec) m_gdb_comm.SetSTDOUT(stdout_file_spec); if (stderr_file_spec) m_gdb_comm.SetSTDERR(stderr_file_spec); m_gdb_comm.SetDisableASLR (launch_flags & eLaunchFlagDisableASLR); m_gdb_comm.SetDetachOnError (launch_flags & eLaunchFlagDetachOnError); m_gdb_comm.SendLaunchArchPacket (GetTarget().GetArchitecture().GetArchitectureName()); - + const char * launch_event_data = launch_info.GetLaunchEventData(); if (launch_event_data != NULL && *launch_event_data != '\0') m_gdb_comm.SendLaunchEventDataPacket (launch_event_data); - + if (working_dir) { m_gdb_comm.SetWorkingDir (working_dir); } // Send the environment and the program + arguments after we connect const Args &environment = launch_info.GetEnvironmentEntries(); if (environment.GetArgumentCount()) { size_t num_environment_entries = environment.GetArgumentCount(); for (size_t i=0; iPrintf("failed to connect to debugserver: %s", error.AsCString()); KillDebugserverProcess (); return error; } StringExtractorGDBRemote response; if (m_gdb_comm.GetStopReply(response)) { SetLastStopPacket(response); // '?' Packets must be handled differently in non-stop mode if (GetTarget().GetNonStopModeEnabled()) HandleStopReplySequence(); const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture(); if (process_arch.IsValid()) { GetTarget().MergeArchitecture(process_arch); } else { const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture(); if (host_arch.IsValid()) GetTarget().MergeArchitecture(host_arch); } SetPrivateState (SetThreadStopInfo (response)); - + if (!disable_stdio) { if (pty.GetMasterFileDescriptor() != lldb_utility::PseudoTerminal::invalid_fd) SetSTDIOFileDescriptor (pty.ReleaseMasterFileDescriptor()); } } } else { if (log) log->Printf("failed to connect to debugserver: %s", error.AsCString()); } } else { // Set our user ID to an invalid process ID. SetID(LLDB_INVALID_PROCESS_ID); - error.SetErrorStringWithFormat ("failed to get object file from '%s' for arch %s", - exe_module->GetFileSpec().GetFilename().AsCString(), + error.SetErrorStringWithFormat ("failed to get object file from '%s' for arch %s", + exe_module->GetFileSpec().GetFilename().AsCString(), exe_module->GetArchitecture().GetArchitectureName()); } return error; } Error ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) { Error error; // Only connect if we have a valid connect URL Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); - + if (connect_url && connect_url[0]) { if (log) log->Printf("ProcessGDBRemote::%s Connecting to %s", __FUNCTION__, connect_url); std::unique_ptr conn_ap(new ConnectionFileDescriptor()); if (conn_ap.get()) { const uint32_t max_retry_count = 50; uint32_t retry_count = 0; while (!m_gdb_comm.IsConnected()) { if (conn_ap->Connect(connect_url, &error) == eConnectionStatusSuccess) { m_gdb_comm.SetConnection (conn_ap.release()); break; } else if (error.WasInterrupted()) { // If we were interrupted, don't keep retrying. break; } - + retry_count++; - + if (retry_count >= max_retry_count) break; usleep (100000); } } } if (!m_gdb_comm.IsConnected()) { if (error.Success()) error.SetErrorString("not connected to remote gdb server"); return error; } // Start the communications read thread so all incoming data can be // parsed into packets and queued as they arrive. if (GetTarget().GetNonStopModeEnabled()) m_gdb_comm.StartReadThread(); // We always seem to be able to open a connection to a local port // so we need to make sure we can then send data to it. If we can't // then we aren't actually connected to anything, so try and do the - // handshake with the remote GDB server and make sure that goes + // handshake with the remote GDB server and make sure that goes // alright. if (!m_gdb_comm.HandshakeWithServer (&error)) { m_gdb_comm.Disconnect(); if (error.Success()) error.SetErrorString("not connected to remote gdb server"); return error; } // Send $QNonStop:1 packet on startup if required if (GetTarget().GetNonStopModeEnabled()) GetTarget().SetNonStopModeEnabled (m_gdb_comm.SetNonStopMode(true)); m_gdb_comm.GetEchoSupported (); m_gdb_comm.GetThreadSuffixSupported (); m_gdb_comm.GetListThreadsInStopReplySupported (); m_gdb_comm.GetHostInfo (); m_gdb_comm.GetVContSupported ('c'); m_gdb_comm.GetVAttachOrWaitSupported(); // Ask the remote server for the default thread id if (GetTarget().GetNonStopModeEnabled()) m_gdb_comm.GetDefaultThreadId(m_initial_tid); size_t num_cmds = GetExtraStartupCommands().GetArgumentCount(); for (size_t idx = 0; idx < num_cmds; idx++) { StringExtractorGDBRemote response; m_gdb_comm.SendPacketAndWaitForResponse (GetExtraStartupCommands().GetArgumentAtIndex(idx), response, false); } return error; } void ProcessGDBRemote::DidLaunchOrAttach (ArchSpec& process_arch) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DidLaunch()"); if (GetID() != LLDB_INVALID_PROCESS_ID) { BuildDynamicRegisterInfo (false); // See if the GDB server supports the qHostInfo information // See if the GDB server supports the qProcessInfo packet, if so // prefer that over the Host information as it will be more specific // to our process. const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); if (remote_process_arch.IsValid()) { process_arch = remote_process_arch; if (log) log->Printf ("ProcessGDBRemote::%s gdb-remote had process architecture, using %s %s", __FUNCTION__, process_arch.GetArchitectureName () ? process_arch.GetArchitectureName () : "", process_arch.GetTriple().getTriple ().c_str() ? process_arch.GetTriple().getTriple ().c_str() : ""); } else { process_arch = m_gdb_comm.GetHostArchitecture(); if (log) log->Printf ("ProcessGDBRemote::%s gdb-remote did not have process architecture, using gdb-remote host architecture %s %s", __FUNCTION__, process_arch.GetArchitectureName () ? process_arch.GetArchitectureName () : "", process_arch.GetTriple().getTriple ().c_str() ? process_arch.GetTriple().getTriple ().c_str() : ""); } if (process_arch.IsValid()) { const ArchSpec &target_arch = GetTarget().GetArchitecture(); if (target_arch.IsValid()) { if (log) log->Printf ("ProcessGDBRemote::%s analyzing target arch, currently %s %s", __FUNCTION__, target_arch.GetArchitectureName () ? target_arch.GetArchitectureName () : "", target_arch.GetTriple().getTriple ().c_str() ? target_arch.GetTriple().getTriple ().c_str() : ""); // If the remote host is ARM and we have apple as the vendor, then // ARM executables and shared libraries can have mixed ARM architectures. // You can have an armv6 executable, and if the host is armv7, then the // system will load the best possible architecture for all shared libraries // it has, so we really need to take the remote host architecture as our // defacto architecture in this case. if ((process_arch.GetMachine() == llvm::Triple::arm || process_arch.GetMachine() == llvm::Triple::thumb) && process_arch.GetTriple().getVendor() == llvm::Triple::Apple) { GetTarget().SetArchitecture (process_arch); if (log) log->Printf ("ProcessGDBRemote::%s remote process is ARM/Apple, setting target arch to %s %s", __FUNCTION__, process_arch.GetArchitectureName () ? process_arch.GetArchitectureName () : "", process_arch.GetTriple().getTriple ().c_str() ? process_arch.GetTriple().getTriple ().c_str() : ""); } else { // Fill in what is missing in the triple const llvm::Triple &remote_triple = process_arch.GetTriple(); llvm::Triple new_target_triple = target_arch.GetTriple(); if (new_target_triple.getVendorName().size() == 0) { new_target_triple.setVendor (remote_triple.getVendor()); if (new_target_triple.getOSName().size() == 0) { new_target_triple.setOS (remote_triple.getOS()); if (new_target_triple.getEnvironmentName().size() == 0) new_target_triple.setEnvironment (remote_triple.getEnvironment()); } ArchSpec new_target_arch = target_arch; new_target_arch.SetTriple(new_target_triple); GetTarget().SetArchitecture(new_target_arch); } } if (log) log->Printf ("ProcessGDBRemote::%s final target arch after adjustments for remote architecture: %s %s", __FUNCTION__, target_arch.GetArchitectureName () ? target_arch.GetArchitectureName () : "", target_arch.GetTriple().getTriple ().c_str() ? target_arch.GetTriple().getTriple ().c_str() : ""); } else { // The target doesn't have a valid architecture yet, set it from // the architecture we got from the remote GDB server GetTarget().SetArchitecture (process_arch); } } } } void ProcessGDBRemote::DidLaunch () { ArchSpec process_arch; DidLaunchOrAttach (process_arch); } Error ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); Error error; if (log) log->Printf ("ProcessGDBRemote::%s()", __FUNCTION__); // Clear out and clean up from any current state Clear(); if (attach_pid != LLDB_INVALID_PROCESS_ID) { error = EstablishConnectionIfNeeded (attach_info); if (error.Success()) { m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); char packet[64]; const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); - SetID (attach_pid); + SetID (attach_pid); m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet, packet_len)); } else SetExitStatus (-1, error.AsCString()); } return error; } Error ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, const ProcessAttachInfo &attach_info) { Error error; // Clear out and clean up from any current state Clear(); if (process_name && process_name[0]) { error = EstablishConnectionIfNeeded (attach_info); if (error.Success()) { StreamString packet; - + m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); - + if (attach_info.GetWaitForLaunch()) { if (!m_gdb_comm.GetVAttachOrWaitSupported()) { packet.PutCString ("vAttachWait"); } else { if (attach_info.GetIgnoreExisting()) packet.PutCString("vAttachWait"); else packet.PutCString ("vAttachOrWait"); } } else packet.PutCString("vAttachName"); packet.PutChar(';'); packet.PutBytesAsRawHex8(process_name, strlen(process_name), endian::InlHostByteOrder(), endian::InlHostByteOrder()); - + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet.GetData(), packet.GetSize())); } else SetExitStatus (-1, error.AsCString()); } return error; } void ProcessGDBRemote::DidExit () { // When we exit, disconnect from the GDB server communications m_gdb_comm.Disconnect(); } void ProcessGDBRemote::DidAttach (ArchSpec &process_arch) { // If you can figure out what the architecture is, fill it in here. process_arch.Clear(); DidLaunchOrAttach (process_arch); } Error ProcessGDBRemote::WillResume () { m_continue_c_tids.clear(); m_continue_C_tids.clear(); m_continue_s_tids.clear(); m_continue_S_tids.clear(); m_jstopinfo_sp.reset(); m_jthreadsinfo_sp.reset(); return Error(); } Error ProcessGDBRemote::DoResume () { Error error; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::Resume()"); - + Listener listener ("gdb-remote.resume-packet-sent"); if (listener.StartListeningForEvents (&m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) { listener.StartListeningForEvents (&m_async_broadcaster, ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); - + const size_t num_threads = GetThreadList().GetSize(); StreamString continue_packet; bool continue_packet_error = false; if (m_gdb_comm.HasAnyVContSupport ()) { if (!GetTarget().GetNonStopModeEnabled() && (m_continue_c_tids.size() == num_threads || (m_continue_c_tids.empty() && m_continue_C_tids.empty() && m_continue_s_tids.empty() && m_continue_S_tids.empty()))) { // All threads are continuing, just send a "c" packet continue_packet.PutCString ("c"); } else { continue_packet.PutCString ("vCont"); - + if (!m_continue_c_tids.empty()) { if (m_gdb_comm.GetVContSupported ('c')) { for (tid_collection::const_iterator t_pos = m_continue_c_tids.begin(), t_end = m_continue_c_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";c:%4.4" PRIx64, *t_pos); } - else + else continue_packet_error = true; } - + if (!continue_packet_error && !m_continue_C_tids.empty()) { if (m_gdb_comm.GetVContSupported ('C')) { for (tid_sig_collection::const_iterator s_pos = m_continue_C_tids.begin(), s_end = m_continue_C_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } - else + else continue_packet_error = true; } if (!continue_packet_error && !m_continue_s_tids.empty()) { if (m_gdb_comm.GetVContSupported ('s')) { for (tid_collection::const_iterator t_pos = m_continue_s_tids.begin(), t_end = m_continue_s_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";s:%4.4" PRIx64, *t_pos); } - else + else continue_packet_error = true; } - + if (!continue_packet_error && !m_continue_S_tids.empty()) { if (m_gdb_comm.GetVContSupported ('S')) { for (tid_sig_collection::const_iterator s_pos = m_continue_S_tids.begin(), s_end = m_continue_S_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";S%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } else continue_packet_error = true; } - + if (continue_packet_error) continue_packet.GetString().clear(); } } else continue_packet_error = true; - + if (continue_packet_error) { // Either no vCont support, or we tried to use part of the vCont // packet that wasn't supported by the remote GDB server. // We need to try and make a simple packet that can do our continue const size_t num_continue_c_tids = m_continue_c_tids.size(); const size_t num_continue_C_tids = m_continue_C_tids.size(); const size_t num_continue_s_tids = m_continue_s_tids.size(); const size_t num_continue_S_tids = m_continue_S_tids.size(); if (num_continue_c_tids > 0) { if (num_continue_c_tids == num_threads) { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun (-1); - continue_packet.PutChar ('c'); + continue_packet.PutChar ('c'); continue_packet_error = false; } else if (num_continue_c_tids == 1 && - num_continue_C_tids == 0 && - num_continue_s_tids == 0 && + num_continue_C_tids == 0 && + num_continue_s_tids == 0 && num_continue_S_tids == 0 ) { // Only one thread is continuing m_gdb_comm.SetCurrentThreadForRun (m_continue_c_tids.front()); - continue_packet.PutChar ('c'); + continue_packet.PutChar ('c'); continue_packet_error = false; } } if (continue_packet_error && num_continue_C_tids > 0) { - if ((num_continue_C_tids + num_continue_c_tids) == num_threads && - num_continue_C_tids > 0 && - num_continue_s_tids == 0 && + if ((num_continue_C_tids + num_continue_c_tids) == num_threads && + num_continue_C_tids > 0 && + num_continue_s_tids == 0 && num_continue_S_tids == 0 ) { const int continue_signo = m_continue_C_tids.front().second; // Only one thread is continuing if (num_continue_C_tids > 1) { - // More that one thread with a signal, yet we don't have + // More that one thread with a signal, yet we don't have // vCont support and we are being asked to resume each // thread with a signal, we need to make sure they are // all the same signal, or we can't issue the continue // accurately with the current support... if (num_continue_C_tids > 1) { continue_packet_error = false; for (size_t i=1; i 0) { if (num_continue_s_tids == num_threads) { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun (-1); // If in Non-Stop-Mode use vCont when stepping if (GetTarget().GetNonStopModeEnabled()) { if (m_gdb_comm.GetVContSupported('s')) continue_packet.PutCString("vCont;s"); else continue_packet.PutChar('s'); } else continue_packet.PutChar('s'); continue_packet_error = false; } else if (num_continue_c_tids == 0 && - num_continue_C_tids == 0 && - num_continue_s_tids == 1 && + num_continue_C_tids == 0 && + num_continue_s_tids == 1 && num_continue_S_tids == 0 ) { // Only one thread is stepping m_gdb_comm.SetCurrentThreadForRun (m_continue_s_tids.front()); - continue_packet.PutChar ('s'); + continue_packet.PutChar ('s'); continue_packet_error = false; } } if (!continue_packet_error && num_continue_S_tids > 0) { if (num_continue_S_tids == num_threads) { const int step_signo = m_continue_S_tids.front().second; // Are all threads trying to step with the same signal? continue_packet_error = false; if (num_continue_S_tids > 1) { for (size_t i=1; iPrintf ("ProcessGDBRemote::DoResume: Trying to resume but the async thread is dead."); return error; } - + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (continue_packet.GetData(), continue_packet.GetSize())); if (listener.WaitForEvent (&timeout, event_sp) == false) { error.SetErrorString("Resume timed out."); if (log) log->Printf ("ProcessGDBRemote::DoResume: Resume timed out."); } else if (event_sp->BroadcasterIs (&m_async_broadcaster)) { error.SetErrorString ("Broadcast continue, but the async thread was killed before we got an ack back."); if (log) log->Printf ("ProcessGDBRemote::DoResume: Broadcast continue, but the async thread was killed before we got an ack back."); return error; } } } return error; } void ProcessGDBRemote::HandleStopReplySequence () { while(true) { // Send vStopped StringExtractorGDBRemote response; m_gdb_comm.SendPacketAndWaitForResponse("vStopped", response, false); // OK represents end of signal list if (response.IsOKResponse()) break; // If not OK or a normal packet we have a problem if (!response.IsNormalResponse()) break; SetLastStopPacket(response); } } void ProcessGDBRemote::ClearThreadIDList () { Mutex::Locker locker(m_thread_list_real.GetMutex()); m_thread_ids.clear(); m_thread_pcs.clear(); } size_t ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue (std::string &value) { m_thread_ids.clear(); m_thread_pcs.clear(); size_t comma_pos; lldb::tid_t tid; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; // thread in big endian hex tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back (tid); value.erase(0, comma_pos + 1); } tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back (tid); return m_thread_ids.size(); } size_t ProcessGDBRemote::UpdateThreadPCsFromStopReplyThreadsValue (std::string &value) { m_thread_pcs.clear(); size_t comma_pos; lldb::addr_t pc; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; pc = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_ADDRESS, 16); if (pc != LLDB_INVALID_ADDRESS) m_thread_pcs.push_back (pc); value.erase(0, comma_pos + 1); } pc = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_ADDRESS, 16); if (pc != LLDB_INVALID_THREAD_ID) m_thread_pcs.push_back (pc); return m_thread_pcs.size(); } bool ProcessGDBRemote::UpdateThreadIDList () { Mutex::Locker locker(m_thread_list_real.GetMutex()); if (m_jthreadsinfo_sp) { // If we have the JSON threads info, we can get the thread list from that StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray(); if (thread_infos && thread_infos->GetSize() > 0) { m_thread_ids.clear(); m_thread_pcs.clear(); thread_infos->ForEach([this](StructuredData::Object* object) -> bool { StructuredData::Dictionary *thread_dict = object->GetAsDictionary(); if (thread_dict) { // Set the thread stop info from the JSON dictionary SetThreadStopInfo (thread_dict); lldb::tid_t tid = LLDB_INVALID_THREAD_ID; if (thread_dict->GetValueForKeyAsInteger("tid", tid)) m_thread_ids.push_back(tid); } return true; // Keep iterating through all thread_info objects }); } if (!m_thread_ids.empty()) return true; } else { // See if we can get the thread IDs from the current stop reply packets // that might contain a "threads" key/value pair // Lock the thread stack while we access it //Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); Mutex::Locker stop_stack_lock; if (stop_stack_lock.TryLock(m_last_stop_packet_mutex)) { // Get the number of stop packets on the stack int nItems = m_stop_packet_stack.size(); // Iterate over them for (int i = 0; i < nItems; i++) { // Get the thread stop info StringExtractorGDBRemote &stop_info = m_stop_packet_stack[i]; const std::string &stop_info_str = stop_info.GetStringRef(); m_thread_pcs.clear(); const size_t thread_pcs_pos = stop_info_str.find(";thread-pcs:"); if (thread_pcs_pos != std::string::npos) { const size_t start = thread_pcs_pos + strlen(";thread-pcs:"); const size_t end = stop_info_str.find(';', start); if (end != std::string::npos) { std::string value = stop_info_str.substr(start, end - start); UpdateThreadPCsFromStopReplyThreadsValue(value); } } const size_t threads_pos = stop_info_str.find(";threads:"); if (threads_pos != std::string::npos) { const size_t start = threads_pos + strlen(";threads:"); const size_t end = stop_info_str.find(';', start); if (end != std::string::npos) { std::string value = stop_info_str.substr(start, end - start); if (UpdateThreadIDsFromStopReplyThreadsValue(value)) return true; } } } } } bool sequence_mutex_unavailable = false; m_gdb_comm.GetCurrentThreadIDs (m_thread_ids, sequence_mutex_unavailable); if (sequence_mutex_unavailable) { return false; // We just didn't get the list } return true; } bool ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) { // locker will keep a mutex locked until it goes out of scope Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD)); if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) log->Printf ("ProcessGDBRemote::%s (pid = %" PRIu64 ")", __FUNCTION__, GetID()); - + size_t num_thread_ids = m_thread_ids.size(); // The "m_thread_ids" thread ID list should always be updated after each stop // reply packet, but in case it isn't, update it here. if (num_thread_ids == 0) { if (!UpdateThreadIDList ()) return false; num_thread_ids = m_thread_ids.size(); } ThreadList old_thread_list_copy(old_thread_list); if (num_thread_ids > 0) { for (size_t i=0; iGetMask().Test(GDBR_LOG_VERBOSE)) log->Printf( "ProcessGDBRemote::%s Making new thread: %p for thread ID: 0x%" PRIx64 ".\n", __FUNCTION__, static_cast(thread_sp.get()), thread_sp->GetID()); } else { if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) log->Printf( "ProcessGDBRemote::%s Found old thread: %p for thread ID: 0x%" PRIx64 ".\n", __FUNCTION__, static_cast(thread_sp.get()), thread_sp->GetID()); } // The m_thread_pcs vector has pc values in big-endian order, not target-endian, unlike most - // of the register read/write packets in gdb-remote protocol. + // of the register read/write packets in gdb-remote protocol. // Early in the process startup, we may not yet have set the process ByteOrder so we ignore these; // they are a performance improvement over fetching thread register values individually, the // method we will fall back to if needed. if (m_thread_ids.size() == m_thread_pcs.size() && thread_sp.get() && GetByteOrder() != eByteOrderInvalid) { ThreadGDBRemote *gdb_thread = static_cast (thread_sp.get()); RegisterContextSP reg_ctx_sp (thread_sp->GetRegisterContext()); if (reg_ctx_sp) { - uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber + uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); if (pc_regnum != LLDB_INVALID_REGNUM) { gdb_thread->PrivateSetRegisterValue (pc_regnum, m_thread_pcs[i]); } } } new_thread_list.AddThread(thread_sp); } } - + // Whatever that is left in old_thread_list_copy are not // present in new_thread_list. Remove non-existent threads from internal id table. size_t old_num_thread_ids = old_thread_list_copy.GetSize(false); for (size_t i=0; iGetProtocolID(); m_thread_id_to_index_id_map.erase(old_thread_id); } } - + return true; } bool ProcessGDBRemote::GetThreadStopInfoFromJSON (ThreadGDBRemote *thread, const StructuredData::ObjectSP &thread_infos_sp) { // See if we got thread stop infos for all threads via the "jThreadsInfo" packet if (thread_infos_sp) { StructuredData::Array *thread_infos = thread_infos_sp->GetAsArray(); if (thread_infos) { lldb::tid_t tid; const size_t n = thread_infos->GetSize(); for (size_t i=0; iGetItemAtIndex(i)->GetAsDictionary(); if (thread_dict) { if (thread_dict->GetValueForKeyAsInteger("tid", tid, LLDB_INVALID_THREAD_ID)) { if (tid == thread->GetID()) return (bool)SetThreadStopInfo(thread_dict); } } } } } return false; } bool ProcessGDBRemote::CalculateThreadStopInfo (ThreadGDBRemote *thread) { // See if we got thread stop infos for all threads via the "jThreadsInfo" packet if (GetThreadStopInfoFromJSON (thread, m_jthreadsinfo_sp)) return true; // See if we got thread stop info for any threads valid stop info reasons threads // via the "jstopinfo" packet stop reply packet key/value pair? if (m_jstopinfo_sp) { // If we have "jstopinfo" then we have stop descriptions for all threads // that have stop reasons, and if there is no entry for a thread, then // it has no stop reason. thread->GetRegisterContext()->InvalidateIfNeeded(true); if (!GetThreadStopInfoFromJSON (thread, m_jstopinfo_sp)) { thread->SetStopInfo (StopInfoSP()); } return true; } // Fall back to using the qThreadStopInfo packet StringExtractorGDBRemote stop_packet; if (GetGDBRemote().GetThreadStopInfo(thread->GetProtocolID(), stop_packet)) return SetThreadStopInfo (stop_packet) == eStateStopped; return false; } ThreadSP ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map, uint8_t signo, const std::string &thread_name, const std::string &reason, const std::string &description, uint32_t exc_type, const std::vector &exc_data, addr_t thread_dispatch_qaddr, bool queue_vars_valid, // Set to true if queue_name, queue_kind and queue_serial are valid std::string &queue_name, QueueKind queue_kind, uint64_t queue_serial) { ThreadSP thread_sp; if (tid != LLDB_INVALID_THREAD_ID) { // Scope for "locker" below { // m_thread_list_real does have its own mutex, but we need to // hold onto the mutex between the call to m_thread_list_real.FindThreadByID(...) // and the m_thread_list_real.AddThread(...) so it doesn't change on us Mutex::Locker locker (m_thread_list_real.GetMutex ()); thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false); if (!thread_sp) { // Create the thread if we need to thread_sp.reset (new ThreadGDBRemote (*this, tid)); m_thread_list_real.AddThread(thread_sp); } } if (thread_sp) { ThreadGDBRemote *gdb_thread = static_cast (thread_sp.get()); gdb_thread->GetRegisterContext()->InvalidateIfNeeded(true); for (const auto &pair : expedited_register_map) { StringExtractor reg_value_extractor; reg_value_extractor.GetStringRef() = pair.second; gdb_thread->PrivateSetRegisterValue (pair.first, reg_value_extractor); } thread_sp->SetName (thread_name.empty() ? NULL : thread_name.c_str()); gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr); // Check if the GDB server was able to provide the queue name, kind and serial number if (queue_vars_valid) gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind, queue_serial); else gdb_thread->ClearQueueInfo(); // Make sure we update our thread stop reason just once if (!thread_sp->StopInfoIsUpToDate()) { thread_sp->SetStopInfo (StopInfoSP()); // If there's a memory thread backed by this thread, we need to use it to calcualte StopInfo. ThreadSP memory_thread_sp = m_thread_list.FindThreadByProtocolID(thread_sp->GetProtocolID()); if (memory_thread_sp) thread_sp = memory_thread_sp; if (exc_type != 0) { const size_t exc_data_size = exc_data.size(); thread_sp->SetStopInfo (StopInfoMachException::CreateStopReasonWithMachException (*thread_sp, exc_type, exc_data_size, exc_data_size >= 1 ? exc_data[0] : 0, exc_data_size >= 2 ? exc_data[1] : 0, exc_data_size >= 3 ? exc_data[2] : 0)); } else { bool handled = false; bool did_exec = false; if (!reason.empty()) { if (reason.compare("trace") == 0) { thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); handled = true; } else if (reason.compare("breakpoint") == 0) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. handled = true; if (bp_site_sp->ValidForThisThread (thread_sp.get())) { thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); } else { StopInfoSP invalid_stop_info_sp; thread_sp->SetStopInfo (invalid_stop_info_sp); } } } else if (reason.compare("trap") == 0) { // Let the trap just use the standard signal stop reason below... } else if (reason.compare("watchpoint") == 0) { StringExtractor desc_extractor(description.c_str()); addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32); addr_t wp_hit_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); watch_id_t watch_id = LLDB_INVALID_WATCH_ID; if (wp_addr != LLDB_INVALID_ADDRESS) { WatchpointSP wp_sp; ArchSpec::Core core = GetTarget().GetArchitecture().GetCore(); if (core >= ArchSpec::kCore_mips_first && core <= ArchSpec::kCore_mips_last) wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_hit_addr); if (!wp_sp) wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); if (wp_sp) { wp_sp->SetHardwareIndex(wp_index); watch_id = wp_sp->GetID(); } } if (watch_id == LLDB_INVALID_WATCH_ID) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_WATCHPOINTS)); if (log) log->Printf ("failed to find watchpoint"); } thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id, wp_hit_addr)); handled = true; } else if (reason.compare("exception") == 0) { thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException(*thread_sp, description.c_str())); handled = true; } else if (reason.compare("exec") == 0) { did_exec = true; thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp)); handled = true; } } if (!handled && signo && did_exec == false) { if (signo == SIGTRAP) { // Currently we are going to assume SIGTRAP means we are either // hitting a breakpoint or hardware single stepping. handled = true; addr_t pc = thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset; lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. if (bp_site_sp->ValidForThisThread (thread_sp.get())) { if(m_breakpoint_pc_offset != 0) thread_sp->GetRegisterContext()->SetPC(pc); thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); } else { StopInfoSP invalid_stop_info_sp; thread_sp->SetStopInfo (invalid_stop_info_sp); } } else { // If we were stepping then assume the stop was the result of the trace. If we were // not stepping then report the SIGTRAP. // FIXME: We are still missing the case where we single step over a trap instruction. if (thread_sp->GetTemporaryResumeState() == eStateStepping) thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); else thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo, description.c_str())); } } if (!handled) thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo, description.c_str())); } if (!description.empty()) { lldb::StopInfoSP stop_info_sp (thread_sp->GetStopInfo ()); if (stop_info_sp) { const char *stop_info_desc = stop_info_sp->GetDescription(); if (!stop_info_desc || !stop_info_desc[0]) stop_info_sp->SetDescription (description.c_str()); } else { thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str())); } } } } } } return thread_sp; } lldb::ThreadSP ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict) { static ConstString g_key_tid("tid"); static ConstString g_key_name("name"); static ConstString g_key_reason("reason"); static ConstString g_key_metype("metype"); static ConstString g_key_medata("medata"); static ConstString g_key_qaddr("qaddr"); static ConstString g_key_queue_name("qname"); static ConstString g_key_queue_kind("qkind"); static ConstString g_key_queue_serial("qserial"); static ConstString g_key_registers("registers"); static ConstString g_key_memory("memory"); static ConstString g_key_address("address"); static ConstString g_key_bytes("bytes"); static ConstString g_key_description("description"); static ConstString g_key_signal("signal"); // Stop with signal and thread info lldb::tid_t tid = LLDB_INVALID_THREAD_ID; uint8_t signo = 0; std::string value; std::string thread_name; std::string reason; std::string description; uint32_t exc_type = 0; std::vector exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; ExpeditedRegisterMap expedited_register_map; bool queue_vars_valid = false; std::string queue_name; QueueKind queue_kind = eQueueKindUnknown; uint64_t queue_serial = 0; // Iterate through all of the thread dictionary key/value pairs from the structured data dictionary thread_dict->ForEach([this, &tid, &expedited_register_map, &thread_name, &signo, &reason, &description, &exc_type, &exc_data, &thread_dispatch_qaddr, &queue_vars_valid, &queue_name, &queue_kind, &queue_serial] (ConstString key, StructuredData::Object* object) -> bool { if (key == g_key_tid) { // thread in big endian hex tid = object->GetIntegerValue(LLDB_INVALID_THREAD_ID); } else if (key == g_key_metype) { // exception type in big endian hex exc_type = object->GetIntegerValue(0); } else if (key == g_key_medata) { // exception data in big endian hex StructuredData::Array *array = object->GetAsArray(); if (array) { array->ForEach([&exc_data](StructuredData::Object* object) -> bool { exc_data.push_back(object->GetIntegerValue()); return true; // Keep iterating through all array items }); } } else if (key == g_key_name) { thread_name = object->GetStringValue(); } else if (key == g_key_qaddr) { thread_dispatch_qaddr = object->GetIntegerValue(LLDB_INVALID_ADDRESS); } else if (key == g_key_queue_name) { queue_vars_valid = true; queue_name = object->GetStringValue(); } else if (key == g_key_queue_kind) { std::string queue_kind_str = object->GetStringValue(); if (queue_kind_str == "serial") { queue_vars_valid = true; queue_kind = eQueueKindSerial; } else if (queue_kind_str == "concurrent") { queue_vars_valid = true; queue_kind = eQueueKindConcurrent; } } else if (key == g_key_queue_serial) { queue_serial = object->GetIntegerValue(0); if (queue_serial != 0) queue_vars_valid = true; } else if (key == g_key_reason) { reason = object->GetStringValue(); } else if (key == g_key_description) { description = object->GetStringValue(); } else if (key == g_key_registers) { StructuredData::Dictionary *registers_dict = object->GetAsDictionary(); if (registers_dict) { registers_dict->ForEach([&expedited_register_map](ConstString key, StructuredData::Object* object) -> bool { const uint32_t reg = StringConvert::ToUInt32 (key.GetCString(), UINT32_MAX, 10); if (reg != UINT32_MAX) expedited_register_map[reg] = object->GetStringValue(); return true; // Keep iterating through all array items }); } } else if (key == g_key_memory) { StructuredData::Array *array = object->GetAsArray(); if (array) { array->ForEach([this](StructuredData::Object* object) -> bool { StructuredData::Dictionary *mem_cache_dict = object->GetAsDictionary(); if (mem_cache_dict) { lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; if (mem_cache_dict->GetValueForKeyAsInteger("address", mem_cache_addr)) { if (mem_cache_addr != LLDB_INVALID_ADDRESS) { StringExtractor bytes; if (mem_cache_dict->GetValueForKeyAsString("bytes", bytes.GetStringRef())) { bytes.SetFilePos(0); const size_t byte_size = bytes.GetStringRef().size()/2; DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); const size_t bytes_copied = bytes.GetHexBytes (data_buffer_sp->GetBytes(), byte_size, 0); if (bytes_copied == byte_size) m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); } } } } return true; // Keep iterating through all array items }); } } else if (key == g_key_signal) signo = object->GetIntegerValue(LLDB_INVALID_SIGNAL_NUMBER); return true; // Keep iterating through all dictionary key/value pairs }); return SetThreadStopInfo (tid, expedited_register_map, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid, queue_name, queue_kind, queue_serial); } StateType ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) { stop_packet.SetFilePos (0); const char stop_type = stop_packet.GetChar(); switch (stop_type) { case 'T': case 'S': { // This is a bit of a hack, but is is required. If we did exec, we // need to clear our thread lists and also know to rebuild our dynamic // register info before we lookup and threads and populate the expedited // register values so we need to know this right away so we can cleanup // and update our registers. const uint32_t stop_id = GetStopID(); if (stop_id == 0) { // Our first stop, make sure we have a process ID, and also make // sure we know about our registers if (GetID() == LLDB_INVALID_PROCESS_ID) { lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID (); if (pid != LLDB_INVALID_PROCESS_ID) SetID (pid); } BuildDynamicRegisterInfo (true); } // Stop with signal and thread info lldb::tid_t tid = LLDB_INVALID_THREAD_ID; const uint8_t signo = stop_packet.GetHexU8(); std::string key; std::string value; std::string thread_name; std::string reason; std::string description; uint32_t exc_type = 0; std::vector exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; bool queue_vars_valid = false; // says if locals below that start with "queue_" are valid std::string queue_name; QueueKind queue_kind = eQueueKindUnknown; uint64_t queue_serial = 0; ExpeditedRegisterMap expedited_register_map; while (stop_packet.GetNameColonValue(key, value)) { if (key.compare("metype") == 0) { // exception type in big endian hex exc_type = StringConvert::ToUInt32 (value.c_str(), 0, 16); } else if (key.compare("medata") == 0) { // exception data in big endian hex exc_data.push_back(StringConvert::ToUInt64 (value.c_str(), 0, 16)); } else if (key.compare("thread") == 0) { // thread in big endian hex tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); } else if (key.compare("threads") == 0) { Mutex::Locker locker(m_thread_list_real.GetMutex()); m_thread_ids.clear(); // A comma separated list of all threads in the current // process that includes the thread for this stop reply // packet size_t comma_pos; lldb::tid_t tid; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; // thread in big endian hex tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back (tid); value.erase(0, comma_pos + 1); } tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back (tid); } else if (key.compare("thread-pcs") == 0) { m_thread_pcs.clear(); // A comma separated list of all threads in the current // process that includes the thread for this stop reply // packet size_t comma_pos; lldb::addr_t pc; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; // thread in big endian hex pc = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_ADDRESS, 16); if (pc != LLDB_INVALID_ADDRESS) m_thread_pcs.push_back (pc); value.erase(0, comma_pos + 1); } pc = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_ADDRESS, 16); if (pc != LLDB_INVALID_ADDRESS) m_thread_pcs.push_back (pc); } else if (key.compare("jstopinfo") == 0) { StringExtractor json_extractor; // Swap "value" over into "name_extractor" json_extractor.GetStringRef().swap(value); // Now convert the HEX bytes into a string value json_extractor.GetHexByteString (value); // This JSON contains thread IDs and thread stop info for all threads. // It doesn't contain expedited registers, memory or queue info. m_jstopinfo_sp = StructuredData::ParseJSON (value); } else if (key.compare("hexname") == 0) { StringExtractor name_extractor; // Swap "value" over into "name_extractor" name_extractor.GetStringRef().swap(value); // Now convert the HEX bytes into a string value name_extractor.GetHexByteString (value); thread_name.swap (value); } else if (key.compare("name") == 0) { thread_name.swap (value); } else if (key.compare("qaddr") == 0) { thread_dispatch_qaddr = StringConvert::ToUInt64 (value.c_str(), 0, 16); } else if (key.compare("qname") == 0) { queue_vars_valid = true; StringExtractor name_extractor; // Swap "value" over into "name_extractor" name_extractor.GetStringRef().swap(value); // Now convert the HEX bytes into a string value name_extractor.GetHexByteString (value); queue_name.swap (value); } else if (key.compare("qkind") == 0) { if (value == "serial") { queue_vars_valid = true; queue_kind = eQueueKindSerial; } else if (value == "concurrent") { queue_vars_valid = true; queue_kind = eQueueKindConcurrent; } } else if (key.compare("qserial") == 0) { queue_serial = StringConvert::ToUInt64 (value.c_str(), 0, 0); if (queue_serial != 0) queue_vars_valid = true; } else if (key.compare("reason") == 0) { reason.swap(value); } else if (key.compare("description") == 0) { StringExtractor desc_extractor; // Swap "value" over into "name_extractor" desc_extractor.GetStringRef().swap(value); // Now convert the HEX bytes into a string value desc_extractor.GetHexByteString (value); description.swap(value); } else if (key.compare("memory") == 0) { // Expedited memory. GDB servers can choose to send back expedited memory // that can populate the L1 memory cache in the process so that things like // the frame pointer backchain can be expedited. This will help stack // backtracing be more efficient by not having to send as many memory read // requests down the remote GDB server. // Key/value pair format: memory:=; // is a number whose base will be interpreted by the prefix: // "0x[0-9a-fA-F]+" for hex // "0[0-7]+" for octal // "[1-9]+" for decimal // is native endian ASCII hex bytes just like the register values llvm::StringRef value_ref(value); std::pair pair; pair = value_ref.split('='); if (!pair.first.empty() && !pair.second.empty()) { std::string addr_str(pair.first.str()); const lldb::addr_t mem_cache_addr = StringConvert::ToUInt64(addr_str.c_str(), LLDB_INVALID_ADDRESS, 0); if (mem_cache_addr != LLDB_INVALID_ADDRESS) { StringExtractor bytes; bytes.GetStringRef() = pair.second.str(); const size_t byte_size = bytes.GetStringRef().size()/2; DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); const size_t bytes_copied = bytes.GetHexBytes (data_buffer_sp->GetBytes(), byte_size, 0); if (bytes_copied == byte_size) m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); } } } else if (key.compare("watch") == 0 || key.compare("rwatch") == 0 || key.compare("awatch") == 0) { // Support standard GDB remote stop reply packet 'TAAwatch:addr' lldb::addr_t wp_addr = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_ADDRESS, 16); WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); uint32_t wp_index = LLDB_INVALID_INDEX32; if (wp_sp) wp_index = wp_sp->GetHardwareIndex(); reason = "watchpoint"; StreamString ostr; ostr.Printf("%" PRIu64 " %" PRIu32, wp_addr, wp_index); description = ostr.GetString().c_str(); } else if (key.compare("library") == 0) { LoadModules(); } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) { uint32_t reg = StringConvert::ToUInt32 (key.c_str(), UINT32_MAX, 16); if (reg != UINT32_MAX) expedited_register_map[reg] = std::move(value); } } if (tid == LLDB_INVALID_THREAD_ID) { - // A thread id may be invalid if the response is old style 'S' packet which does not provide the + // A thread id may be invalid if the response is old style 'S' packet which does not provide the // thread information. So update the thread list and choose the first one. UpdateThreadIDList (); - + if (!m_thread_ids.empty ()) { tid = m_thread_ids.front (); } } ThreadSP thread_sp = SetThreadStopInfo (tid, expedited_register_map, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid, queue_name, queue_kind, queue_serial); return eStateStopped; } break; case 'W': case 'X': // process exited return eStateExited; default: break; } return eStateInvalid; } void ProcessGDBRemote::RefreshStateAfterStop () { Mutex::Locker locker(m_thread_list_real.GetMutex()); m_thread_ids.clear(); m_thread_pcs.clear(); // Set the thread stop info. It might have a "threads" key whose value is // a list of all thread IDs in the current process, so m_thread_ids might // get set. // Scope for the lock { // Lock the thread stack while we access it Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); // Get the number of stop packets on the stack int nItems = m_stop_packet_stack.size(); // Iterate over them for (int i = 0; i < nItems; i++) { // Get the thread stop info StringExtractorGDBRemote stop_info = m_stop_packet_stack[i]; // Process thread stop info SetThreadStopInfo(stop_info); } // Clear the thread stop stack m_stop_packet_stack.clear(); } // Check to see if SetThreadStopInfo() filled in m_thread_ids? if (m_thread_ids.empty()) { // No, we need to fetch the thread list manually UpdateThreadIDList(); } // If we have queried for a default thread id if (m_initial_tid != LLDB_INVALID_THREAD_ID) { m_thread_list.SetSelectedThreadByID(m_initial_tid); m_initial_tid = LLDB_INVALID_THREAD_ID; } // Let all threads recover from stopping and do any clean up based // on the previous thread state (if any). m_thread_list_real.RefreshStateAfterStop(); - + } Error ProcessGDBRemote::DoHalt (bool &caused_stop) { Error error; bool timed_out = false; Mutex::Locker locker; - + if (m_public_state.GetValue() == eStateAttaching) { // We are being asked to halt during an attach. We need to just close // our file handle and debugserver will go away, and we can be done... m_gdb_comm.Disconnect(); } else { if (!m_gdb_comm.SendInterrupt (locker, 2, timed_out)) { if (timed_out) error.SetErrorString("timed out sending interrupt packet"); else error.SetErrorString("unknown error sending interrupt packet"); } - + caused_stop = m_gdb_comm.GetInterruptWasSent (); } return error; } Error ProcessGDBRemote::DoDetach(bool keep_stopped) { Error error; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped); - + error = m_gdb_comm.Detach (keep_stopped); if (log) { if (error.Success()) log->PutCString ("ProcessGDBRemote::DoDetach() detach packet sent successfully"); else log->Printf ("ProcessGDBRemote::DoDetach() detach packet send failed: %s", error.AsCString() ? error.AsCString() : ""); } - + if (!error.Success()) return error; // Sleep for one second to let the process get all detached... StopAsyncThread (); SetPrivateState (eStateDetached); ResumePrivateStateThread(); //KillDebugserverProcess (); return error; } Error ProcessGDBRemote::DoDestroy () { Error error; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DoDestroy()"); #if 0 // XXX Currently no iOS target support on FreeBSD // There is a bug in older iOS debugservers where they don't shut down the process // they are debugging properly. If the process is sitting at a breakpoint or an exception, // this can cause problems with restarting. So we check to see if any of our threads are stopped // at a breakpoint, and if so we remove all the breakpoints, resume the process, and THEN // destroy it again. // // Note, we don't have a good way to test the version of debugserver, but I happen to know that // the set of all the iOS debugservers which don't support GetThreadSuffixSupported() and that of // the debugservers with this bug are equal. There really should be a better way to test this! // // We also use m_destroy_tried_resuming to make sure we only do this once, if we resume and then halt and // get called here to destroy again and we're still at a breakpoint or exception, then we should // just do the straight-forward kill. // // And of course, if we weren't able to stop the process by the time we get here, it isn't // necessary (or helpful) to do any of this. if (!m_gdb_comm.GetThreadSuffixSupported() && m_public_state.GetValue() != eStateRunning) { PlatformSP platform_sp = GetTarget().GetPlatform(); - + // FIXME: These should be ConstStrings so we aren't doing strcmp'ing. if (platform_sp && platform_sp->GetName() && platform_sp->GetName() == PlatformRemoteiOS::GetPluginNameStatic()) { if (m_destroy_tried_resuming) { if (log) log->PutCString ("ProcessGDBRemote::DoDestroy() - Tried resuming to destroy once already, not doing it again."); } else - { + { // At present, the plans are discarded and the breakpoints disabled Process::Destroy, // but we really need it to happen here and it doesn't matter if we do it twice. m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); - + bool stop_looks_like_crash = false; ThreadList &threads = GetThreadList(); - + { Mutex::Locker locker(threads.GetMutex()); - + size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); StopReason reason = eStopReasonInvalid; if (stop_info_sp) reason = stop_info_sp->GetStopReason(); if (reason == eStopReasonBreakpoint || reason == eStopReasonException) { if (log) log->Printf ("ProcessGDBRemote::DoDestroy() - thread: 0x%4.4" PRIx64 " stopped with reason: %s.", thread_sp->GetProtocolID(), stop_info_sp->GetDescription()); stop_looks_like_crash = true; break; } } } - + if (stop_looks_like_crash) { if (log) log->PutCString ("ProcessGDBRemote::DoDestroy() - Stopped at a breakpoint, continue and then kill."); m_destroy_tried_resuming = true; - - // If we are going to run again before killing, it would be good to suspend all the threads + + // If we are going to run again before killing, it would be good to suspend all the threads // before resuming so they won't get into more trouble. Sadly, for the threads stopped with // the breakpoint or exception, the exception doesn't get cleared if it is suspended, so we do // have to run the risk of letting those threads proceed a bit. - + { Mutex::Locker locker(threads.GetMutex()); - + size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); StopReason reason = eStopReasonInvalid; if (stop_info_sp) reason = stop_info_sp->GetStopReason(); if (reason != eStopReasonBreakpoint && reason != eStopReasonException) { if (log) log->Printf ("ProcessGDBRemote::DoDestroy() - Suspending thread: 0x%4.4" PRIx64 " before running.", thread_sp->GetProtocolID()); thread_sp->SetResumeState(eStateSuspended); } } } Resume (); return Destroy(false); } } } } #endif - + // Interrupt if our inferior is running... int exit_status = SIGABRT; std::string exit_string; if (m_gdb_comm.IsConnected()) { if (m_public_state.GetValue() != eStateAttaching) { StringExtractorGDBRemote response; bool send_async = true; GDBRemoteCommunication::ScopedTimeout (m_gdb_comm, 3); if (m_gdb_comm.SendPacketAndWaitForResponse("k", 1, response, send_async) == GDBRemoteCommunication::PacketResult::Success) { char packet_cmd = response.GetChar(0); if (packet_cmd == 'W' || packet_cmd == 'X') { #if defined(__APPLE__) // For Native processes on Mac OS X, we launch through the Host Platform, then hand the process off // to debugserver, which becomes the parent process through "PT_ATTACH". Then when we go to kill // the process on Mac OS X we call ptrace(PT_KILL) to kill it, then we call waitpid which returns // with no error and the correct status. But amusingly enough that doesn't seem to actually reap // the process, but instead it is left around as a Zombie. Probably the kernel is in the process of // switching ownership back to lldb which was the original parent, and gets confused in the handoff. // Anyway, so call waitpid here to finally reap it. PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp && platform_sp->IsHost()) { int status; ::pid_t reap_pid; reap_pid = waitpid (GetID(), &status, WNOHANG); if (log) log->Printf ("Reaped pid: %d, status: %d.\n", reap_pid, status); } #endif SetLastStopPacket (response); ClearThreadIDList (); exit_status = response.GetHexU8(); } else { if (log) log->Printf ("ProcessGDBRemote::DoDestroy - got unexpected response to k packet: %s", response.GetStringRef().c_str()); exit_string.assign("got unexpected response to k packet: "); exit_string.append(response.GetStringRef()); } } else { if (log) log->Printf ("ProcessGDBRemote::DoDestroy - failed to send k packet"); exit_string.assign("failed to send the k packet"); } } else { if (log) log->Printf ("ProcessGDBRemote::DoDestroy - killed or interrupted while attaching"); exit_string.assign ("killed or interrupted while attaching."); } } else { // If we missed setting the exit status on the way out, do it here. // NB set exit status can be called multiple times, the first one sets the status. exit_string.assign("destroying when not connected to debugserver"); } SetExitStatus(exit_status, exit_string.c_str()); StopAsyncThread (); KillDebugserverProcess (); return error; } void ProcessGDBRemote::SetLastStopPacket (const StringExtractorGDBRemote &response) { const bool did_exec = response.GetStringRef().find(";reason:exec;") != std::string::npos; if (did_exec) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::SetLastStopPacket () - detected exec"); m_thread_list_real.Clear(); m_thread_list.Clear(); BuildDynamicRegisterInfo (true); m_gdb_comm.ResetDiscoverableSettings (did_exec); } // Scope the lock { // Lock the thread stack while we access it Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); // We are are not using non-stop mode, there can only be one last stop // reply packet, so clear the list. if (GetTarget().GetNonStopModeEnabled() == false) m_stop_packet_stack.clear(); // Add this stop packet to the stop packet stack // This stack will get popped and examined when we switch to the // Stopped state m_stop_packet_stack.push_back(response); } } void ProcessGDBRemote::SetUnixSignals(const UnixSignalsSP &signals_sp) { Process::SetUnixSignals(std::make_shared(signals_sp)); } //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ bool ProcessGDBRemote::IsAlive () { return m_gdb_comm.IsConnected() && Process::IsAlive(); } addr_t ProcessGDBRemote::GetImageInfoAddress() { // request the link map address via the $qShlibInfoAddr packet lldb::addr_t addr = m_gdb_comm.GetShlibInfoAddr(); // the loaded module list can also provides a link map address if (addr == LLDB_INVALID_ADDRESS) { GDBLoadedModuleInfoList list; if (GetLoadedModuleList (list).Success()) addr = list.m_link_map; } return addr; } void ProcessGDBRemote::WillPublicStop () { // See if the GDB remote client supports the JSON threads info. // If so, we gather stop info for all threads, expedited registers, // expedited memory, runtime queue information (iOS and MacOSX only), // and more. Expediting memory will help stack backtracing be much // faster. Expediting registers will make sure we don't have to read // the thread registers for GPRs. m_jthreadsinfo_sp = m_gdb_comm.GetThreadsInfo(); if (m_jthreadsinfo_sp) { // Now set the stop info for each thread and also expedite any registers // and memory that was in the jThreadsInfo response. StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray(); if (thread_infos) { const size_t n = thread_infos->GetSize(); for (size_t i=0; iGetItemAtIndex(i)->GetAsDictionary(); if (thread_dict) SetThreadStopInfo(thread_dict); } } } } //------------------------------------------------------------------ // Process Memory //------------------------------------------------------------------ size_t ProcessGDBRemote::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) { GetMaxMemorySize (); if (size > m_max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be - // called multiple times in order to complete the task by + // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = m_max_memory_size; } char packet[64]; int packet_len; bool binary_memory_read = m_gdb_comm.GetxPacketSupported(); packet_len = ::snprintf(packet, sizeof(packet), "%c%" PRIx64 ",%" PRIx64, binary_memory_read ? 'x' : 'm', (uint64_t)addr, (uint64_t)size); assert (packet_len + 1 < (int)sizeof(packet)); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, true) == GDBRemoteCommunication::PacketResult::Success) { if (response.IsNormalResponse()) { error.Clear(); if (binary_memory_read) { // The lower level GDBRemoteCommunication packet receive layer has already de-quoted any // 0x7d character escaping that was present in the packet size_t data_received_size = response.GetBytesLeft(); if (data_received_size > size) { // Don't write past the end of BUF if the remote debug server gave us too // much data for some reason. data_received_size = size; } memcpy (buf, response.GetStringRef().data(), data_received_size); return data_received_size; } else { return response.GetHexBytes(buf, size, '\xdd'); } } else if (response.IsErrorResponse()) error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) error.SetErrorStringWithFormat("GDB server does not support reading memory"); else error.SetErrorStringWithFormat("unexpected response to GDB server memory read packet '%s': '%s'", packet, response.GetStringRef().c_str()); } else { error.SetErrorStringWithFormat("failed to send packet: '%s'", packet); } return 0; } size_t ProcessGDBRemote::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error) { GetMaxMemorySize (); if (size > m_max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be - // called multiple times in order to complete the task by + // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = m_max_memory_size; } StreamString packet; packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size); packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(), endian::InlHostByteOrder()); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, true) == GDBRemoteCommunication::PacketResult::Success) { if (response.IsOKResponse()) { error.Clear(); return size; } else if (response.IsErrorResponse()) error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) error.SetErrorStringWithFormat("GDB server does not support writing memory"); else error.SetErrorStringWithFormat("unexpected response to GDB server memory write packet '%s': '%s'", packet.GetString().c_str(), response.GetStringRef().c_str()); } else { error.SetErrorStringWithFormat("failed to send packet: '%s'", packet.GetString().c_str()); } return 0; } lldb::addr_t ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &error) { Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_EXPRESSIONS)); addr_t allocated_addr = LLDB_INVALID_ADDRESS; - + LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { case eLazyBoolCalculate: case eLazyBoolYes: allocated_addr = m_gdb_comm.AllocateMemory (size, permissions); if (allocated_addr != LLDB_INVALID_ADDRESS || supported == eLazyBoolYes) return allocated_addr; case eLazyBoolNo: // Call mmap() to create memory in the inferior.. unsigned prot = 0; if (permissions & lldb::ePermissionsReadable) prot |= eMmapProtRead; if (permissions & lldb::ePermissionsWritable) prot |= eMmapProtWrite; if (permissions & lldb::ePermissionsExecutable) prot |= eMmapProtExec; if (InferiorCallMmap(this, allocated_addr, 0, size, prot, eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) m_addr_to_mmap_size[allocated_addr] = size; else { allocated_addr = LLDB_INVALID_ADDRESS; if (log) log->Printf ("ProcessGDBRemote::%s no direct stub support for memory allocation, and InferiorCallMmap also failed - is stub missing register context save/restore capability?", __FUNCTION__); } break; } - + if (allocated_addr == LLDB_INVALID_ADDRESS) error.SetErrorStringWithFormat("unable to allocate %" PRIu64 " bytes of memory with permissions %s", (uint64_t)size, GetPermissionsAsCString (permissions)); else error.Clear(); return allocated_addr; } Error -ProcessGDBRemote::GetMemoryRegionInfo (addr_t load_addr, +ProcessGDBRemote::GetMemoryRegionInfo (addr_t load_addr, MemoryRegionInfo ®ion_info) { - + Error error (m_gdb_comm.GetMemoryRegionInfo (load_addr, region_info)); return error; } Error ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num) { - + Error error (m_gdb_comm.GetWatchpointSupportInfo (num)); return error; } Error ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num, bool& after) { Error error (m_gdb_comm.GetWatchpointSupportInfo (num, after, GetTarget().GetArchitecture())); return error; } Error ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr) { - Error error; + Error error; LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { case eLazyBoolCalculate: - // We should never be deallocating memory without allocating memory + // We should never be deallocating memory without allocating memory // first so we should never get eLazyBoolCalculate error.SetErrorString ("tried to deallocate memory without ever allocating memory"); break; case eLazyBoolYes: if (!m_gdb_comm.DeallocateMemory (addr)) error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr); break; - + case eLazyBoolNo: // Call munmap() to deallocate memory in the inferior.. { MMapMap::iterator pos = m_addr_to_mmap_size.find(addr); if (pos != m_addr_to_mmap_size.end() && InferiorCallMunmap(this, addr, pos->second)) m_addr_to_mmap_size.erase (pos); else error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr); } break; } return error; } //------------------------------------------------------------------ // Process STDIO //------------------------------------------------------------------ size_t ProcessGDBRemote::PutSTDIN (const char *src, size_t src_len, Error &error) { if (m_stdio_communication.IsConnected()) { ConnectionStatus status; m_stdio_communication.Write(src, src_len, status, NULL); } else if (m_stdin_forward) { m_gdb_comm.SendStdinNotification(src, src_len); } return 0; } Error ProcessGDBRemote::EnableBreakpointSite (BreakpointSite *bp_site) { Error error; assert(bp_site != NULL); // Get logging info Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); user_id_t site_id = bp_site->GetID(); // Get the breakpoint address const addr_t addr = bp_site->GetLoadAddress(); // Log that a breakpoint was requested if (log) log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64, site_id, (uint64_t)addr); // Breakpoint already exists and is enabled if (bp_site->IsEnabled()) { if (log) log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64 " -- SUCCESS (already enabled)", site_id, (uint64_t)addr); return error; } // Get the software breakpoint trap opcode size const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); // SupportsGDBStoppointPacket() simply checks a boolean, indicating if this breakpoint type // is supported by the remote stub. These are set to true by default, and later set to false // only after we receive an unimplemented response when sending a breakpoint packet. This means // initially that unless we were specifically instructed to use a hardware breakpoint, LLDB will // attempt to set a software breakpoint. HardwareRequired() also queries a boolean variable which // indicates if the user specifically asked for hardware breakpoints. If true then we will // skip over software breakpoints. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware) && (!bp_site->HardwareRequired())) { // Try to send off a software breakpoint packet ($Z0) if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, true, addr, bp_op_size) == 0) { // The breakpoint was placed successfully bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eExternal); return error; } // SendGDBStoppointTypePacket() will return an error if it was unable to set this // breakpoint. We need to differentiate between a error specific to placing this breakpoint // or if we have learned that this breakpoint type is unsupported. To do this, we // must test the support boolean for this breakpoint type to see if it now indicates that // this breakpoint type is unsupported. If they are still supported then we should return // with the error code. If they are now unsupported, then we would like to fall through // and try another form of breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) return error; // We reach here when software breakpoints have been found to be unsupported. For future // calls to set a breakpoint, we will not attempt to set a breakpoint with a type that is // known not to be supported. if (log) log->Printf("Software breakpoints are unsupported"); // So we will fall through and try a hardware breakpoint } // The process of setting a hardware breakpoint is much the same as above. We check the // supported boolean for this breakpoint type, and if it is thought to be supported then we // will try to set this breakpoint with a hardware breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Try to send off a hardware breakpoint packet ($Z1) if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, true, addr, bp_op_size) == 0) { // The breakpoint was placed successfully bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eHardware); return error; } // Check if the error was something other then an unsupported breakpoint type if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Unable to set this hardware breakpoint error.SetErrorString("failed to set hardware breakpoint (hardware breakpoint resources might be exhausted or unavailable)"); return error; } // We will reach here when the stub gives an unsupported response to a hardware breakpoint if (log) log->Printf("Hardware breakpoints are unsupported"); // Finally we will falling through to a #trap style breakpoint } // Don't fall through when hardware breakpoints were specifically requested if (bp_site->HardwareRequired()) { error.SetErrorString("hardware breakpoints are not supported"); return error; } // As a last resort we want to place a manual breakpoint. An instruction // is placed into the process memory using memory write packets. return EnableSoftwareBreakpoint(bp_site); } Error ProcessGDBRemote::DisableBreakpointSite (BreakpointSite *bp_site) { Error error; assert (bp_site != NULL); addr_t addr = bp_site->GetLoadAddress(); user_id_t site_id = bp_site->GetID(); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); if (log) log->Printf ("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64, site_id, (uint64_t)addr); if (bp_site->IsEnabled()) { const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site); BreakpointSite::Type bp_type = bp_site->GetType(); switch (bp_type) { case BreakpointSite::eSoftware: error = DisableSoftwareBreakpoint (bp_site); break; case BreakpointSite::eHardware: if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, false, addr, bp_op_size)) error.SetErrorToGenericError(); break; case BreakpointSite::eExternal: { GDBStoppointType stoppoint_type; if (bp_site->IsHardware()) stoppoint_type = eBreakpointHardware; else stoppoint_type = eBreakpointSoftware; - + if (m_gdb_comm.SendGDBStoppointTypePacket(stoppoint_type, false, addr, bp_op_size)) error.SetErrorToGenericError(); } break; } if (error.Success()) bp_site->SetEnabled(false); } else { if (log) log->Printf ("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", site_id, (uint64_t)addr); return error; } if (error.Success()) error.SetErrorToGenericError(); return error; } // Pre-requisite: wp != NULL. static GDBStoppointType GetGDBStoppointType (Watchpoint *wp) { assert(wp); bool watch_read = wp->WatchpointRead(); bool watch_write = wp->WatchpointWrite(); // watch_read and watch_write cannot both be false. assert(watch_read || watch_write); if (watch_read && watch_write) return eWatchpointReadWrite; else if (watch_read) return eWatchpointRead; else // Must be watch_write, then. return eWatchpointWrite; } Error ProcessGDBRemote::EnableWatchpoint (Watchpoint *wp, bool notify) { Error error; if (wp) { user_id_t watchID = wp->GetID(); addr_t addr = wp->GetLoadAddress(); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); if (log) log->Printf ("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ")", watchID); if (wp->IsEnabled()) { if (log) log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.", watchID, (uint64_t)addr); return error; } GDBStoppointType type = GetGDBStoppointType(wp); // Pass down an appropriate z/Z packet... if (m_gdb_comm.SupportsGDBStoppointPacket (type)) { if (m_gdb_comm.SendGDBStoppointTypePacket(type, true, addr, wp->GetByteSize()) == 0) { wp->SetEnabled(true, notify); return error; } else error.SetErrorString("sending gdb watchpoint packet failed"); } else error.SetErrorString("watchpoints not supported"); } else { error.SetErrorString("Watchpoint argument was NULL."); } if (error.Success()) error.SetErrorToGenericError(); return error; } Error ProcessGDBRemote::DisableWatchpoint (Watchpoint *wp, bool notify) { Error error; if (wp) { user_id_t watchID = wp->GetID(); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); addr_t addr = wp->GetLoadAddress(); if (log) log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64, watchID, (uint64_t)addr); if (!wp->IsEnabled()) { if (log) log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", watchID, (uint64_t)addr); // See also 'class WatchpointSentry' within StopInfo.cpp. // This disabling attempt might come from the user-supplied actions, we'll route it in order for // the watchpoint object to intelligently process this action. wp->SetEnabled(false, notify); return error; } - + if (wp->IsHardware()) { GDBStoppointType type = GetGDBStoppointType(wp); // Pass down an appropriate z/Z packet... if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, wp->GetByteSize()) == 0) { wp->SetEnabled(false, notify); return error; } else - error.SetErrorString("sending gdb watchpoint packet failed"); + error.SetErrorString("sending gdb watchpoint packet failed"); } // TODO: clear software watchpoints if we implement them } else { error.SetErrorString("Watchpoint argument was NULL."); } if (error.Success()) error.SetErrorToGenericError(); return error; } void ProcessGDBRemote::Clear() { m_flags = 0; m_thread_list_real.Clear(); m_thread_list.Clear(); } Error ProcessGDBRemote::DoSignal (int signo) { Error error; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DoSignal (signal = %d)", signo); if (!m_gdb_comm.SendAsyncSignal (signo)) error.SetErrorStringWithFormat("failed to send signal %i", signo); return error; } Error ProcessGDBRemote::EstablishConnectionIfNeeded (const ProcessInfo &process_info) { // Make sure we aren't already connected? if (m_gdb_comm.IsConnected()) return Error(); PlatformSP platform_sp (GetTarget ().GetPlatform ()); if (platform_sp && !platform_sp->IsHost ()) return Error("Lost debug server connection"); auto error = LaunchAndConnectToDebugserver (process_info); if (error.Fail()) { const char *error_string = error.AsCString(); if (error_string == nullptr) error_string = "unable to launch " DEBUGSERVER_BASENAME; } return error; } Error ProcessGDBRemote::LaunchAndConnectToDebugserver (const ProcessInfo &process_info) { Error error; if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) { // If we locate debugserver, keep that located version around static FileSpec g_debugserver_file_spec; ProcessLaunchInfo debugserver_launch_info; // Make debugserver run in its own session so signals generated by // special terminal key sequences (^C) don't affect debugserver. debugserver_launch_info.SetLaunchInSeparateProcessGroup(true); debugserver_launch_info.SetMonitorProcessCallback (MonitorDebugserverProcess, this, false); debugserver_launch_info.SetUserID(process_info.GetUserID()); #if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__)) // On iOS, still do a local connection using a random port const char *hostname = "127.0.0.1"; uint16_t port = get_random_port (); #else // Set hostname being NULL to do the reverse connect where debugserver // will bind to port zero and it will communicate back to us the port // that we will connect to const char *hostname = nullptr; uint16_t port = 0; #endif StreamString url_str; const char* url = nullptr; if (hostname != nullptr) { url_str.Printf("%s:%u", hostname, port); url = url_str.GetData(); } error = m_gdb_comm.StartDebugserverProcess (url, GetTarget().GetPlatform().get(), debugserver_launch_info, &port); if (error.Success ()) m_debugserver_pid = debugserver_launch_info.GetProcessID(); else m_debugserver_pid = LLDB_INVALID_PROCESS_ID; if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) StartAsyncThread (); - + if (error.Fail()) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf("failed to start debugserver process: %s", error.AsCString()); return error; } - + if (m_gdb_comm.IsConnected()) { // Finish the connection process by doing the handshake without connecting (send NULL URL) ConnectToDebugserver (NULL); } else { StreamString connect_url; connect_url.Printf("connect://%s:%u", hostname, port); error = ConnectToDebugserver (connect_url.GetString().c_str()); } } return error; } bool ProcessGDBRemote::MonitorDebugserverProcess ( void *callback_baton, lldb::pid_t debugserver_pid, bool exited, // True if the process did exit int signo, // Zero for no signal int exit_status // Exit value of process if signal is zero ) { // The baton is a "ProcessGDBRemote *". Now this class might be gone // and might not exist anymore, so we need to carefully try to get the // target for this process first since we have a race condition when - // we are done running between getting the notice that the inferior + // we are done running between getting the notice that the inferior // process has died and the debugserver that was debugging this process. // In our test suite, we are also continually running process after // process, so we must be very careful to make sure: // 1 - process object hasn't been deleted already // 2 - that a new process object hasn't been recreated in its place // "debugserver_pid" argument passed in is the process ID for // debugserver that we are tracking... Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); ProcessGDBRemote *process = (ProcessGDBRemote *)callback_baton; // Get a shared pointer to the target that has a matching process pointer. // This target could be gone, or the target could already have a new process // object inside of it TargetSP target_sp (Debugger::FindTargetWithProcess(process)); if (log) log->Printf ("ProcessGDBRemote::MonitorDebugserverProcess (baton=%p, pid=%" PRIu64 ", signo=%i (0x%x), exit_status=%i)", callback_baton, debugserver_pid, signo, signo, exit_status); if (target_sp) { // We found a process in a target that matches, but another thread // might be in the process of launching a new process that will // soon replace it, so get a shared pointer to the process so we // can keep it alive. ProcessSP process_sp (target_sp->GetProcessSP()); // Now we have a shared pointer to the process that can't go away on us // so we now make sure it was the same as the one passed in, and also make - // sure that our previous "process *" didn't get deleted and have a new + // sure that our previous "process *" didn't get deleted and have a new // "process *" created in its place with the same pointer. To verify this // we make sure the process has our debugserver process ID. If we pass all // of these tests, then we are sure that this process is the one we were // looking for. if (process_sp && process == process_sp.get() && process->m_debugserver_pid == debugserver_pid) { // Sleep for a half a second to make sure our inferior process has // time to set its exit status before we set it incorrectly when // both the debugserver and the inferior process shut down. usleep (500000); // If our process hasn't yet exited, debugserver might have died. // If the process did exit, the we are reaping it. const StateType state = process->GetState(); - + if (process->m_debugserver_pid != LLDB_INVALID_PROCESS_ID && state != eStateInvalid && state != eStateUnloaded && state != eStateExited && state != eStateDetached) { char error_str[1024]; if (signo) { const char *signal_cstr = process->GetUnixSignals()->GetSignalAsCString(signo); if (signal_cstr) ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr); else ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %i", signo); } else { ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", exit_status); } process->SetExitStatus (-1, error_str); } // Debugserver has exited we need to let our ProcessGDBRemote // know that it no longer has a debugserver instance process->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; } } return true; } void ProcessGDBRemote::KillDebugserverProcess () { m_gdb_comm.Disconnect(); if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { Host::Kill (m_debugserver_pid, SIGINT); m_debugserver_pid = LLDB_INVALID_PROCESS_ID; } } void ProcessGDBRemote::Initialize() { static std::once_flag g_once_flag; std::call_once(g_once_flag, []() { PluginManager::RegisterPlugin (GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, DebuggerInitialize); }); } void ProcessGDBRemote::DebuggerInitialize (Debugger &debugger) { if (!PluginManager::GetSettingForProcessPlugin(debugger, PluginProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForProcessPlugin (debugger, GetGlobalPluginProperties()->GetValueProperties(), ConstString ("Properties for the gdb-remote process plug-in."), is_global_setting); } } bool ProcessGDBRemote::StartAsyncThread () { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); - + Mutex::Locker start_locker(m_async_thread_state_mutex); if (!m_async_thread.IsJoinable()) { // Create a thread that watches our internal state and controls which // events make it to clients (into the DCProcess event queue). m_async_thread = ThreadLauncher::LaunchThread("", ProcessGDBRemote::AsyncThread, this, NULL); } else if (log) log->Printf("ProcessGDBRemote::%s () - Called when Async thread was already running.", __FUNCTION__); return m_async_thread.IsJoinable(); } void ProcessGDBRemote::StopAsyncThread () { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); Mutex::Locker start_locker(m_async_thread_state_mutex); if (m_async_thread.IsJoinable()) { m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); - + // This will shut down the async thread. m_gdb_comm.Disconnect(); // Disconnect from the debug server. // Stop the stdio thread m_async_thread.Join(nullptr); m_async_thread.Reset(); } else if (log) log->Printf("ProcessGDBRemote::%s () - Called when Async thread was not running.", __FUNCTION__); } bool ProcessGDBRemote::HandleNotifyPacket (StringExtractorGDBRemote &packet) { // get the packet at a string const std::string &pkt = packet.GetStringRef(); // skip %stop: StringExtractorGDBRemote stop_info(pkt.c_str() + 5); // pass as a thread stop info packet SetLastStopPacket(stop_info); // check for more stop reasons HandleStopReplySequence(); // if the process is stopped then we need to fake a resume // so that we can stop properly with the new break. This // is possible due to SetPrivateState() broadcasting the // state change as a side effect. if (GetPrivateState() == lldb::StateType::eStateStopped) { SetPrivateState(lldb::StateType::eStateRunning); } // since we have some stopped packets we can halt the process SetPrivateState(lldb::StateType::eStateStopped); return true; } thread_result_t ProcessGDBRemote::AsyncThread (void *arg) { ProcessGDBRemote *process = (ProcessGDBRemote*) arg; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread starting...", __FUNCTION__, arg, process->GetID()); EventSP event_sp; bool done = false; while (!done) { if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID()); if (process->m_async_listener.WaitForEvent (NULL, event_sp)) { const uint32_t event_type = event_sp->GetType(); if (event_sp->BroadcasterIs (&process->m_async_broadcaster)) { if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") Got an event of type: %d...", __FUNCTION__, arg, process->GetID(), event_type); switch (event_type) { case eBroadcastBitAsyncContinue: { const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get()); if (continue_packet) { const char *continue_cstr = (const char *)continue_packet->GetBytes (); const size_t continue_cstr_len = continue_packet->GetByteSize (); if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr); if (::strstr (continue_cstr, "vAttach") == NULL) process->SetPrivateState(eStateRunning); StringExtractorGDBRemote response; // If in Non-Stop-Mode if (process->GetTarget().GetNonStopModeEnabled()) { // send the vCont packet if (!process->GetGDBRemote().SendvContPacket(process, continue_cstr, continue_cstr_len, response)) { // Something went wrong done = true; break; } } // If in All-Stop-Mode else { StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response); // We need to immediately clear the thread ID list so we are sure to get a valid list of threads. // The thread ID list might be contained within the "response", or the stop reply packet that // caused the stop. So clear it now before we give the stop reply packet to the process // using the process->SetLastStopPacket()... process->ClearThreadIDList (); switch (stop_state) { case eStateStopped: case eStateCrashed: case eStateSuspended: process->SetLastStopPacket (response); process->SetPrivateState (stop_state); break; case eStateExited: { process->SetLastStopPacket (response); process->ClearThreadIDList(); response.SetFilePos(1); - + int exit_status = response.GetHexU8(); const char *desc_cstr = NULL; StringExtractor extractor; std::string desc_string; if (response.GetBytesLeft() > 0 && response.GetChar('-') == ';') { std::string desc_token; while (response.GetNameColonValue (desc_token, desc_string)) { if (desc_token == "description") { extractor.GetStringRef().swap(desc_string); extractor.SetFilePos(0); extractor.GetHexByteString (desc_string); desc_cstr = desc_string.c_str(); } } } process->SetExitStatus(exit_status, desc_cstr); done = true; break; } case eStateInvalid: { // Check to see if we were trying to attach and if we got back // the "E87" error code from debugserver -- this indicates that // the process is not debuggable. Return a slightly more helpful // error message about why the attach failed. if (::strstr (continue_cstr, "vAttach") != NULL && response.GetError() == 0x87) { process->SetExitStatus(-1, "cannot attach to process due to System Integrity Protection"); } // E01 code from vAttach means that the attach failed if (::strstr (continue_cstr, "vAttach") != NULL && response.GetError() == 0x1) { process->SetExitStatus(-1, "unable to attach"); } else { process->SetExitStatus(-1, "lost connection"); } break; } default: process->SetPrivateState (stop_state); break; } // switch(stop_state) } // else // if in All-stop-mode } // if (continue_packet) } // case eBroadcastBitAysncContinue break; case eBroadcastBitAsyncThreadShouldExit: if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID()); done = true; break; default: if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); done = true; break; } } else if (event_sp->BroadcasterIs (&process->m_gdb_comm)) { switch (event_type) { case Communication::eBroadcastBitReadThreadDidExit: process->SetExitStatus (-1, "lost connection"); done = true; break; case GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify: { lldb_private::Event *event = event_sp.get(); const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event); StringExtractorGDBRemote notify((const char*)continue_packet->GetBytes()); // Hand this over to the process to handle process->HandleNotifyPacket(notify); break; } default: if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); done = true; break; } } } else { if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID()); done = true; } } if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, arg, process->GetID()); return NULL; } //uint32_t //ProcessGDBRemote::ListProcessesMatchingName (const char *name, StringList &matches, std::vector &pids) //{ // // If we are planning to launch the debugserver remotely, then we need to fire up a debugserver // // process and ask it for the list of processes. But if we are local, we can let the Host do it. // if (m_local_debugserver) // { // return Host::ListProcessesMatchingName (name, matches, pids); // } -// else +// else // { // // FIXME: Implement talking to the remote debugserver. // return 0; // } // //} // bool ProcessGDBRemote::NewThreadNotifyBreakpointHit (void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { - // I don't think I have to do anything here, just make sure I notice the new thread when it starts to + // I don't think I have to do anything here, just make sure I notice the new thread when it starts to // run so I can stop it if that's what I want to do. Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (log) log->Printf("Hit New Thread Notification breakpoint."); return false; } bool ProcessGDBRemote::StartNoticingNewThreads() { Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) log->Printf("Enabled noticing new thread breakpoint."); m_thread_create_bp_sp->SetEnabled(true); } else { PlatformSP platform_sp (GetTarget().GetPlatform()); if (platform_sp) { m_thread_create_bp_sp = platform_sp->SetThreadCreationBreakpoint(GetTarget()); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) log->Printf("Successfully created new thread notification breakpoint %i", m_thread_create_bp_sp->GetID()); m_thread_create_bp_sp->SetCallback (ProcessGDBRemote::NewThreadNotifyBreakpointHit, this, true); } else { if (log) log->Printf("Failed to create new thread notification breakpoint."); } } } return m_thread_create_bp_sp.get() != NULL; } bool ProcessGDBRemote::StopNoticingNewThreads() -{ +{ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf ("Disabling new thread notification breakpoint."); if (m_thread_create_bp_sp) m_thread_create_bp_sp->SetEnabled(false); return true; } - + DynamicLoader * ProcessGDBRemote::GetDynamicLoader () { if (m_dyld_ap.get() == NULL) m_dyld_ap.reset (DynamicLoader::FindPlugin(this, NULL)); return m_dyld_ap.get(); } Error ProcessGDBRemote::SendEventData(const char *data) { int return_value; bool was_supported; - + Error error; - + return_value = m_gdb_comm.SendLaunchEventDataPacket (data, &was_supported); if (return_value != 0) { if (!was_supported) error.SetErrorString("Sending events is not supported for this process."); else error.SetErrorStringWithFormat("Error sending event data: %d.", return_value); } return error; } const DataBufferSP ProcessGDBRemote::GetAuxvData() { DataBufferSP buf; if (m_gdb_comm.GetQXferAuxvReadSupported()) { std::string response_string; if (m_gdb_comm.SendPacketsAndConcatenateResponses("qXfer:auxv:read::", response_string) == GDBRemoteCommunication::PacketResult::Success) buf.reset(new DataBufferHeap(response_string.c_str(), response_string.length())); } return buf; } StructuredData::ObjectSP ProcessGDBRemote::GetExtendedInfoForThread (lldb::tid_t tid) { StructuredData::ObjectSP object_sp; if (m_gdb_comm.GetThreadExtendedInfoSupported()) { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); SystemRuntime *runtime = GetSystemRuntime(); if (runtime) { runtime->AddThreadExtendedInfoPacketHints (args_dict); } args_dict->GetAsDictionary()->AddIntegerItem ("thread", tid); StreamString packet; packet << "jThreadExtendedInfo:"; args_dict->Dump (packet); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version // of the } character here manually in case we talk to a debugserver which // un-escapes the characters at packet read time. packet << (char) (0x7d ^ 0x20); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { object_sp = StructuredData::ParseJSON (response.GetStringRef()); } } } } return object_sp; } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos (lldb::addr_t image_list_address, lldb::addr_t image_count) { StructuredData::ObjectSP object_sp; if (m_gdb_comm.GetLoadedDynamicLibrariesInfosSupported()) { // Scope for the scoped timeout object GDBRemoteCommunication::ScopedTimeout timeout (m_gdb_comm, 10); StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); args_dict->GetAsDictionary()->AddIntegerItem ("image_list_address", image_list_address); args_dict->GetAsDictionary()->AddIntegerItem ("image_count", image_count); StreamString packet; packet << "jGetLoadedDynamicLibrariesInfos:"; args_dict->Dump (packet); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version // of the } character here manually in case we talk to a debugserver which // un-escapes the characters at packet read time. packet << (char) (0x7d ^ 0x20); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { object_sp = StructuredData::ParseJSON (response.GetStringRef()); } } } } return object_sp; } // Establish the largest memory read/write payloads we should use. // If the remote stub has a max packet size, stay under that size. -// -// If the remote stub's max packet size is crazy large, use a +// +// If the remote stub's max packet size is crazy large, use a // reasonable largeish default. // // If the remote stub doesn't advertise a max packet size, use a // conservative default. void ProcessGDBRemote::GetMaxMemorySize() { const uint64_t reasonable_largeish_default = 128 * 1024; const uint64_t conservative_default = 512; if (m_max_memory_size == 0) { uint64_t stub_max_size = m_gdb_comm.GetRemoteMaxPacketSize(); if (stub_max_size != UINT64_MAX && stub_max_size != 0) { // Save the stub's claimed maximum packet size m_remote_stub_max_memory_size = stub_max_size; // Even if the stub says it can support ginormous packets, // don't exceed our reasonable largeish default packet size. if (stub_max_size > reasonable_largeish_default) { stub_max_size = reasonable_largeish_default; } m_max_memory_size = stub_max_size; } else { m_max_memory_size = conservative_default; } } } void ProcessGDBRemote::SetUserSpecifiedMaxMemoryTransferSize (uint64_t user_specified_max) { if (user_specified_max != 0) { GetMaxMemorySize (); if (m_remote_stub_max_memory_size != 0) { if (m_remote_stub_max_memory_size < user_specified_max) { m_max_memory_size = m_remote_stub_max_memory_size; // user specified a packet size too big, go as big // as the remote stub says we can go. } else { m_max_memory_size = user_specified_max; // user's packet size is good } } else { m_max_memory_size = user_specified_max; // user's packet size is probably fine } } } bool ProcessGDBRemote::GetModuleSpec(const FileSpec& module_file_spec, const ArchSpec& arch, ModuleSpec &module_spec) { Log *log = GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PLATFORM); if (!m_gdb_comm.GetModuleInfo (module_file_spec, arch, module_spec)) { if (log) log->Printf ("ProcessGDBRemote::%s - failed to get module info for %s:%s", __FUNCTION__, module_file_spec.GetPath ().c_str (), arch.GetTriple ().getTriple ().c_str ()); return false; } if (log) { StreamString stream; module_spec.Dump (stream); log->Printf ("ProcessGDBRemote::%s - got module info for (%s:%s) : %s", __FUNCTION__, module_file_spec.GetPath ().c_str (), arch.GetTriple ().getTriple ().c_str (), stream.GetString ().c_str ()); } return true; } bool ProcessGDBRemote::GetHostOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) { if (m_gdb_comm.GetOSVersion(major, minor, update)) return true; // We failed to get the host OS version, defer to the base // implementation to correctly invalidate the arguments. return Process::GetHostOSVersion(major, minor, update); } namespace { typedef std::vector stringVec; typedef std::vector GDBServerRegisterVec; struct RegisterSetInfo { ConstString name; }; typedef std::map RegisterSetMap; - + struct GdbServerTargetInfo { std::string arch; std::string osabi; stringVec includes; RegisterSetMap reg_set_map; XMLNode feature_node; }; - + bool ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp) { if (!feature_node) return false; - + uint32_t cur_reg_num = 0; uint32_t reg_offset = 0; feature_node.ForEachChildElementWithName("reg", [&target_info, &dyn_reg_info, &cur_reg_num, ®_offset, &abi_sp](const XMLNode ®_node) -> bool { std::string gdb_group; std::string gdb_type; ConstString reg_name; ConstString alt_name; ConstString set_name; std::vector value_regs; std::vector invalidate_regs; bool encoding_set = false; bool format_set = false; RegisterInfo reg_info = { NULL, // Name NULL, // Alt name 0, // byte size reg_offset, // offset eEncodingUint, // encoding eFormatHex, // format { LLDB_INVALID_REGNUM, // eh_frame reg num LLDB_INVALID_REGNUM, // DWARF reg num LLDB_INVALID_REGNUM, // generic reg num cur_reg_num, // process plugin reg num cur_reg_num // native register number }, NULL, NULL }; - + reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type, ®_name, &alt_name, &set_name, &value_regs, &invalidate_regs, &encoding_set, &format_set, ®_info, &cur_reg_num, ®_offset](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "name") { reg_name.SetString(value); } else if (name == "bitsize") { reg_info.byte_size = StringConvert::ToUInt32(value.data(), 0, 0) / CHAR_BIT; } else if (name == "type") { gdb_type = value.str(); } else if (name == "group") { gdb_group = value.str(); } else if (name == "regnum") { const uint32_t regnum = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); if (regnum != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindProcessPlugin] = regnum; } } else if (name == "offset") { reg_offset = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); } else if (name == "altname") { alt_name.SetString(value); } else if (name == "encoding") { encoding_set = true; reg_info.encoding = Args::StringToEncoding (value.data(), eEncodingUint); } else if (name == "format") { format_set = true; Format format = eFormatInvalid; if (Args::StringToFormat (value.data(), format, NULL).Success()) reg_info.format = format; else if (value == "vector-sint8") reg_info.format = eFormatVectorOfSInt8; else if (value == "vector-uint8") reg_info.format = eFormatVectorOfUInt8; else if (value == "vector-sint16") reg_info.format = eFormatVectorOfSInt16; else if (value == "vector-uint16") reg_info.format = eFormatVectorOfUInt16; else if (value == "vector-sint32") reg_info.format = eFormatVectorOfSInt32; else if (value == "vector-uint32") reg_info.format = eFormatVectorOfUInt32; else if (value == "vector-float32") reg_info.format = eFormatVectorOfFloat32; else if (value == "vector-uint128") reg_info.format = eFormatVectorOfUInt128; } else if (name == "group_id") { const uint32_t set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); RegisterSetMap::const_iterator pos = target_info.reg_set_map.find(set_id); if (pos != target_info.reg_set_map.end()) set_name = pos->second.name; } else if (name == "gcc_regnum" || name == "ehframe_regnum") { reg_info.kinds[eRegisterKindEHFrame] = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); } else if (name == "dwarf_regnum") { reg_info.kinds[eRegisterKindDWARF] = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); } else if (name == "generic") { reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister(value.data()); } else if (name == "value_regnums") { SplitCommaSeparatedRegisterNumberString(value, value_regs, 0); } else if (name == "invalidate_regnums") { SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 0); } else { printf("unhandled attribute %s = %s\n", name.data(), value.data()); } return true; // Keep iterating through all attributes }); - + if (!gdb_type.empty() && !(encoding_set || format_set)) { if (gdb_type.find("int") == 0) { reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; } else if (gdb_type == "data_ptr" || gdb_type == "code_ptr") { reg_info.format = eFormatAddressInfo; reg_info.encoding = eEncodingUint; } else if (gdb_type == "i387_ext" || gdb_type == "float") { reg_info.format = eFormatFloat; reg_info.encoding = eEncodingIEEE754; } } - + // Only update the register set name if we didn't get a "reg_set" attribute. // "set_name" will be empty if we didn't have a "reg_set" attribute. if (!set_name && !gdb_group.empty()) set_name.SetCString(gdb_group.c_str()); - + reg_info.byte_offset = reg_offset; assert (reg_info.byte_size != 0); reg_offset += reg_info.byte_size; if (!value_regs.empty()) { value_regs.push_back(LLDB_INVALID_REGNUM); reg_info.value_regs = value_regs.data(); } if (!invalidate_regs.empty()) { invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } - + ++cur_reg_num; AugmentRegisterInfoViaABI (reg_info, reg_name, abi_sp); dyn_reg_info.AddRegister(reg_info, reg_name, alt_name, set_name); - + return true; // Keep iterating through all "reg" elements }); return true; } - + } // namespace {} // query the target of gdb-remote for extended target information // return: 'true' on success // 'false' on failure bool ProcessGDBRemote::GetGDBServerRegisterInfo () { // Make sure LLDB has an XML parser it can use first if (!XMLDocument::XMLEnabled()) return false; // redirect libxml2's error handler since the default prints to stdout GDBRemoteCommunicationClient & comm = m_gdb_comm; // check that we have extended feature read support if ( !comm.GetQXferFeaturesReadSupported( ) ) return false; // request the target xml file std::string raw; lldb_private::Error lldberr; if (!comm.ReadExtFeature(ConstString("features"), ConstString("target.xml"), raw, lldberr)) { return false; } - + XMLDocument xml_document; if (xml_document.ParseMemory(raw.c_str(), raw.size(), "target.xml")) { GdbServerTargetInfo target_info; - + XMLNode target_node = xml_document.GetRootElement("target"); if (target_node) { XMLNode feature_node; target_node.ForEachChildElement([&target_info, this, &feature_node](const XMLNode &node) -> bool { llvm::StringRef name = node.GetName(); if (name == "architecture") { node.GetElementText(target_info.arch); } else if (name == "osabi") { node.GetElementText(target_info.osabi); } else if (name == "xi:include" || name == "include") { llvm::StringRef href = node.GetAttributeValue("href"); if (!href.empty()) target_info.includes.push_back(href.str()); } else if (name == "feature") { feature_node = node; } else if (name == "groups") { node.ForEachChildElementWithName("group", [&target_info](const XMLNode &node) -> bool { uint32_t set_id = UINT32_MAX; RegisterSetInfo set_info; - + node.ForEachAttribute([&set_id, &set_info](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "id") set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); if (name == "name") set_info.name = ConstString(value); return true; // Keep iterating through all attributes }); - + if (set_id != UINT32_MAX) target_info.reg_set_map[set_id] = set_info; return true; // Keep iterating through all "group" elements }); } return true; // Keep iterating through all children of the target_node }); - + if (feature_node) { ParseRegisters(feature_node, target_info, this->m_register_info, GetABI()); } - + for (const auto &include : target_info.includes) { // request register file std::string xml_data; if (!comm.ReadExtFeature(ConstString("features"), ConstString(include), xml_data, lldberr)) continue; XMLDocument include_xml_document; include_xml_document.ParseMemory(xml_data.data(), xml_data.size(), include.c_str()); XMLNode include_feature_node = include_xml_document.GetRootElement("feature"); if (include_feature_node) { ParseRegisters(include_feature_node, target_info, this->m_register_info, GetABI()); } } this->m_register_info.Finalize(GetTarget().GetArchitecture()); } } return m_register_info.GetNumRegisters() > 0; } Error ProcessGDBRemote::GetLoadedModuleList (GDBLoadedModuleInfoList & list) { // Make sure LLDB has an XML parser it can use first if (!XMLDocument::XMLEnabled()) return Error (0, ErrorType::eErrorTypeGeneric); Log *log = GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS); if (log) log->Printf ("ProcessGDBRemote::%s", __FUNCTION__); GDBRemoteCommunicationClient & comm = m_gdb_comm; // check that we have extended feature read support if (comm.GetQXferLibrariesSVR4ReadSupported ()) { list.clear (); // request the loaded library list std::string raw; lldb_private::Error lldberr; if (!comm.ReadExtFeature (ConstString ("libraries-svr4"), ConstString (""), raw, lldberr)) return Error (0, ErrorType::eErrorTypeGeneric); // parse the xml file in memory if (log) log->Printf ("parsing: %s", raw.c_str()); XMLDocument doc; - + if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) return Error (0, ErrorType::eErrorTypeGeneric); XMLNode root_element = doc.GetRootElement("library-list-svr4"); if (!root_element) return Error(); // main link map structure llvm::StringRef main_lm = root_element.GetAttributeValue("main-lm"); if (!main_lm.empty()) { list.m_link_map = StringConvert::ToUInt64(main_lm.data(), LLDB_INVALID_ADDRESS, 0); } root_element.ForEachChildElementWithName("library", [log, &list](const XMLNode &library) -> bool { GDBLoadedModuleInfoList::LoadedModuleInfo module; library.ForEachAttribute([log, &module](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { - + if (name == "name") module.set_name (value.str()); else if (name == "lm") { // the address of the link_map struct. module.set_link_map(StringConvert::ToUInt64(value.data(), LLDB_INVALID_ADDRESS, 0)); } else if (name == "l_addr") { // the displacement as read from the field 'l_addr' of the link_map struct. module.set_base(StringConvert::ToUInt64(value.data(), LLDB_INVALID_ADDRESS, 0)); // base address is always a displacement, not an absolute value. module.set_base_is_offset(true); } else if (name == "l_ld") { // the memory address of the libraries PT_DYAMIC section. module.set_dynamic(StringConvert::ToUInt64(value.data(), LLDB_INVALID_ADDRESS, 0)); } - + return true; // Keep iterating over all properties of "library" }); if (log) { std::string name; lldb::addr_t lm=0, base=0, ld=0; bool base_is_offset; module.get_name (name); module.get_link_map (lm); module.get_base (base); module.get_base_is_offset (base_is_offset); module.get_dynamic (ld); log->Printf ("found (link_map:0x%08" PRIx64 ", base:0x%08" PRIx64 "[%s], ld:0x%08" PRIx64 ", name:'%s')", lm, base, (base_is_offset ? "offset" : "absolute"), ld, name.c_str()); } list.add (module); return true; // Keep iterating over all "library" elements in the root node }); if (log) log->Printf ("found %" PRId32 " modules in total", (int) list.m_list.size()); } else if (comm.GetQXferLibrariesReadSupported ()) { list.clear (); // request the loaded library list std::string raw; lldb_private::Error lldberr; if (!comm.ReadExtFeature (ConstString ("libraries"), ConstString (""), raw, lldberr)) return Error (0, ErrorType::eErrorTypeGeneric); if (log) log->Printf ("parsing: %s", raw.c_str()); XMLDocument doc; if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) return Error (0, ErrorType::eErrorTypeGeneric); XMLNode root_element = doc.GetRootElement("library-list"); if (!root_element) return Error(); root_element.ForEachChildElementWithName("library", [log, &list](const XMLNode &library) -> bool { GDBLoadedModuleInfoList::LoadedModuleInfo module; llvm::StringRef name = library.GetAttributeValue("name"); module.set_name(name.str()); // The base address of a given library will be the address of its // first section. Most remotes send only one section for Windows // targets for example. const XMLNode §ion = library.FindFirstChildElementWithName("section"); llvm::StringRef address = section.GetAttributeValue("address"); module.set_base(StringConvert::ToUInt64(address.data(), LLDB_INVALID_ADDRESS, 0)); // These addresses are absolute values. module.set_base_is_offset(false); if (log) { std::string name; lldb::addr_t base = 0; bool base_is_offset; module.get_name (name); module.get_base (base); module.get_base_is_offset (base_is_offset); log->Printf ("found (base:0x%08" PRIx64 "[%s], name:'%s')", base, (base_is_offset ? "offset" : "absolute"), name.c_str()); } list.add (module); return true; // Keep iterating over all "library" elements in the root node }); if (log) log->Printf ("found %" PRId32 " modules in total", (int) list.m_list.size()); } else { return Error (0, ErrorType::eErrorTypeGeneric); } return Error(); } lldb::ModuleSP ProcessGDBRemote::LoadModuleAtAddress (const FileSpec &file, lldb::addr_t base_addr, bool value_is_offset) { Target &target = m_process->GetTarget(); ModuleList &modules = target.GetImages(); ModuleSP module_sp; bool changed = false; ModuleSpec module_spec (file, target.GetArchitecture()); if ((module_sp = modules.FindFirstModule (module_spec))) { module_sp->SetLoadAddress (target, base_addr, value_is_offset, changed); } else if ((module_sp = target.GetSharedModule (module_spec))) { module_sp->SetLoadAddress (target, base_addr, value_is_offset, changed); } return module_sp; } size_t ProcessGDBRemote::LoadModules () { using lldb_private::process_gdb_remote::ProcessGDBRemote; // request a list of loaded libraries from GDBServer GDBLoadedModuleInfoList module_list; if (GetLoadedModuleList (module_list).Fail()) return 0; // get a list of all the modules ModuleList new_modules; for (GDBLoadedModuleInfoList::LoadedModuleInfo & modInfo : module_list.m_list) { std::string mod_name; lldb::addr_t mod_base; bool mod_base_is_offset; bool valid = true; valid &= modInfo.get_name (mod_name); valid &= modInfo.get_base (mod_base); valid &= modInfo.get_base_is_offset (mod_base_is_offset); if (!valid) continue; // hack (cleaner way to get file name only?) (win/unix compat?) size_t marker = mod_name.rfind ('/'); if (marker == std::string::npos) marker = 0; else marker += 1; FileSpec file (mod_name.c_str()+marker, true); lldb::ModuleSP module_sp = LoadModuleAtAddress (file, mod_base, mod_base_is_offset); if (module_sp.get()) new_modules.Append (module_sp); } if (new_modules.GetSize() > 0) { Target &target = GetTarget(); new_modules.ForEach ([&target](const lldb::ModuleSP module_sp) -> bool { lldb_private::ObjectFile * obj = module_sp->GetObjectFile (); if (!obj) return true; if (obj->GetType () != ObjectFile::Type::eTypeExecutable) return true; lldb::ModuleSP module_copy_sp = module_sp; target.SetExecutableModule (module_copy_sp, false); return false; }); ModuleList &loaded_modules = m_process->GetTarget().GetImages(); loaded_modules.AppendIfNeeded (new_modules); m_process->GetTarget().ModulesDidLoad (new_modules); } return new_modules.GetSize(); } Error ProcessGDBRemote::GetFileLoadAddress(const FileSpec& file, bool& is_loaded, lldb::addr_t& load_addr) { is_loaded = false; load_addr = LLDB_INVALID_ADDRESS; std::string file_path = file.GetPath(false); if (file_path.empty ()) return Error("Empty file name specified"); StreamString packet; packet.PutCString("qFileLoadAddress:"); packet.PutCStringAsRawHex8(file_path.c_str()); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(), response, false) != GDBRemoteCommunication::PacketResult::Success) return Error("Sending qFileLoadAddress packet failed"); if (response.IsErrorResponse()) { if (response.GetError() == 1) { // The file is not loaded into the inferior is_loaded = false; load_addr = LLDB_INVALID_ADDRESS; return Error(); } return Error("Fetching file load address from remote server returned an error"); } if (response.IsNormalResponse()) { is_loaded = true; load_addr = response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); return Error(); } return Error("Unknown error happened during sending the load address packet"); } void ProcessGDBRemote::ModulesDidLoad (ModuleList &module_list) { // We must call the lldb_private::Process::ModulesDidLoad () first before we do anything Process::ModulesDidLoad (module_list); // After loading shared libraries, we can ask our remote GDB server if // it needs any symbols. m_gdb_comm.ServeSymbolLookups(this); } class CommandObjectProcessGDBRemoteSpeedTest: public CommandObjectParsed { public: CommandObjectProcessGDBRemoteSpeedTest(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "process plugin packet speed-test", "Tests packet speeds of various sizes to determine the performance characteristics of the GDB remote connection. ", NULL), m_option_group (interpreter), m_num_packets (LLDB_OPT_SET_1, false, "count", 'c', 0, eArgTypeCount, "The number of packets to send of each varying size (default is 1000).", 1000), m_max_send (LLDB_OPT_SET_1, false, "max-send", 's', 0, eArgTypeCount, "The maximum number of bytes to send in a packet. Sizes increase in powers of 2 while the size is less than or equal to this option value. (default 1024).", 1024), m_max_recv (LLDB_OPT_SET_1, false, "max-receive", 'r', 0, eArgTypeCount, "The maximum number of bytes to receive in a packet. Sizes increase in powers of 2 while the size is less than or equal to this option value. (default 1024).", 1024), m_json (LLDB_OPT_SET_1, false, "json", 'j', "Print the output as JSON data for easy parsing.", false, true) { m_option_group.Append (&m_num_packets, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_max_send, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_max_recv, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_json, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectProcessGDBRemoteSpeedTest () { } Options * GetOptions () override { return &m_option_group; } bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { StreamSP output_stream_sp (m_interpreter.GetDebugger().GetAsyncOutputStream()); result.SetImmediateOutputStream (output_stream_sp); const uint32_t num_packets = (uint32_t)m_num_packets.GetOptionValue().GetCurrentValue(); const uint64_t max_send = m_max_send.GetOptionValue().GetCurrentValue(); const uint64_t max_recv = m_max_recv.GetOptionValue().GetCurrentValue(); const bool json = m_json.GetOptionValue().GetCurrentValue(); if (output_stream_sp) process->GetGDBRemote().TestPacketSpeed (num_packets, max_send, max_recv, json, *output_stream_sp); else { process->GetGDBRemote().TestPacketSpeed (num_packets, max_send, max_recv, json, result.GetOutputStream()); } result.SetStatus (eReturnStatusSuccessFinishResult); return true; } } else { result.AppendErrorWithFormat ("'%s' takes no arguments", m_cmd_name.c_str()); } result.SetStatus (eReturnStatusFailed); return false; } protected: OptionGroupOptions m_option_group; OptionGroupUInt64 m_num_packets; OptionGroupUInt64 m_max_send; OptionGroupUInt64 m_max_recv; OptionGroupBoolean m_json; }; class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed { private: - + public: CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "process plugin packet history", "Dumps the packet history buffer. ", NULL) { } - + ~CommandObjectProcessGDBRemotePacketHistory () { } - + bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { process->GetGDBRemote().DumpHistory(result.GetOutputStream()); result.SetStatus (eReturnStatusSuccessFinishResult); return true; } } else { result.AppendErrorWithFormat ("'%s' takes no arguments", m_cmd_name.c_str()); } result.SetStatus (eReturnStatusFailed); return false; } }; class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed { private: - + public: CommandObjectProcessGDBRemotePacketXferSize(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "process plugin packet xfer-size", "Maximum size that lldb will try to read/write one one chunk.", NULL) { } - + ~CommandObjectProcessGDBRemotePacketXferSize () { } - + bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { result.AppendErrorWithFormat ("'%s' takes an argument to specify the max amount to be transferred when reading/writing", m_cmd_name.c_str()); result.SetStatus (eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { const char *packet_size = command.GetArgumentAtIndex(0); errno = 0; uint64_t user_specified_max = strtoul (packet_size, NULL, 10); if (errno == 0 && user_specified_max != 0) { process->SetUserSpecifiedMaxMemoryTransferSize (user_specified_max); result.SetStatus (eReturnStatusSuccessFinishResult); return true; } } result.SetStatus (eReturnStatusFailed); return false; } }; class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed { private: - + public: CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "process plugin packet send", "Send a custom packet through the GDB remote protocol and print the answer. " "The packet header and footer will automatically be added to the packet prior to sending and stripped from the result.", NULL) { } - + ~CommandObjectProcessGDBRemotePacketSend () { } - + bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { result.AppendErrorWithFormat ("'%s' takes a one or more packet content arguments", m_cmd_name.c_str()); result.SetStatus (eReturnStatusFailed); return false; } - + ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { for (size_t i=0; iGetGDBRemote().SendPacketAndWaitForResponse(packet_cstr, response, send_async); result.SetStatus (eReturnStatusSuccessFinishResult); Stream &output_strm = result.GetOutputStream(); output_strm.Printf (" packet: %s\n", packet_cstr); std::string &response_str = response.GetStringRef(); - + if (strstr(packet_cstr, "qGetProfileData") != NULL) { response_str = process->GetGDBRemote().HarmonizeThreadIdsForProfileData(process, response); } if (response_str.empty()) output_strm.PutCString ("response: \nerror: UNIMPLEMENTED\n"); else output_strm.Printf ("response: %s\n", response.GetStringRef().c_str()); } } return true; } }; class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw { private: - + public: CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) : CommandObjectRaw (interpreter, "process plugin packet monitor", "Send a qRcmd packet through the GDB remote protocol and print the response." "The argument passed to this command will be hex encoded into a valid 'qRcmd' packet, sent and the response will be printed.", NULL) { } - + ~CommandObjectProcessGDBRemotePacketMonitor () { } - + bool DoExecute (const char *command, CommandReturnObject &result) override { if (command == NULL || command[0] == '\0') { result.AppendErrorWithFormat ("'%s' takes a command string argument", m_cmd_name.c_str()); result.SetStatus (eReturnStatusFailed); return false; } - + ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { StreamString packet; packet.PutCString("qRcmd,"); packet.PutBytesAsRawHex8(command, strlen(command)); const char *packet_cstr = packet.GetString().c_str(); - + bool send_async = true; StringExtractorGDBRemote response; process->GetGDBRemote().SendPacketAndWaitForResponse(packet_cstr, response, send_async); result.SetStatus (eReturnStatusSuccessFinishResult); Stream &output_strm = result.GetOutputStream(); output_strm.Printf (" packet: %s\n", packet_cstr); const std::string &response_str = response.GetStringRef(); - + if (response_str.empty()) output_strm.PutCString ("response: \nerror: UNIMPLEMENTED\n"); else output_strm.Printf ("response: %s\n", response.GetStringRef().c_str()); } return true; } }; class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword { private: - + public: CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "process plugin packet", "Commands that deal with GDB remote packets.", NULL) { LoadSubCommand ("history", CommandObjectSP (new CommandObjectProcessGDBRemotePacketHistory (interpreter))); LoadSubCommand ("send", CommandObjectSP (new CommandObjectProcessGDBRemotePacketSend (interpreter))); LoadSubCommand ("monitor", CommandObjectSP (new CommandObjectProcessGDBRemotePacketMonitor (interpreter))); LoadSubCommand ("xfer-size", CommandObjectSP (new CommandObjectProcessGDBRemotePacketXferSize (interpreter))); LoadSubCommand ("speed-test", CommandObjectSP (new CommandObjectProcessGDBRemoteSpeedTest (interpreter))); } - + ~CommandObjectProcessGDBRemotePacket () { - } + } }; class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword { public: CommandObjectMultiwordProcessGDBRemote (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "process plugin", "A set of commands for operating on a ProcessGDBRemote process.", "process plugin []") { LoadSubCommand ("packet", CommandObjectSP (new CommandObjectProcessGDBRemotePacket (interpreter))); } ~CommandObjectMultiwordProcessGDBRemote () { } }; CommandObject * ProcessGDBRemote::GetPluginCommandObject() { if (!m_command_sp) m_command_sp.reset (new CommandObjectMultiwordProcessGDBRemote (GetTarget().GetDebugger().GetCommandInterpreter())); return m_command_sp.get(); } Index: projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp (revision 293283) @@ -1,96 +1,96 @@ //===-- NameToDIE.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "NameToDIE.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/Stream.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Symbol/ObjectFile.h" #include "DWARFCompileUnit.h" #include "DWARFDebugInfo.h" #include "DWARFDebugInfoEntry.h" #include "SymbolFileDWARF.h" using namespace lldb; using namespace lldb_private; void NameToDIE::Finalize() { m_map.Sort (); m_map.SizeToFit (); } void NameToDIE::Insert (const ConstString& name, const DIERef& die_ref) { m_map.Append(name.GetCString(), die_ref); } size_t NameToDIE::Find (const ConstString &name, DIEArray &info_array) const { return m_map.GetValues (name.GetCString(), info_array); } size_t NameToDIE::Find (const RegularExpression& regex, DIEArray &info_array) const { return m_map.GetValues (regex, info_array); } size_t NameToDIE::FindAllEntriesForCompileUnit (dw_offset_t cu_offset, DIEArray &info_array) const { const size_t initial_size = info_array.size(); const uint32_t size = m_map.GetSize(); for (uint32_t i=0; iPrintf("%p: {0x%8.8x/0x%8.8x} \"%s\"\n", cstr, die_ref.cu_offset, die_ref.die_offset, cstr); + s->Printf("%p: {0x%8.8x/0x%8.8x} \"%s\"\n", (const void*) cstr, die_ref.cu_offset, die_ref.die_offset, cstr); } } void NameToDIE::ForEach (std::function const &callback) const { const uint32_t size = m_map.GetSize(); for (uint32_t i=0; iGetValueProperties()) { } const Property * GetPropertyAtIndex(const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const override { // When getting the value for a key from the process options, we will always // try and grab the setting from the current process if there is one. Else we just // use the one from this instance. if (exe_ctx) { Process *process = exe_ctx->GetProcessPtr(); if (process) { ProcessOptionValueProperties *instance_properties = static_cast(process->GetValueProperties().get()); if (this != instance_properties) return instance_properties->ProtectedGetPropertyAtIndex (idx); } } return ProtectedGetPropertyAtIndex (idx); } }; static PropertyDefinition g_properties[] = { { "disable-memory-cache" , OptionValue::eTypeBoolean, false, DISABLE_MEM_CACHE_DEFAULT, NULL, NULL, "Disable reading and caching of memory in fixed-size units." }, { "extra-startup-command", OptionValue::eTypeArray , false, OptionValue::eTypeString, NULL, NULL, "A list containing extra commands understood by the particular process plugin used. " "For instance, to turn on debugserver logging set this to \"QSetLogging:bitmask=LOG_DEFAULT;\"" }, { "ignore-breakpoints-in-expressions", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, breakpoints will be ignored during expression evaluation." }, { "unwind-on-error-in-expressions", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, errors in expression evaluation will unwind the stack back to the state before the call." }, { "python-os-plugin-path", OptionValue::eTypeFileSpec, false, true, NULL, NULL, "A path to a python OS plug-in module file that contains a OperatingSystemPlugIn class." }, { "stop-on-sharedlibrary-events" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, stop when a shared library is loaded or unloaded." }, { "detach-keeps-stopped" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, detach will attempt to keep the process stopped." }, { "memory-cache-line-size" , OptionValue::eTypeUInt64, false, 512, NULL, NULL, "The memory cache line size" }, { "optimization-warnings" , OptionValue::eTypeBoolean, false, true, NULL, NULL, "If true, warn when stopped in code that is optimized where stepping and variable availability may not behave as expected." }, { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } }; enum { ePropertyDisableMemCache, ePropertyExtraStartCommand, ePropertyIgnoreBreakpointsInExpressions, ePropertyUnwindOnErrorInExpressions, ePropertyPythonOSPluginPath, ePropertyStopOnSharedLibraryEvents, ePropertyDetachKeepsStopped, ePropertyMemCacheLineSize, ePropertyWarningOptimization }; ProcessProperties::ProcessProperties (lldb_private::Process *process) : Properties (), m_process (process) // Can be NULL for global ProcessProperties { if (process == NULL) { // Global process properties, set them up one time m_collection_sp.reset (new ProcessOptionValueProperties(ConstString("process"))); m_collection_sp->Initialize(g_properties); m_collection_sp->AppendProperty(ConstString("thread"), ConstString("Settings specific to threads."), true, Thread::GetGlobalProperties()->GetValueProperties()); } else { m_collection_sp.reset (new ProcessOptionValueProperties(Process::GetGlobalProperties().get())); m_collection_sp->SetValueChangedCallback(ePropertyPythonOSPluginPath, ProcessProperties::OptionValueChangedCallback, this); } } ProcessProperties::~ProcessProperties() = default; void ProcessProperties::OptionValueChangedCallback (void *baton, OptionValue *option_value) { ProcessProperties *properties = (ProcessProperties *)baton; if (properties->m_process) properties->m_process->LoadOperatingSystemPlugin(true); } bool ProcessProperties::GetDisableMemoryCache() const { const uint32_t idx = ePropertyDisableMemCache; return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); } uint64_t ProcessProperties::GetMemoryCacheLineSize() const { const uint32_t idx = ePropertyMemCacheLineSize; return m_collection_sp->GetPropertyAtIndexAsUInt64 (NULL, idx, g_properties[idx].default_uint_value); } Args ProcessProperties::GetExtraStartupCommands () const { Args args; const uint32_t idx = ePropertyExtraStartCommand; m_collection_sp->GetPropertyAtIndexAsArgs(NULL, idx, args); return args; } void ProcessProperties::SetExtraStartupCommands (const Args &args) { const uint32_t idx = ePropertyExtraStartCommand; m_collection_sp->SetPropertyAtIndexFromArgs(NULL, idx, args); } FileSpec ProcessProperties::GetPythonOSPluginPath () const { const uint32_t idx = ePropertyPythonOSPluginPath; return m_collection_sp->GetPropertyAtIndexAsFileSpec(NULL, idx); } void ProcessProperties::SetPythonOSPluginPath (const FileSpec &file) { const uint32_t idx = ePropertyPythonOSPluginPath; m_collection_sp->SetPropertyAtIndexAsFileSpec(NULL, idx, file); } bool ProcessProperties::GetIgnoreBreakpointsInExpressions () const { const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions; return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value != 0); } void ProcessProperties::SetIgnoreBreakpointsInExpressions (bool ignore) { const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions; m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, ignore); } bool ProcessProperties::GetUnwindOnErrorInExpressions () const { const uint32_t idx = ePropertyUnwindOnErrorInExpressions; return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value != 0); } void ProcessProperties::SetUnwindOnErrorInExpressions (bool ignore) { const uint32_t idx = ePropertyUnwindOnErrorInExpressions; m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, ignore); } bool ProcessProperties::GetStopOnSharedLibraryEvents () const { const uint32_t idx = ePropertyStopOnSharedLibraryEvents; return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value != 0); } void ProcessProperties::SetStopOnSharedLibraryEvents (bool stop) { const uint32_t idx = ePropertyStopOnSharedLibraryEvents; m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, stop); } bool ProcessProperties::GetDetachKeepsStopped () const { const uint32_t idx = ePropertyDetachKeepsStopped; return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value != 0); } void ProcessProperties::SetDetachKeepsStopped (bool stop) { const uint32_t idx = ePropertyDetachKeepsStopped; m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, stop); } bool ProcessProperties::GetWarningsOptimization () const { const uint32_t idx = ePropertyWarningOptimization; return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); } void ProcessInstanceInfo::Dump (Stream &s, Platform *platform) const { const char *cstr; if (m_pid != LLDB_INVALID_PROCESS_ID) s.Printf (" pid = %" PRIu64 "\n", m_pid); if (m_parent_pid != LLDB_INVALID_PROCESS_ID) s.Printf (" parent = %" PRIu64 "\n", m_parent_pid); if (m_executable) { s.Printf (" name = %s\n", m_executable.GetFilename().GetCString()); s.PutCString (" file = "); m_executable.Dump(&s); s.EOL(); } const uint32_t argc = m_arguments.GetArgumentCount(); if (argc > 0) { for (uint32_t i=0; i 0) { for (uint32_t i=0; iGetGroupName (m_gid); s.Printf (" gid = %-5u (%s)\n", m_gid, cstr ? cstr : ""); } if (m_euid != UINT32_MAX) { cstr = platform->GetUserName (m_euid); s.Printf (" euid = %-5u (%s)\n", m_euid, cstr ? cstr : ""); } if (m_egid != UINT32_MAX) { cstr = platform->GetGroupName (m_egid); s.Printf (" egid = %-5u (%s)\n", m_egid, cstr ? cstr : ""); } } void ProcessInstanceInfo::DumpTableHeader (Stream &s, Platform *platform, bool show_args, bool verbose) { const char *label; if (show_args || verbose) label = "ARGUMENTS"; else label = "NAME"; if (verbose) { s.Printf ("PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE %s\n", label); s.PutCString ("====== ====== ========== ========== ========== ========== ======================== ============================\n"); } else { s.Printf ("PID PARENT USER TRIPLE %s\n", label); s.PutCString ("====== ====== ========== ======================== ============================\n"); } } void ProcessInstanceInfo::DumpAsTableRow (Stream &s, Platform *platform, bool show_args, bool verbose) const { if (m_pid != LLDB_INVALID_PROCESS_ID) { const char *cstr; s.Printf ("%-6" PRIu64 " %-6" PRIu64 " ", m_pid, m_parent_pid); StreamString arch_strm; if (m_arch.IsValid()) m_arch.DumpTriple(arch_strm); if (verbose) { cstr = platform->GetUserName (m_uid); if (cstr && cstr[0]) // Watch for empty string that indicates lookup failed s.Printf ("%-10s ", cstr); else s.Printf ("%-10u ", m_uid); cstr = platform->GetGroupName (m_gid); if (cstr && cstr[0]) // Watch for empty string that indicates lookup failed s.Printf ("%-10s ", cstr); else s.Printf ("%-10u ", m_gid); cstr = platform->GetUserName (m_euid); if (cstr && cstr[0]) // Watch for empty string that indicates lookup failed s.Printf ("%-10s ", cstr); else s.Printf ("%-10u ", m_euid); cstr = platform->GetGroupName (m_egid); if (cstr && cstr[0]) // Watch for empty string that indicates lookup failed s.Printf ("%-10s ", cstr); else s.Printf ("%-10u ", m_egid); s.Printf ("%-24s ", arch_strm.GetString().c_str()); } else { s.Printf ("%-10s %-24s ", platform->GetUserName (m_euid), arch_strm.GetString().c_str()); } if (verbose || show_args) { const uint32_t argc = m_arguments.GetArgumentCount(); if (argc > 0) { for (uint32_t i=0; i 0) s.PutChar (' '); s.PutCString (m_arguments.GetArgumentAtIndex(i)); } } } else { s.PutCString (GetName()); } s.EOL(); } } Error ProcessLaunchCommandOptions::SetOptionValue (uint32_t option_idx, const char *option_arg) { Error error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 's': // Stop at program entry point launch_info.GetFlags().Set (eLaunchFlagStopAtEntry); break; case 'i': // STDIN for read only { FileAction action; if (action.Open(STDIN_FILENO, FileSpec{option_arg, false}, true, false)) launch_info.AppendFileAction (action); break; } case 'o': // Open STDOUT for write only { FileAction action; if (action.Open(STDOUT_FILENO, FileSpec{option_arg, false}, false, true)) launch_info.AppendFileAction (action); break; } case 'e': // STDERR for write only { FileAction action; if (action.Open(STDERR_FILENO, FileSpec{option_arg, false}, false, true)) launch_info.AppendFileAction (action); break; } case 'p': // Process plug-in name launch_info.SetProcessPluginName (option_arg); break; case 'n': // Disable STDIO { FileAction action; const FileSpec dev_null{FileSystem::DEV_NULL, false}; if (action.Open(STDIN_FILENO, dev_null, true, false)) launch_info.AppendFileAction (action); if (action.Open(STDOUT_FILENO, dev_null, false, true)) launch_info.AppendFileAction (action); if (action.Open(STDERR_FILENO, dev_null, false, true)) launch_info.AppendFileAction (action); break; } case 'w': launch_info.SetWorkingDirectory(FileSpec{option_arg, false}); break; case 't': // Open process in new terminal window launch_info.GetFlags().Set (eLaunchFlagLaunchInTTY); break; case 'a': if (!launch_info.GetArchitecture().SetTriple (option_arg, m_interpreter.GetPlatform(true).get())) launch_info.GetArchitecture().SetTriple (option_arg); break; case 'A': // Disable ASLR. { bool success; const bool disable_aslr_arg = Args::StringToBoolean (option_arg, true, &success); if (success) disable_aslr = disable_aslr_arg ? eLazyBoolYes : eLazyBoolNo; else error.SetErrorStringWithFormat ("Invalid boolean value for disable-aslr option: '%s'", option_arg ? option_arg : ""); break; } case 'X': // shell expand args. { bool success; const bool expand_args = Args::StringToBoolean (option_arg, true, &success); if (success) launch_info.SetShellExpandArguments(expand_args); else error.SetErrorStringWithFormat ("Invalid boolean value for shell-expand-args option: '%s'", option_arg ? option_arg : ""); break; } case 'c': if (option_arg && option_arg[0]) launch_info.SetShell (FileSpec(option_arg, false)); else launch_info.SetShell (HostInfo::GetDefaultShell()); break; case 'v': launch_info.GetEnvironmentEntries().AppendArgument(option_arg); break; default: error.SetErrorStringWithFormat("unrecognized short option character '%c'", short_option); break; } return error; } OptionDefinition ProcessLaunchCommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "stop-at-entry", 's', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Stop at the entry point of the program when launching a process."}, { LLDB_OPT_SET_ALL, false, "disable-aslr", 'A', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean, "Set whether to disable address space layout randomization when launching a process."}, { LLDB_OPT_SET_ALL, false, "plugin", 'p', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypePlugin, "Name of the process plugin you want to use."}, { LLDB_OPT_SET_ALL, false, "working-dir", 'w', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeDirectoryName, "Set the current working directory to when running the inferior."}, { LLDB_OPT_SET_ALL, false, "arch", 'a', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeArchitecture, "Set the architecture for the process to launch when ambiguous."}, { LLDB_OPT_SET_ALL, false, "environment", 'v', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeNone, "Specify an environment variable name/value string (--environment NAME=VALUE). Can be specified multiple times for subsequent environment entries."}, { LLDB_OPT_SET_1|LLDB_OPT_SET_2|LLDB_OPT_SET_3, false, "shell", 'c', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeFilename, "Run the process in a shell (not supported on all platforms)."}, { LLDB_OPT_SET_1 , false, "stdin", 'i', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFilename, "Redirect stdin for the process to ."}, { LLDB_OPT_SET_1 , false, "stdout", 'o', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFilename, "Redirect stdout for the process to ."}, { LLDB_OPT_SET_1 , false, "stderr", 'e', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFilename, "Redirect stderr for the process to ."}, { LLDB_OPT_SET_2 , false, "tty", 't', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Start the process in a terminal (not supported on all platforms)."}, { LLDB_OPT_SET_3 , false, "no-stdio", 'n', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Do not set up for terminal I/O to go to running process."}, { LLDB_OPT_SET_4, false, "shell-expand-args", 'X', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean, "Set whether to shell expand arguments to the process when launching."}, { 0 , false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; bool ProcessInstanceInfoMatch::NameMatches (const char *process_name) const { if (m_name_match_type == eNameMatchIgnore || process_name == NULL) return true; const char *match_name = m_match_info.GetName(); if (!match_name) return true; return lldb_private::NameMatches (process_name, m_name_match_type, match_name); } bool ProcessInstanceInfoMatch::Matches (const ProcessInstanceInfo &proc_info) const { if (!NameMatches (proc_info.GetName())) return false; if (m_match_info.ProcessIDIsValid() && m_match_info.GetProcessID() != proc_info.GetProcessID()) return false; if (m_match_info.ParentProcessIDIsValid() && m_match_info.GetParentProcessID() != proc_info.GetParentProcessID()) return false; if (m_match_info.UserIDIsValid () && m_match_info.GetUserID() != proc_info.GetUserID()) return false; if (m_match_info.GroupIDIsValid () && m_match_info.GetGroupID() != proc_info.GetGroupID()) return false; if (m_match_info.EffectiveUserIDIsValid () && m_match_info.GetEffectiveUserID() != proc_info.GetEffectiveUserID()) return false; if (m_match_info.EffectiveGroupIDIsValid () && m_match_info.GetEffectiveGroupID() != proc_info.GetEffectiveGroupID()) return false; if (m_match_info.GetArchitecture().IsValid() && !m_match_info.GetArchitecture().IsCompatibleMatch(proc_info.GetArchitecture())) return false; return true; } bool ProcessInstanceInfoMatch::MatchAllProcesses () const { if (m_name_match_type != eNameMatchIgnore) return false; if (m_match_info.ProcessIDIsValid()) return false; if (m_match_info.ParentProcessIDIsValid()) return false; if (m_match_info.UserIDIsValid ()) return false; if (m_match_info.GroupIDIsValid ()) return false; if (m_match_info.EffectiveUserIDIsValid ()) return false; if (m_match_info.EffectiveGroupIDIsValid ()) return false; if (m_match_info.GetArchitecture().IsValid()) return false; if (m_match_all_users) return false; return true; } void ProcessInstanceInfoMatch::Clear() { m_match_info.Clear(); m_name_match_type = eNameMatchIgnore; m_match_all_users = false; } ProcessSP Process::FindPlugin (lldb::TargetSP target_sp, const char *plugin_name, Listener &listener, const FileSpec *crash_file_path) { static uint32_t g_process_unique_id = 0; ProcessSP process_sp; ProcessCreateInstance create_callback = NULL; if (plugin_name) { ConstString const_plugin_name(plugin_name); create_callback = PluginManager::GetProcessCreateCallbackForPluginName (const_plugin_name); if (create_callback) { process_sp = create_callback(target_sp, listener, crash_file_path); if (process_sp) { if (process_sp->CanDebug(target_sp, true)) { process_sp->m_process_unique_id = ++g_process_unique_id; } else process_sp.reset(); } } } else { for (uint32_t idx = 0; (create_callback = PluginManager::GetProcessCreateCallbackAtIndex(idx)) != NULL; ++idx) { process_sp = create_callback(target_sp, listener, crash_file_path); if (process_sp) { if (process_sp->CanDebug(target_sp, false)) { process_sp->m_process_unique_id = ++g_process_unique_id; break; } else process_sp.reset(); } } } return process_sp; } ConstString & Process::GetStaticBroadcasterClass () { static ConstString class_name ("lldb.process"); return class_name; } Process::Process(lldb::TargetSP target_sp, Listener &listener) : Process(target_sp, listener, UnixSignals::Create(HostInfo::GetArchitecture())) { // This constructor just delegates to the full Process constructor, // defaulting to using the Host's UnixSignals. } Process::Process(lldb::TargetSP target_sp, Listener &listener, const UnixSignalsSP &unix_signals_sp) : ProcessProperties (this), UserID (LLDB_INVALID_PROCESS_ID), Broadcaster (&(target_sp->GetDebugger()), Process::GetStaticBroadcasterClass().AsCString()), m_target_sp (target_sp), m_public_state (eStateUnloaded), m_private_state (eStateUnloaded), m_private_state_broadcaster (NULL, "lldb.process.internal_state_broadcaster"), m_private_state_control_broadcaster (NULL, "lldb.process.internal_state_control_broadcaster"), m_private_state_listener ("lldb.process.internal_state_listener"), m_private_state_control_wait(), m_mod_id (), m_process_unique_id(0), m_thread_index_id (0), m_thread_id_to_index_id_map (), m_exit_status (-1), m_exit_string (), m_exit_status_mutex(), m_thread_mutex (Mutex::eMutexTypeRecursive), m_thread_list_real (this), m_thread_list (this), m_extended_thread_list (this), m_extended_thread_stop_id (0), m_queue_list (this), m_queue_list_stop_id (0), m_notifications (), m_image_tokens (), m_listener (listener), m_breakpoint_site_list (), m_dynamic_checkers_ap (), m_unix_signals_sp (unix_signals_sp), m_abi_sp (), m_process_input_reader (), m_stdio_communication ("process.stdio"), m_stdio_communication_mutex (Mutex::eMutexTypeRecursive), m_stdin_forward (false), m_stdout_data (), m_stderr_data (), m_profile_data_comm_mutex (Mutex::eMutexTypeRecursive), m_profile_data (), m_iohandler_sync (0), m_memory_cache (*this), m_allocated_memory_cache (*this), m_should_detach (false), m_next_event_action_ap(), m_public_run_lock (), m_private_run_lock (), m_stop_info_override_callback (NULL), m_finalizing (false), m_finalize_called (false), m_clear_thread_plans_on_stop (false), m_force_next_event_delivery (false), m_last_broadcast_state (eStateInvalid), m_destroy_in_process (false), m_can_interpret_function_calls(false), m_warnings_issued (), m_can_jit(eCanJITDontKnow) { CheckInWithManager (); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); if (log) log->Printf ("%p Process::Process()", static_cast(this)); if (!m_unix_signals_sp) m_unix_signals_sp = std::make_shared(); SetEventName (eBroadcastBitStateChanged, "state-changed"); SetEventName (eBroadcastBitInterrupt, "interrupt"); SetEventName (eBroadcastBitSTDOUT, "stdout-available"); SetEventName (eBroadcastBitSTDERR, "stderr-available"); SetEventName (eBroadcastBitProfileData, "profile-data-available"); m_private_state_control_broadcaster.SetEventName (eBroadcastInternalStateControlStop , "control-stop" ); m_private_state_control_broadcaster.SetEventName (eBroadcastInternalStateControlPause , "control-pause" ); m_private_state_control_broadcaster.SetEventName (eBroadcastInternalStateControlResume, "control-resume"); listener.StartListeningForEvents (this, eBroadcastBitStateChanged | eBroadcastBitInterrupt | eBroadcastBitSTDOUT | eBroadcastBitSTDERR | eBroadcastBitProfileData); m_private_state_listener.StartListeningForEvents(&m_private_state_broadcaster, eBroadcastBitStateChanged | eBroadcastBitInterrupt); m_private_state_listener.StartListeningForEvents(&m_private_state_control_broadcaster, eBroadcastInternalStateControlStop | eBroadcastInternalStateControlPause | eBroadcastInternalStateControlResume); // We need something valid here, even if just the default UnixSignalsSP. assert (m_unix_signals_sp && "null m_unix_signals_sp after initialization"); // Allow the platform to override the default cache line size OptionValueSP value_sp = m_collection_sp->GetPropertyAtIndex(nullptr, true, ePropertyMemCacheLineSize)->GetValue(); uint32_t platform_cache_line_size = target_sp->GetPlatform()->GetDefaultMemoryCacheLineSize(); if (! value_sp->OptionWasSet() && platform_cache_line_size != 0) value_sp->SetUInt64Value(platform_cache_line_size); } Process::~Process() { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); if (log) log->Printf ("%p Process::~Process()", static_cast(this)); StopPrivateStateThread(); // ThreadList::Clear() will try to acquire this process's mutex, so // explicitly clear the thread list here to ensure that the mutex // is not destroyed before the thread list. m_thread_list.Clear(); } const ProcessPropertiesSP & Process::GetGlobalProperties() { static ProcessPropertiesSP g_settings_sp; if (!g_settings_sp) g_settings_sp.reset (new ProcessProperties (NULL)); return g_settings_sp; } void Process::Finalize() { m_finalizing = true; // Destroy this process if needed switch (GetPrivateState()) { case eStateConnected: case eStateAttaching: case eStateLaunching: case eStateStopped: case eStateRunning: case eStateStepping: case eStateCrashed: case eStateSuspended: Destroy(false); break; case eStateInvalid: case eStateUnloaded: case eStateDetached: case eStateExited: break; } // Clear our broadcaster before we proceed with destroying Broadcaster::Clear(); // Do any cleanup needed prior to being destructed... Subclasses // that override this method should call this superclass method as well. // We need to destroy the loader before the derived Process class gets destroyed // since it is very likely that undoing the loader will require access to the real process. m_dynamic_checkers_ap.reset(); m_abi_sp.reset(); m_os_ap.reset(); m_system_runtime_ap.reset(); m_dyld_ap.reset(); m_jit_loaders_ap.reset(); m_thread_list_real.Destroy(); m_thread_list.Destroy(); m_extended_thread_list.Destroy(); m_queue_list.Clear(); m_queue_list_stop_id = 0; std::vector empty_notifications; m_notifications.swap(empty_notifications); m_image_tokens.clear(); m_memory_cache.Clear(); m_allocated_memory_cache.Clear(); m_language_runtimes.clear(); m_instrumentation_runtimes.clear(); m_next_event_action_ap.reset(); m_stop_info_override_callback = NULL; // Clear the last natural stop ID since it has a strong // reference to this process m_mod_id.SetStopEventForLastNaturalStopID(EventSP()); //#ifdef LLDB_CONFIGURATION_DEBUG // StreamFile s(stdout, false); // EventSP event_sp; // while (m_private_state_listener.GetNextEvent(event_sp)) // { // event_sp->Dump (&s); // s.EOL(); // } //#endif // We have to be very careful here as the m_private_state_listener might // contain events that have ProcessSP values in them which can keep this // process around forever. These events need to be cleared out. m_private_state_listener.Clear(); m_public_run_lock.TrySetRunning(); // This will do nothing if already locked m_public_run_lock.SetStopped(); m_private_run_lock.TrySetRunning(); // This will do nothing if already locked m_private_run_lock.SetStopped(); m_finalize_called = true; } void Process::RegisterNotificationCallbacks (const Notifications& callbacks) { m_notifications.push_back(callbacks); if (callbacks.initialize != NULL) callbacks.initialize (callbacks.baton, this); } bool Process::UnregisterNotificationCallbacks(const Notifications& callbacks) { std::vector::iterator pos, end = m_notifications.end(); for (pos = m_notifications.begin(); pos != end; ++pos) { if (pos->baton == callbacks.baton && pos->initialize == callbacks.initialize && pos->process_state_changed == callbacks.process_state_changed) { m_notifications.erase(pos); return true; } } return false; } void Process::SynchronouslyNotifyStateChanged (StateType state) { std::vector::iterator notification_pos, notification_end = m_notifications.end(); for (notification_pos = m_notifications.begin(); notification_pos != notification_end; ++notification_pos) { if (notification_pos->process_state_changed) notification_pos->process_state_changed (notification_pos->baton, this, state); } } // FIXME: We need to do some work on events before the general Listener sees them. // For instance if we are continuing from a breakpoint, we need to ensure that we do // the little "insert real insn, step & stop" trick. But we can't do that when the // event is delivered by the broadcaster - since that is done on the thread that is // waiting for new events, so if we needed more than one event for our handling, we would // stall. So instead we do it when we fetch the event off of the queue. // StateType Process::GetNextEvent (EventSP &event_sp) { StateType state = eStateInvalid; if (m_listener.GetNextEventForBroadcaster (this, event_sp) && event_sp) state = Process::ProcessEventData::GetStateFromEvent (event_sp.get()); return state; } void Process::SyncIOHandler (uint32_t iohandler_id, uint64_t timeout_msec) { // don't sync (potentially context switch) in case where there is no process IO if (! m_process_input_reader) return; TimeValue timeout = TimeValue::Now(); timeout.OffsetWithMicroSeconds(timeout_msec*1000); uint32_t new_iohandler_id = 0; m_iohandler_sync.WaitForValueNotEqualTo(iohandler_id, new_iohandler_id, &timeout); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::%s waited for m_iohandler_sync to change from %u, new value is %u", __FUNCTION__, iohandler_id, new_iohandler_id); } StateType Process::WaitForProcessToStop (const TimeValue *timeout, EventSP *event_sp_ptr, bool wait_always, Listener *hijack_listener, Stream *stream, bool use_run_lock) { // We can't just wait for a "stopped" event, because the stopped event may have restarted the target. // We have to actually check each event, and in the case of a stopped event check the restarted flag // on the event. if (event_sp_ptr) event_sp_ptr->reset(); StateType state = GetState(); // If we are exited or detached, we won't ever get back to any // other valid state... if (state == eStateDetached || state == eStateExited) return state; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::%s (timeout = %p)", __FUNCTION__, static_cast(timeout)); if (!wait_always && StateIsStoppedState(state, true) && StateIsStoppedState(GetPrivateState(), true)) { if (log) log->Printf("Process::%s returning without waiting for events; process private and public states are already 'stopped'.", __FUNCTION__); // We need to toggle the run lock as this won't get done in // SetPublicState() if the process is hijacked. if (hijack_listener && use_run_lock) m_public_run_lock.SetStopped(); return state; } while (state != eStateInvalid) { EventSP event_sp; state = WaitForStateChangedEvents (timeout, event_sp, hijack_listener); if (event_sp_ptr && event_sp) *event_sp_ptr = event_sp; bool pop_process_io_handler = hijack_listener != NULL; Process::HandleProcessStateChangedEvent (event_sp, stream, pop_process_io_handler); switch (state) { case eStateCrashed: case eStateDetached: case eStateExited: case eStateUnloaded: // We need to toggle the run lock as this won't get done in // SetPublicState() if the process is hijacked. if (hijack_listener && use_run_lock) m_public_run_lock.SetStopped(); return state; case eStateStopped: if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) continue; else { // We need to toggle the run lock as this won't get done in // SetPublicState() if the process is hijacked. if (hijack_listener && use_run_lock) m_public_run_lock.SetStopped(); return state; } default: continue; } } return state; } bool Process::HandleProcessStateChangedEvent (const EventSP &event_sp, Stream *stream, bool &pop_process_io_handler) { const bool handle_pop = pop_process_io_handler == true; pop_process_io_handler = false; ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); if (!process_sp) return false; StateType event_state = Process::ProcessEventData::GetStateFromEvent (event_sp.get()); if (event_state == eStateInvalid) return false; switch (event_state) { case eStateInvalid: case eStateUnloaded: case eStateAttaching: case eStateLaunching: case eStateStepping: case eStateDetached: { if (stream) stream->Printf ("Process %" PRIu64 " %s\n", process_sp->GetID(), StateAsCString (event_state)); if (event_state == eStateDetached) pop_process_io_handler = true; } break; case eStateConnected: case eStateRunning: // Don't be chatty when we run... break; case eStateExited: if (stream) process_sp->GetStatus(*stream); pop_process_io_handler = true; break; case eStateStopped: case eStateCrashed: case eStateSuspended: // Make sure the program hasn't been auto-restarted: if (Process::ProcessEventData::GetRestartedFromEvent (event_sp.get())) { if (stream) { size_t num_reasons = Process::ProcessEventData::GetNumRestartedReasons(event_sp.get()); if (num_reasons > 0) { // FIXME: Do we want to report this, or would that just be annoyingly chatty? if (num_reasons == 1) { const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), 0); stream->Printf ("Process %" PRIu64 " stopped and restarted: %s\n", process_sp->GetID(), reason ? reason : ""); } else { stream->Printf ("Process %" PRIu64 " stopped and restarted, reasons:\n", process_sp->GetID()); for (size_t i = 0; i < num_reasons; i++) { const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), i); stream->Printf("\t%s\n", reason ? reason : ""); } } } } } else { // Lock the thread list so it doesn't change on us, this is the scope for the locker: { ThreadList &thread_list = process_sp->GetThreadList(); Mutex::Locker locker (thread_list.GetMutex()); ThreadSP curr_thread (thread_list.GetSelectedThread()); ThreadSP thread; StopReason curr_thread_stop_reason = eStopReasonInvalid; if (curr_thread) curr_thread_stop_reason = curr_thread->GetStopReason(); if (!curr_thread || !curr_thread->IsValid() || curr_thread_stop_reason == eStopReasonInvalid || curr_thread_stop_reason == eStopReasonNone) { // Prefer a thread that has just completed its plan over another thread as current thread. ThreadSP plan_thread; ThreadSP other_thread; const size_t num_threads = thread_list.GetSize(); size_t i; for (i = 0; i < num_threads; ++i) { thread = thread_list.GetThreadAtIndex(i); StopReason thread_stop_reason = thread->GetStopReason(); switch (thread_stop_reason) { case eStopReasonInvalid: case eStopReasonNone: break; case eStopReasonSignal: { // Don't select a signal thread if we weren't going to stop at that // signal. We have to have had another reason for stopping here, and // the user doesn't want to see this thread. uint64_t signo = thread->GetStopInfo()->GetValue(); if (process_sp->GetUnixSignals()->GetShouldStop(signo)) { if (!other_thread) other_thread = thread; } break; } case eStopReasonTrace: case eStopReasonBreakpoint: case eStopReasonWatchpoint: case eStopReasonException: case eStopReasonExec: case eStopReasonThreadExiting: case eStopReasonInstrumentation: if (!other_thread) other_thread = thread; break; case eStopReasonPlanComplete: if (!plan_thread) plan_thread = thread; break; } } if (plan_thread) thread_list.SetSelectedThreadByID (plan_thread->GetID()); else if (other_thread) thread_list.SetSelectedThreadByID (other_thread->GetID()); else { if (curr_thread && curr_thread->IsValid()) thread = curr_thread; else thread = thread_list.GetThreadAtIndex(0); if (thread) thread_list.SetSelectedThreadByID (thread->GetID()); } } } // Drop the ThreadList mutex by here, since GetThreadStatus below might have to run code, // e.g. for Data formatters, and if we hold the ThreadList mutex, then the process is going to // have a hard time restarting the process. if (stream) { Debugger &debugger = process_sp->GetTarget().GetDebugger(); if (debugger.GetTargetList().GetSelectedTarget().get() == &process_sp->GetTarget()) { const bool only_threads_with_stop_reason = true; const uint32_t start_frame = 0; const uint32_t num_frames = 1; const uint32_t num_frames_with_source = 1; process_sp->GetStatus(*stream); process_sp->GetThreadStatus (*stream, only_threads_with_stop_reason, start_frame, num_frames, num_frames_with_source); } else { uint32_t target_idx = debugger.GetTargetList().GetIndexOfTarget(process_sp->GetTarget().shared_from_this()); if (target_idx != UINT32_MAX) stream->Printf ("Target %d: (", target_idx); else stream->Printf ("Target : ("); process_sp->GetTarget().Dump (stream, eDescriptionLevelBrief); stream->Printf (") stopped.\n"); } } // Pop the process IO handler pop_process_io_handler = true; } break; } if (handle_pop && pop_process_io_handler) process_sp->PopProcessIOHandler(); return true; } StateType Process::WaitForState(const TimeValue *timeout, const StateType *match_states, const uint32_t num_match_states) { EventSP event_sp; uint32_t i; StateType state = GetState(); while (state != eStateInvalid) { // If we are exited or detached, we won't ever get back to any // other valid state... if (state == eStateDetached || state == eStateExited) return state; state = WaitForStateChangedEvents (timeout, event_sp, NULL); for (i=0; iPrintf ("Process::%s (timeout = %p, event_sp)...", __FUNCTION__, static_cast(timeout)); Listener *listener = hijack_listener; if (listener == NULL) listener = &m_listener; StateType state = eStateInvalid; if (listener->WaitForEventForBroadcasterWithType (timeout, this, eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp)) { if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged) state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); else if (log) log->Printf ("Process::%s got no event or was interrupted.", __FUNCTION__); } if (log) log->Printf ("Process::%s (timeout = %p, event_sp) => %s", __FUNCTION__, static_cast(timeout), StateAsCString(state)); return state; } Event * Process::PeekAtStateChangedEvents () { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::%s...", __FUNCTION__); Event *event_ptr; event_ptr = m_listener.PeekAtNextEventForBroadcasterWithType (this, eBroadcastBitStateChanged); if (log) { if (event_ptr) { log->Printf ("Process::%s (event_ptr) => %s", __FUNCTION__, StateAsCString(ProcessEventData::GetStateFromEvent (event_ptr))); } else { log->Printf ("Process::%s no events found", __FUNCTION__); } } return event_ptr; } StateType Process::WaitForStateChangedEventsPrivate (const TimeValue *timeout, EventSP &event_sp) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::%s (timeout = %p, event_sp)...", __FUNCTION__, static_cast(timeout)); StateType state = eStateInvalid; if (m_private_state_listener.WaitForEventForBroadcasterWithType (timeout, &m_private_state_broadcaster, eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp)) if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged) state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); // This is a bit of a hack, but when we wait here we could very well return // to the command-line, and that could disable the log, which would render the // log we got above invalid. if (log) log->Printf ("Process::%s (timeout = %p, event_sp) => %s", __FUNCTION__, static_cast(timeout), state == eStateInvalid ? "TIMEOUT" : StateAsCString(state)); return state; } bool Process::WaitForEventsPrivate (const TimeValue *timeout, EventSP &event_sp, bool control_only) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::%s (timeout = %p, event_sp)...", __FUNCTION__, static_cast(timeout)); if (control_only) return m_private_state_listener.WaitForEventForBroadcaster(timeout, &m_private_state_control_broadcaster, event_sp); else return m_private_state_listener.WaitForEvent(timeout, event_sp); } bool Process::IsRunning () const { return StateIsRunningState (m_public_state.GetValue()); } int Process::GetExitStatus () { Mutex::Locker locker (m_exit_status_mutex); if (m_public_state.GetValue() == eStateExited) return m_exit_status; return -1; } const char * Process::GetExitDescription () { Mutex::Locker locker (m_exit_status_mutex); if (m_public_state.GetValue() == eStateExited && !m_exit_string.empty()) return m_exit_string.c_str(); return NULL; } bool Process::SetExitStatus (int status, const char *cstr) { // Use a mutex to protect setting the exit status. Mutex::Locker locker (m_exit_status_mutex); Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::SetExitStatus (status=%i (0x%8.8x), description=%s%s%s)", status, status, cstr ? "\"" : "", cstr ? cstr : "NULL", cstr ? "\"" : ""); // We were already in the exited state if (m_private_state.GetValue() == eStateExited) { if (log) log->Printf("Process::SetExitStatus () ignoring exit status because state was already set to eStateExited"); return false; } m_exit_status = status; if (cstr) m_exit_string = cstr; else m_exit_string.clear(); // When we exit, we don't need the input reader anymore if (m_process_input_reader) { m_process_input_reader->SetIsDone(true); m_process_input_reader->Cancel(); m_process_input_reader.reset(); } // Clear the last natural stop ID since it has a strong // reference to this process m_mod_id.SetStopEventForLastNaturalStopID(EventSP()); SetPrivateState (eStateExited); // Allow subclasses to do some cleanup DidExit (); return true; } bool Process::IsAlive () { switch (m_private_state.GetValue()) { case eStateInvalid: case eStateUnloaded: case eStateDetached: case eStateExited: return false; case eStateConnected: case eStateAttaching: case eStateLaunching: case eStateStopped: case eStateRunning: case eStateStepping: case eStateCrashed: case eStateSuspended: return true; } } // This static callback can be used to watch for local child processes on // the current host. The child process exits, the process will be // found in the global target list (we want to be completely sure that the // lldb_private::Process doesn't go away before we can deliver the signal. bool Process::SetProcessExitStatus (void *callback_baton, lldb::pid_t pid, bool exited, int signo, // Zero for no signal int exit_status // Exit value of process if signal is zero ) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::SetProcessExitStatus (baton=%p, pid=%" PRIu64 ", exited=%i, signal=%i, exit_status=%i)\n", callback_baton, pid, exited, signo, exit_status); if (exited) { TargetSP target_sp(Debugger::FindTargetWithProcessID (pid)); if (target_sp) { ProcessSP process_sp (target_sp->GetProcessSP()); if (process_sp) { const char *signal_cstr = NULL; if (signo) signal_cstr = process_sp->GetUnixSignals()->GetSignalAsCString(signo); process_sp->SetExitStatus (exit_status, signal_cstr); } } return true; } return false; } void Process::UpdateThreadListIfNeeded () { const uint32_t stop_id = GetStopID(); if (m_thread_list.GetSize(false) == 0 || stop_id != m_thread_list.GetStopID()) { const StateType state = GetPrivateState(); if (StateIsStoppedState (state, true)) { Mutex::Locker locker (m_thread_list.GetMutex ()); // m_thread_list does have its own mutex, but we need to // hold onto the mutex between the call to UpdateThreadList(...) // and the os->UpdateThreadList(...) so it doesn't change on us ThreadList &old_thread_list = m_thread_list; ThreadList real_thread_list(this); ThreadList new_thread_list(this); // Always update the thread list with the protocol specific // thread list, but only update if "true" is returned if (UpdateThreadList (m_thread_list_real, real_thread_list)) { // Don't call into the OperatingSystem to update the thread list if we are shutting down, since // that may call back into the SBAPI's, requiring the API lock which is already held by whoever is // shutting us down, causing a deadlock. OperatingSystem *os = GetOperatingSystem (); if (os && !m_destroy_in_process) { // Clear any old backing threads where memory threads might have been // backed by actual threads from the lldb_private::Process subclass size_t num_old_threads = old_thread_list.GetSize(false); for (size_t i=0; iClearBackingThread(); // Turn off dynamic types to ensure we don't run any expressions. Objective C // can run an expression to determine if a SBValue is a dynamic type or not // and we need to avoid this. OperatingSystem plug-ins can't run expressions // that require running code... Target &target = GetTarget(); const lldb::DynamicValueType saved_prefer_dynamic = target.GetPreferDynamicValue (); if (saved_prefer_dynamic != lldb::eNoDynamicValues) target.SetPreferDynamicValue(lldb::eNoDynamicValues); // Now let the OperatingSystem plug-in update the thread list os->UpdateThreadList (old_thread_list, // Old list full of threads created by OS plug-in real_thread_list, // The actual thread list full of threads created by each lldb_private::Process subclass new_thread_list); // The new thread list that we will show to the user that gets filled in if (saved_prefer_dynamic != lldb::eNoDynamicValues) target.SetPreferDynamicValue(saved_prefer_dynamic); } else { // No OS plug-in, the new thread list is the same as the real thread list new_thread_list = real_thread_list; } m_thread_list_real.Update(real_thread_list); m_thread_list.Update (new_thread_list); m_thread_list.SetStopID (stop_id); if (GetLastNaturalStopID () != m_extended_thread_stop_id) { // Clear any extended threads that we may have accumulated previously m_extended_thread_list.Clear(); m_extended_thread_stop_id = GetLastNaturalStopID (); m_queue_list.Clear(); m_queue_list_stop_id = GetLastNaturalStopID (); } } } } } void Process::UpdateQueueListIfNeeded () { if (m_system_runtime_ap.get()) { if (m_queue_list.GetSize() == 0 || m_queue_list_stop_id != GetLastNaturalStopID()) { const StateType state = GetPrivateState(); if (StateIsStoppedState (state, true)) { m_system_runtime_ap->PopulateQueueList (m_queue_list); m_queue_list_stop_id = GetLastNaturalStopID(); } } } } ThreadSP Process::CreateOSPluginThread (lldb::tid_t tid, lldb::addr_t context) { OperatingSystem *os = GetOperatingSystem (); if (os) return os->CreateThread(tid, context); return ThreadSP(); } uint32_t Process::GetNextThreadIndexID (uint64_t thread_id) { return AssignIndexIDToThread(thread_id); } bool Process::HasAssignedIndexIDToThread(uint64_t thread_id) { return (m_thread_id_to_index_id_map.find(thread_id) != m_thread_id_to_index_id_map.end()); } uint32_t Process::AssignIndexIDToThread(uint64_t thread_id) { uint32_t result = 0; std::map::iterator iterator = m_thread_id_to_index_id_map.find(thread_id); if (iterator == m_thread_id_to_index_id_map.end()) { result = ++m_thread_index_id; m_thread_id_to_index_id_map[thread_id] = result; } else { result = iterator->second; } return result; } StateType Process::GetState() { // If any other threads access this we will need a mutex for it return m_public_state.GetValue (); } bool Process::StateChangedIsExternallyHijacked() { if (IsHijackedForEvent(eBroadcastBitStateChanged)) { if (strcmp(m_hijacking_listeners.back()->GetName(), "lldb.Process.ResumeSynchronous.hijack")) return true; } return false; } void Process::SetPublicState (StateType new_state, bool restarted) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::SetPublicState (state = %s, restarted = %i)", StateAsCString(new_state), restarted); const StateType old_state = m_public_state.GetValue(); m_public_state.SetValue (new_state); // On the transition from Run to Stopped, we unlock the writer end of the // run lock. The lock gets locked in Resume, which is the public API // to tell the program to run. if (!StateChangedIsExternallyHijacked()) { if (new_state == eStateDetached) { if (log) log->Printf("Process::SetPublicState (%s) -- unlocking run lock for detach", StateAsCString(new_state)); m_public_run_lock.SetStopped(); } else { const bool old_state_is_stopped = StateIsStoppedState(old_state, false); const bool new_state_is_stopped = StateIsStoppedState(new_state, false); if ((old_state_is_stopped != new_state_is_stopped)) { if (new_state_is_stopped && !restarted) { if (log) log->Printf("Process::SetPublicState (%s) -- unlocking run lock", StateAsCString(new_state)); m_public_run_lock.SetStopped(); } } } } } Error Process::Resume () { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::Resume -- locking run lock"); if (!m_public_run_lock.TrySetRunning()) { Error error("Resume request failed - process still running."); if (log) log->Printf ("Process::Resume: -- TrySetRunning failed, not resuming."); return error; } return PrivateResume(); } Error Process::ResumeSynchronous (Stream *stream) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::ResumeSynchronous -- locking run lock"); if (!m_public_run_lock.TrySetRunning()) { Error error("Resume request failed - process still running."); if (log) log->Printf ("Process::Resume: -- TrySetRunning failed, not resuming."); return error; } ListenerSP listener_sp (new Listener("lldb.Process.ResumeSynchronous.hijack")); HijackProcessEvents(listener_sp.get()); Error error = PrivateResume(); if (error.Success()) { StateType state = WaitForProcessToStop (NULL, NULL, true, listener_sp.get(), stream); const bool must_be_alive = false; // eStateExited is ok, so this must be false if (!StateIsStoppedState(state, must_be_alive)) error.SetErrorStringWithFormat("process not in stopped state after synchronous resume: %s", StateAsCString(state)); } // Undo the hijacking of process events... RestoreProcessEvents(); return error; } StateType Process::GetPrivateState () { return m_private_state.GetValue(); } void Process::SetPrivateState (StateType new_state) { if (m_finalize_called) return; Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); bool state_changed = false; if (log) log->Printf("Process::SetPrivateState (%s)", StateAsCString(new_state)); Mutex::Locker thread_locker(m_thread_list.GetMutex()); Mutex::Locker locker(m_private_state.GetMutex()); const StateType old_state = m_private_state.GetValueNoLock (); state_changed = old_state != new_state; const bool old_state_is_stopped = StateIsStoppedState(old_state, false); const bool new_state_is_stopped = StateIsStoppedState(new_state, false); if (old_state_is_stopped != new_state_is_stopped) { if (new_state_is_stopped) m_private_run_lock.SetStopped(); else m_private_run_lock.SetRunning(); } if (state_changed) { m_private_state.SetValueNoLock (new_state); EventSP event_sp (new Event (eBroadcastBitStateChanged, new ProcessEventData (shared_from_this(), new_state))); if (StateIsStoppedState(new_state, false)) { // Note, this currently assumes that all threads in the list // stop when the process stops. In the future we will want to // support a debugging model where some threads continue to run // while others are stopped. When that happens we will either need // a way for the thread list to identify which threads are stopping // or create a special thread list containing only threads which // actually stopped. // // The process plugin is responsible for managing the actual // behavior of the threads and should have stopped any threads // that are going to stop before we get here. m_thread_list.DidStop(); m_mod_id.BumpStopID(); if (!m_mod_id.IsLastResumeForUserExpression()) m_mod_id.SetStopEventForLastNaturalStopID(event_sp); m_memory_cache.Clear(); if (log) log->Printf("Process::SetPrivateState (%s) stop_id = %u", StateAsCString(new_state), m_mod_id.GetStopID()); } // Use our target to get a shared pointer to ourselves... if (m_finalize_called && PrivateStateThreadIsValid() == false) BroadcastEvent (event_sp); else m_private_state_broadcaster.BroadcastEvent (event_sp); } else { if (log) log->Printf("Process::SetPrivateState (%s) state didn't change. Ignoring...", StateAsCString(new_state)); } } void Process::SetRunningUserExpression (bool on) { m_mod_id.SetRunningUserExpression (on); } addr_t Process::GetImageInfoAddress() { return LLDB_INVALID_ADDRESS; } const lldb::ABISP & Process::GetABI() { if (!m_abi_sp) m_abi_sp = ABI::FindPlugin(GetTarget().GetArchitecture()); return m_abi_sp; } LanguageRuntime * Process::GetLanguageRuntime(lldb::LanguageType language, bool retry_if_null) { if (m_finalizing) return nullptr; LanguageRuntimeCollection::iterator pos; pos = m_language_runtimes.find (language); if (pos == m_language_runtimes.end() || (retry_if_null && !(*pos).second)) { lldb::LanguageRuntimeSP runtime_sp(LanguageRuntime::FindPlugin(this, language)); m_language_runtimes[language] = runtime_sp; return runtime_sp.get(); } else return (*pos).second.get(); } CPPLanguageRuntime * Process::GetCPPLanguageRuntime (bool retry_if_null) { LanguageRuntime *runtime = GetLanguageRuntime(eLanguageTypeC_plus_plus, retry_if_null); if (runtime != NULL && runtime->GetLanguageType() == eLanguageTypeC_plus_plus) return static_cast (runtime); return NULL; } ObjCLanguageRuntime * Process::GetObjCLanguageRuntime (bool retry_if_null) { LanguageRuntime *runtime = GetLanguageRuntime(eLanguageTypeObjC, retry_if_null); if (runtime != NULL && runtime->GetLanguageType() == eLanguageTypeObjC) return static_cast (runtime); return NULL; } bool Process::IsPossibleDynamicValue (ValueObject& in_value) { if (m_finalizing) return false; if (in_value.IsDynamic()) return false; LanguageType known_type = in_value.GetObjectRuntimeLanguage(); if (known_type != eLanguageTypeUnknown && known_type != eLanguageTypeC) { LanguageRuntime *runtime = GetLanguageRuntime (known_type); return runtime ? runtime->CouldHaveDynamicValue(in_value) : false; } LanguageRuntime *cpp_runtime = GetLanguageRuntime (eLanguageTypeC_plus_plus); if (cpp_runtime && cpp_runtime->CouldHaveDynamicValue(in_value)) return true; LanguageRuntime *objc_runtime = GetLanguageRuntime (eLanguageTypeObjC); return objc_runtime ? objc_runtime->CouldHaveDynamicValue(in_value) : false; } void Process::SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers) { m_dynamic_checkers_ap.reset(dynamic_checkers); } BreakpointSiteList & Process::GetBreakpointSiteList() { return m_breakpoint_site_list; } const BreakpointSiteList & Process::GetBreakpointSiteList() const { return m_breakpoint_site_list; } void Process::DisableAllBreakpointSites () { m_breakpoint_site_list.ForEach([this](BreakpointSite *bp_site) -> void { // bp_site->SetEnabled(true); DisableBreakpointSite(bp_site); }); } Error Process::ClearBreakpointSiteByID (lldb::user_id_t break_id) { Error error (DisableBreakpointSiteByID (break_id)); if (error.Success()) m_breakpoint_site_list.Remove(break_id); return error; } Error Process::DisableBreakpointSiteByID (lldb::user_id_t break_id) { Error error; BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID (break_id); if (bp_site_sp) { if (bp_site_sp->IsEnabled()) error = DisableBreakpointSite (bp_site_sp.get()); } else { error.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64, break_id); } return error; } Error Process::EnableBreakpointSiteByID (lldb::user_id_t break_id) { Error error; BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID (break_id); if (bp_site_sp) { if (!bp_site_sp->IsEnabled()) error = EnableBreakpointSite (bp_site_sp.get()); } else { error.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64, break_id); } return error; } lldb::break_id_t Process::CreateBreakpointSite (const BreakpointLocationSP &owner, bool use_hardware) { addr_t load_addr = LLDB_INVALID_ADDRESS; bool show_error = true; switch (GetState()) { case eStateInvalid: case eStateUnloaded: case eStateConnected: case eStateAttaching: case eStateLaunching: case eStateDetached: case eStateExited: show_error = false; break; case eStateStopped: case eStateRunning: case eStateStepping: case eStateCrashed: case eStateSuspended: show_error = IsAlive(); break; } // Reset the IsIndirect flag here, in case the location changes from // pointing to a indirect symbol to a regular symbol. owner->SetIsIndirect (false); if (owner->ShouldResolveIndirectFunctions()) { Symbol *symbol = owner->GetAddress().CalculateSymbolContextSymbol(); if (symbol && symbol->IsIndirect()) { Error error; Address symbol_address = symbol->GetAddress(); load_addr = ResolveIndirectFunction (&symbol_address, error); if (!error.Success() && show_error) { GetTarget().GetDebugger().GetErrorFile()->Printf ("warning: failed to resolve indirect function at 0x%" PRIx64 " for breakpoint %i.%i: %s\n", symbol->GetLoadAddress(&GetTarget()), owner->GetBreakpoint().GetID(), owner->GetID(), error.AsCString() ? error.AsCString() : "unknown error"); return LLDB_INVALID_BREAK_ID; } Address resolved_address(load_addr); load_addr = resolved_address.GetOpcodeLoadAddress (&GetTarget()); owner->SetIsIndirect(true); } else load_addr = owner->GetAddress().GetOpcodeLoadAddress (&GetTarget()); } else load_addr = owner->GetAddress().GetOpcodeLoadAddress (&GetTarget()); if (load_addr != LLDB_INVALID_ADDRESS) { BreakpointSiteSP bp_site_sp; // Look up this breakpoint site. If it exists, then add this new owner, otherwise // create a new breakpoint site and add it. bp_site_sp = m_breakpoint_site_list.FindByAddress (load_addr); if (bp_site_sp) { bp_site_sp->AddOwner (owner); owner->SetBreakpointSite (bp_site_sp); return bp_site_sp->GetID(); } else { bp_site_sp.reset (new BreakpointSite (&m_breakpoint_site_list, owner, load_addr, use_hardware)); if (bp_site_sp) { Error error = EnableBreakpointSite (bp_site_sp.get()); if (error.Success()) { owner->SetBreakpointSite (bp_site_sp); return m_breakpoint_site_list.Add (bp_site_sp); } else { if (show_error) { // Report error for setting breakpoint... GetTarget().GetDebugger().GetErrorFile()->Printf ("warning: failed to set breakpoint site at 0x%" PRIx64 " for breakpoint %i.%i: %s\n", load_addr, owner->GetBreakpoint().GetID(), owner->GetID(), error.AsCString() ? error.AsCString() : "unknown error"); } } } } } // We failed to enable the breakpoint return LLDB_INVALID_BREAK_ID; } void Process::RemoveOwnerFromBreakpointSite (lldb::user_id_t owner_id, lldb::user_id_t owner_loc_id, BreakpointSiteSP &bp_site_sp) { uint32_t num_owners = bp_site_sp->RemoveOwner (owner_id, owner_loc_id); if (num_owners == 0) { // Don't try to disable the site if we don't have a live process anymore. if (IsAlive()) DisableBreakpointSite (bp_site_sp.get()); m_breakpoint_site_list.RemoveByAddress(bp_site_sp->GetLoadAddress()); } } size_t Process::RemoveBreakpointOpcodesFromBuffer (addr_t bp_addr, size_t size, uint8_t *buf) const { size_t bytes_removed = 0; BreakpointSiteList bp_sites_in_range; if (m_breakpoint_site_list.FindInRange (bp_addr, bp_addr + size, bp_sites_in_range)) { bp_sites_in_range.ForEach([bp_addr, size, buf, &bytes_removed](BreakpointSite *bp_site) -> void { if (bp_site->GetType() == BreakpointSite::eSoftware) { addr_t intersect_addr; size_t intersect_size; size_t opcode_offset; if (bp_site->IntersectsRange(bp_addr, size, &intersect_addr, &intersect_size, &opcode_offset)) { assert(bp_addr <= intersect_addr && intersect_addr < bp_addr + size); assert(bp_addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= bp_addr + size); assert(opcode_offset + intersect_size <= bp_site->GetByteSize()); size_t buf_offset = intersect_addr - bp_addr; ::memcpy(buf + buf_offset, bp_site->GetSavedOpcodeBytes() + opcode_offset, intersect_size); } } }); } return bytes_removed; } size_t Process::GetSoftwareBreakpointTrapOpcode (BreakpointSite* bp_site) { PlatformSP platform_sp (GetTarget().GetPlatform()); if (platform_sp) return platform_sp->GetSoftwareBreakpointTrapOpcode (GetTarget(), bp_site); return 0; } Error Process::EnableSoftwareBreakpoint (BreakpointSite *bp_site) { Error error; assert (bp_site != NULL); Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); const addr_t bp_addr = bp_site->GetLoadAddress(); if (log) log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64, bp_site->GetID(), (uint64_t)bp_addr); if (bp_site->IsEnabled()) { if (log) log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- already enabled", bp_site->GetID(), (uint64_t)bp_addr); return error; } if (bp_addr == LLDB_INVALID_ADDRESS) { error.SetErrorString("BreakpointSite contains an invalid load address."); return error; } // Ask the lldb::Process subclass to fill in the correct software breakpoint // trap for the breakpoint site const size_t bp_opcode_size = GetSoftwareBreakpointTrapOpcode(bp_site); if (bp_opcode_size == 0) { error.SetErrorStringWithFormat ("Process::GetSoftwareBreakpointTrapOpcode() returned zero, unable to get breakpoint trap for address 0x%" PRIx64, bp_addr); } else { const uint8_t * const bp_opcode_bytes = bp_site->GetTrapOpcodeBytes(); if (bp_opcode_bytes == NULL) { error.SetErrorString ("BreakpointSite doesn't contain a valid breakpoint trap opcode."); return error; } // Save the original opcode by reading it if (DoReadMemory(bp_addr, bp_site->GetSavedOpcodeBytes(), bp_opcode_size, error) == bp_opcode_size) { // Write a software breakpoint in place of the original opcode if (DoWriteMemory(bp_addr, bp_opcode_bytes, bp_opcode_size, error) == bp_opcode_size) { uint8_t verify_bp_opcode_bytes[64]; if (DoReadMemory(bp_addr, verify_bp_opcode_bytes, bp_opcode_size, error) == bp_opcode_size) { if (::memcmp(bp_opcode_bytes, verify_bp_opcode_bytes, bp_opcode_size) == 0) { bp_site->SetEnabled(true); bp_site->SetType (BreakpointSite::eSoftware); if (log) log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- SUCCESS", bp_site->GetID(), (uint64_t)bp_addr); } else error.SetErrorString("failed to verify the breakpoint trap in memory."); } else error.SetErrorString("Unable to read memory to verify breakpoint trap."); } else error.SetErrorString("Unable to write breakpoint trap to memory."); } else error.SetErrorString("Unable to read memory at breakpoint address."); } if (log && error.Fail()) log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- FAILED: %s", bp_site->GetID(), (uint64_t)bp_addr, error.AsCString()); return error; } Error Process::DisableSoftwareBreakpoint (BreakpointSite *bp_site) { Error error; assert (bp_site != NULL); Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); addr_t bp_addr = bp_site->GetLoadAddress(); lldb::user_id_t breakID = bp_site->GetID(); if (log) log->Printf ("Process::DisableSoftwareBreakpoint (breakID = %" PRIu64 ") addr = 0x%" PRIx64, breakID, (uint64_t)bp_addr); if (bp_site->IsHardware()) { error.SetErrorString("Breakpoint site is a hardware breakpoint."); } else if (bp_site->IsEnabled()) { const size_t break_op_size = bp_site->GetByteSize(); const uint8_t * const break_op = bp_site->GetTrapOpcodeBytes(); if (break_op_size > 0) { // Clear a software breakpoint instruction uint8_t curr_break_op[8]; assert (break_op_size <= sizeof(curr_break_op)); bool break_op_found = false; // Read the breakpoint opcode if (DoReadMemory (bp_addr, curr_break_op, break_op_size, error) == break_op_size) { bool verify = false; // Make sure the breakpoint opcode exists at this address if (::memcmp (curr_break_op, break_op, break_op_size) == 0) { break_op_found = true; // We found a valid breakpoint opcode at this address, now restore // the saved opcode. if (DoWriteMemory (bp_addr, bp_site->GetSavedOpcodeBytes(), break_op_size, error) == break_op_size) { verify = true; } else error.SetErrorString("Memory write failed when restoring original opcode."); } else { error.SetErrorString("Original breakpoint trap is no longer in memory."); // Set verify to true and so we can check if the original opcode has already been restored verify = true; } if (verify) { uint8_t verify_opcode[8]; assert (break_op_size < sizeof(verify_opcode)); // Verify that our original opcode made it back to the inferior if (DoReadMemory (bp_addr, verify_opcode, break_op_size, error) == break_op_size) { // compare the memory we just read with the original opcode if (::memcmp (bp_site->GetSavedOpcodeBytes(), verify_opcode, break_op_size) == 0) { // SUCCESS bp_site->SetEnabled(false); if (log) log->Printf ("Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- SUCCESS", bp_site->GetID(), (uint64_t)bp_addr); return error; } else { if (break_op_found) error.SetErrorString("Failed to restore original opcode."); } } else error.SetErrorString("Failed to read memory to verify that breakpoint trap was restored."); } } else error.SetErrorString("Unable to read memory that should contain the breakpoint trap."); } } else { if (log) log->Printf ("Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- already disabled", bp_site->GetID(), (uint64_t)bp_addr); return error; } if (log) log->Printf ("Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- FAILED: %s", bp_site->GetID(), (uint64_t)bp_addr, error.AsCString()); return error; } // Uncomment to verify memory caching works after making changes to caching code //#define VERIFY_MEMORY_READS size_t Process::ReadMemory (addr_t addr, void *buf, size_t size, Error &error) { error.Clear(); if (!GetDisableMemoryCache()) { #if defined (VERIFY_MEMORY_READS) // Memory caching is enabled, with debug verification if (buf && size) { // Uncomment the line below to make sure memory caching is working. // I ran this through the test suite and got no assertions, so I am // pretty confident this is working well. If any changes are made to // memory caching, uncomment the line below and test your changes! // Verify all memory reads by using the cache first, then redundantly // reading the same memory from the inferior and comparing to make sure // everything is exactly the same. std::string verify_buf (size, '\0'); assert (verify_buf.size() == size); const size_t cache_bytes_read = m_memory_cache.Read (this, addr, buf, size, error); Error verify_error; const size_t verify_bytes_read = ReadMemoryFromInferior (addr, const_cast(verify_buf.data()), verify_buf.size(), verify_error); assert (cache_bytes_read == verify_bytes_read); assert (memcmp(buf, verify_buf.data(), verify_buf.size()) == 0); assert (verify_error.Success() == error.Success()); return cache_bytes_read; } return 0; #else // !defined(VERIFY_MEMORY_READS) // Memory caching is enabled, without debug verification return m_memory_cache.Read (addr, buf, size, error); #endif // defined (VERIFY_MEMORY_READS) } else { // Memory caching is disabled return ReadMemoryFromInferior (addr, buf, size, error); } } size_t Process::ReadCStringFromMemory (addr_t addr, std::string &out_str, Error &error) { char buf[256]; out_str.clear(); addr_t curr_addr = addr; while (1) { size_t length = ReadCStringFromMemory (curr_addr, buf, sizeof(buf), error); if (length == 0) break; out_str.append(buf, length); // If we got "length - 1" bytes, we didn't get the whole C string, we // need to read some more characters if (length == sizeof(buf) - 1) curr_addr += length; else break; } return out_str.size(); } size_t Process::ReadStringFromMemory (addr_t addr, char *dst, size_t max_bytes, Error &error, size_t type_width) { size_t total_bytes_read = 0; if (dst && max_bytes && type_width && max_bytes >= type_width) { // Ensure a null terminator independent of the number of bytes that is read. memset (dst, 0, max_bytes); size_t bytes_left = max_bytes - type_width; const char terminator[4] = {'\0', '\0', '\0', '\0'}; assert(sizeof(terminator) >= type_width && "Attempting to validate a string with more than 4 bytes per character!"); addr_t curr_addr = addr; const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize(); char *curr_dst = dst; error.Clear(); while (bytes_left > 0 && error.Success()) { addr_t cache_line_bytes_left = cache_line_size - (curr_addr % cache_line_size); addr_t bytes_to_read = std::min(bytes_left, cache_line_bytes_left); size_t bytes_read = ReadMemory (curr_addr, curr_dst, bytes_to_read, error); if (bytes_read == 0) break; // Search for a null terminator of correct size and alignment in bytes_read size_t aligned_start = total_bytes_read - total_bytes_read % type_width; for (size_t i = aligned_start; i + type_width <= total_bytes_read + bytes_read; i += type_width) if (::strncmp(&dst[i], terminator, type_width) == 0) { error.Clear(); return i; } total_bytes_read += bytes_read; curr_dst += bytes_read; curr_addr += bytes_read; bytes_left -= bytes_read; } } else { if (max_bytes) error.SetErrorString("invalid arguments"); } return total_bytes_read; } // Deprecated in favor of ReadStringFromMemory which has wchar support and correct code to find // null terminators. size_t Process::ReadCStringFromMemory (addr_t addr, char *dst, size_t dst_max_len, Error &result_error) { size_t total_cstr_len = 0; if (dst && dst_max_len) { result_error.Clear(); // NULL out everything just to be safe memset (dst, 0, dst_max_len); Error error; addr_t curr_addr = addr; const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize(); size_t bytes_left = dst_max_len - 1; char *curr_dst = dst; while (bytes_left > 0) { addr_t cache_line_bytes_left = cache_line_size - (curr_addr % cache_line_size); addr_t bytes_to_read = std::min(bytes_left, cache_line_bytes_left); size_t bytes_read = ReadMemory (curr_addr, curr_dst, bytes_to_read, error); if (bytes_read == 0) { result_error = error; dst[total_cstr_len] = '\0'; break; } const size_t len = strlen(curr_dst); total_cstr_len += len; if (len < bytes_to_read) break; curr_dst += bytes_read; curr_addr += bytes_read; bytes_left -= bytes_read; } } else { if (dst == NULL) result_error.SetErrorString("invalid arguments"); else result_error.Clear(); } return total_cstr_len; } size_t Process::ReadMemoryFromInferior (addr_t addr, void *buf, size_t size, Error &error) { if (buf == NULL || size == 0) return 0; size_t bytes_read = 0; uint8_t *bytes = (uint8_t *)buf; while (bytes_read < size) { const size_t curr_size = size - bytes_read; const size_t curr_bytes_read = DoReadMemory (addr + bytes_read, bytes + bytes_read, curr_size, error); bytes_read += curr_bytes_read; if (curr_bytes_read == curr_size || curr_bytes_read == 0) break; } // Replace any software breakpoint opcodes that fall into this range back // into "buf" before we return if (bytes_read > 0) RemoveBreakpointOpcodesFromBuffer (addr, bytes_read, (uint8_t *)buf); return bytes_read; } uint64_t Process::ReadUnsignedIntegerFromMemory (lldb::addr_t vm_addr, size_t integer_byte_size, uint64_t fail_value, Error &error) { Scalar scalar; if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, false, scalar, error)) return scalar.ULongLong(fail_value); return fail_value; } addr_t Process::ReadPointerFromMemory (lldb::addr_t vm_addr, Error &error) { Scalar scalar; if (ReadScalarIntegerFromMemory(vm_addr, GetAddressByteSize(), false, scalar, error)) return scalar.ULongLong(LLDB_INVALID_ADDRESS); return LLDB_INVALID_ADDRESS; } bool Process::WritePointerToMemory (lldb::addr_t vm_addr, lldb::addr_t ptr_value, Error &error) { Scalar scalar; const uint32_t addr_byte_size = GetAddressByteSize(); if (addr_byte_size <= 4) scalar = (uint32_t)ptr_value; else scalar = ptr_value; return WriteScalarToMemory(vm_addr, scalar, addr_byte_size, error) == addr_byte_size; } size_t Process::WriteMemoryPrivate (addr_t addr, const void *buf, size_t size, Error &error) { size_t bytes_written = 0; const uint8_t *bytes = (const uint8_t *)buf; while (bytes_written < size) { const size_t curr_size = size - bytes_written; const size_t curr_bytes_written = DoWriteMemory (addr + bytes_written, bytes + bytes_written, curr_size, error); bytes_written += curr_bytes_written; if (curr_bytes_written == curr_size || curr_bytes_written == 0) break; } return bytes_written; } size_t Process::WriteMemory (addr_t addr, const void *buf, size_t size, Error &error) { #if defined (ENABLE_MEMORY_CACHING) m_memory_cache.Flush (addr, size); #endif if (buf == NULL || size == 0) return 0; m_mod_id.BumpMemoryID(); // We need to write any data that would go where any current software traps // (enabled software breakpoints) any software traps (breakpoints) that we // may have placed in our tasks memory. BreakpointSiteList bp_sites_in_range; if (m_breakpoint_site_list.FindInRange (addr, addr + size, bp_sites_in_range)) { // No breakpoint sites overlap if (bp_sites_in_range.IsEmpty()) return WriteMemoryPrivate (addr, buf, size, error); else { const uint8_t *ubuf = (const uint8_t *)buf; uint64_t bytes_written = 0; bp_sites_in_range.ForEach([this, addr, size, &bytes_written, &ubuf, &error](BreakpointSite *bp) -> void { if (error.Success()) { addr_t intersect_addr; size_t intersect_size; size_t opcode_offset; const bool intersects = bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset); UNUSED_IF_ASSERT_DISABLED(intersects); assert(intersects); assert(addr <= intersect_addr && intersect_addr < addr + size); assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); assert(opcode_offset + intersect_size <= bp->GetByteSize()); // Check for bytes before this breakpoint const addr_t curr_addr = addr + bytes_written; if (intersect_addr > curr_addr) { // There are some bytes before this breakpoint that we need to // just write to memory size_t curr_size = intersect_addr - curr_addr; size_t curr_bytes_written = WriteMemoryPrivate (curr_addr, ubuf + bytes_written, curr_size, error); bytes_written += curr_bytes_written; if (curr_bytes_written != curr_size) { // We weren't able to write all of the requested bytes, we // are done looping and will return the number of bytes that // we have written so far. if (error.Success()) error.SetErrorToGenericError(); } } // Now write any bytes that would cover up any software breakpoints // directly into the breakpoint opcode buffer ::memcpy(bp->GetSavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size); bytes_written += intersect_size; } }); if (bytes_written < size) WriteMemoryPrivate (addr + bytes_written, ubuf + bytes_written, size - bytes_written, error); } } else { return WriteMemoryPrivate (addr, buf, size, error); } // Write any remaining bytes after the last breakpoint if we have any left return 0; //bytes_written; } size_t Process::WriteScalarToMemory (addr_t addr, const Scalar &scalar, size_t byte_size, Error &error) { if (byte_size == UINT32_MAX) byte_size = scalar.GetByteSize(); if (byte_size > 0) { uint8_t buf[32]; const size_t mem_size = scalar.GetAsMemoryData (buf, byte_size, GetByteOrder(), error); if (mem_size > 0) return WriteMemory(addr, buf, mem_size, error); else error.SetErrorString ("failed to get scalar as memory data"); } else { error.SetErrorString ("invalid scalar value"); } return 0; } size_t Process::ReadScalarIntegerFromMemory (addr_t addr, uint32_t byte_size, bool is_signed, Scalar &scalar, Error &error) { uint64_t uval = 0; if (byte_size == 0) { error.SetErrorString ("byte size is zero"); } else if (byte_size & (byte_size - 1)) { error.SetErrorStringWithFormat ("byte size %u is not a power of 2", byte_size); } else if (byte_size <= sizeof(uval)) { const size_t bytes_read = ReadMemory (addr, &uval, byte_size, error); if (bytes_read == byte_size) { DataExtractor data (&uval, sizeof(uval), GetByteOrder(), GetAddressByteSize()); lldb::offset_t offset = 0; if (byte_size <= 4) scalar = data.GetMaxU32 (&offset, byte_size); else scalar = data.GetMaxU64 (&offset, byte_size); if (is_signed) scalar.SignExtend(byte_size * 8); return bytes_read; } } else { error.SetErrorStringWithFormat ("byte size of %u is too large for integer scalar type", byte_size); } return 0; } #define USE_ALLOCATE_MEMORY_CACHE 1 addr_t Process::AllocateMemory(size_t size, uint32_t permissions, Error &error) { if (GetPrivateState() != eStateStopped) return LLDB_INVALID_ADDRESS; #if defined (USE_ALLOCATE_MEMORY_CACHE) return m_allocated_memory_cache.AllocateMemory(size, permissions, error); #else addr_t allocated_addr = DoAllocateMemory (size, permissions, error); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::AllocateMemory(size=%" PRIu64 ", permissions=%s) => 0x%16.16" PRIx64 " (m_stop_id = %u m_memory_id = %u)", (uint64_t)size, GetPermissionsAsCString (permissions), (uint64_t)allocated_addr, m_mod_id.GetStopID(), m_mod_id.GetMemoryID()); return allocated_addr; #endif } addr_t Process::CallocateMemory(size_t size, uint32_t permissions, Error &error) { addr_t return_addr = AllocateMemory(size, permissions, error); if (error.Success()) { std::string buffer(size, 0); WriteMemory(return_addr, buffer.c_str(), size, error); } return return_addr; } bool Process::CanJIT () { if (m_can_jit == eCanJITDontKnow) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); Error err; uint64_t allocated_memory = AllocateMemory(8, ePermissionsReadable | ePermissionsWritable | ePermissionsExecutable, err); if (err.Success()) { m_can_jit = eCanJITYes; if (log) log->Printf ("Process::%s pid %" PRIu64 " allocation test passed, CanJIT () is true", __FUNCTION__, GetID ()); } else { m_can_jit = eCanJITNo; if (log) log->Printf ("Process::%s pid %" PRIu64 " allocation test failed, CanJIT () is false: %s", __FUNCTION__, GetID (), err.AsCString ()); } DeallocateMemory (allocated_memory); } return m_can_jit == eCanJITYes; } void Process::SetCanJIT (bool can_jit) { m_can_jit = (can_jit ? eCanJITYes : eCanJITNo); } void Process::SetCanRunCode (bool can_run_code) { SetCanJIT(can_run_code); m_can_interpret_function_calls = can_run_code; } Error Process::DeallocateMemory (addr_t ptr) { Error error; #if defined (USE_ALLOCATE_MEMORY_CACHE) if (!m_allocated_memory_cache.DeallocateMemory(ptr)) { error.SetErrorStringWithFormat ("deallocation of memory at 0x%" PRIx64 " failed.", (uint64_t)ptr); } #else error = DoDeallocateMemory (ptr); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::DeallocateMemory(addr=0x%16.16" PRIx64 ") => err = %s (m_stop_id = %u, m_memory_id = %u)", ptr, error.AsCString("SUCCESS"), m_mod_id.GetStopID(), m_mod_id.GetMemoryID()); #endif return error; } ModuleSP Process::ReadModuleFromMemory (const FileSpec& file_spec, lldb::addr_t header_addr, size_t size_to_read) { Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); if (log) { log->Printf ("Process::ReadModuleFromMemory reading %s binary from memory", file_spec.GetPath().c_str()); } ModuleSP module_sp (new Module (file_spec, ArchSpec())); if (module_sp) { Error error; ObjectFile *objfile = module_sp->GetMemoryObjectFile (shared_from_this(), header_addr, error, size_to_read); if (objfile) return module_sp; } return ModuleSP(); } bool Process::GetLoadAddressPermissions (lldb::addr_t load_addr, uint32_t &permissions) { MemoryRegionInfo range_info; permissions = 0; Error error (GetMemoryRegionInfo (load_addr, range_info)); if (!error.Success()) return false; if (range_info.GetReadable() == MemoryRegionInfo::eDontKnow || range_info.GetWritable() == MemoryRegionInfo::eDontKnow || range_info.GetExecutable() == MemoryRegionInfo::eDontKnow) { return false; } if (range_info.GetReadable() == MemoryRegionInfo::eYes) permissions |= lldb::ePermissionsReadable; if (range_info.GetWritable() == MemoryRegionInfo::eYes) permissions |= lldb::ePermissionsWritable; if (range_info.GetExecutable() == MemoryRegionInfo::eYes) permissions |= lldb::ePermissionsExecutable; return true; } Error Process::EnableWatchpoint (Watchpoint *watchpoint, bool notify) { Error error; error.SetErrorString("watchpoints are not supported"); return error; } Error Process::DisableWatchpoint (Watchpoint *watchpoint, bool notify) { Error error; error.SetErrorString("watchpoints are not supported"); return error; } StateType Process::WaitForProcessStopPrivate (const TimeValue *timeout, EventSP &event_sp) { StateType state; // Now wait for the process to launch and return control to us, and then // call DidLaunch: while (1) { event_sp.reset(); state = WaitForStateChangedEventsPrivate (timeout, event_sp); if (StateIsStoppedState(state, false)) break; // If state is invalid, then we timed out if (state == eStateInvalid) break; if (event_sp) HandlePrivateEvent (event_sp); } return state; } void Process::LoadOperatingSystemPlugin(bool flush) { if (flush) m_thread_list.Clear(); m_os_ap.reset (OperatingSystem::FindPlugin (this, NULL)); if (flush) Flush(); } Error Process::Launch (ProcessLaunchInfo &launch_info) { Error error; m_abi_sp.reset(); m_dyld_ap.reset(); m_jit_loaders_ap.reset(); m_system_runtime_ap.reset(); m_os_ap.reset(); m_process_input_reader.reset(); m_stop_info_override_callback = NULL; Module *exe_module = GetTarget().GetExecutableModulePointer(); if (exe_module) { char local_exec_file_path[PATH_MAX]; char platform_exec_file_path[PATH_MAX]; exe_module->GetFileSpec().GetPath(local_exec_file_path, sizeof(local_exec_file_path)); exe_module->GetPlatformFileSpec().GetPath(platform_exec_file_path, sizeof(platform_exec_file_path)); if (exe_module->GetFileSpec().Exists()) { // Install anything that might need to be installed prior to launching. // For host systems, this will do nothing, but if we are connected to a // remote platform it will install any needed binaries error = GetTarget().Install(&launch_info); if (error.Fail()) return error; if (PrivateStateThreadIsValid ()) PausePrivateStateThread (); error = WillLaunch (exe_module); if (error.Success()) { const bool restarted = false; SetPublicState (eStateLaunching, restarted); m_should_detach = false; if (m_public_run_lock.TrySetRunning()) { // Now launch using these arguments. error = DoLaunch (exe_module, launch_info); } else { // This shouldn't happen error.SetErrorString("failed to acquire process run lock"); } if (error.Fail()) { if (GetID() != LLDB_INVALID_PROCESS_ID) { SetID (LLDB_INVALID_PROCESS_ID); const char *error_string = error.AsCString(); if (error_string == NULL) error_string = "launch failed"; SetExitStatus (-1, error_string); } } else { EventSP event_sp; TimeValue timeout_time; timeout_time = TimeValue::Now(); timeout_time.OffsetWithSeconds(10); StateType state = WaitForProcessStopPrivate(&timeout_time, event_sp); if (state == eStateInvalid || event_sp.get() == NULL) { // We were able to launch the process, but we failed to // catch the initial stop. error.SetErrorString ("failed to catch stop after launch"); SetExitStatus (0, "failed to catch stop after launch"); Destroy(false); } else if (state == eStateStopped || state == eStateCrashed) { DidLaunch (); DynamicLoader *dyld = GetDynamicLoader (); if (dyld) dyld->DidLaunch(); GetJITLoaders().DidLaunch(); SystemRuntime *system_runtime = GetSystemRuntime (); if (system_runtime) system_runtime->DidLaunch(); LoadOperatingSystemPlugin(false); // Note, the stop event was consumed above, but not handled. This was done // to give DidLaunch a chance to run. The target is either stopped or crashed. // Directly set the state. This is done to prevent a stop message with a bunch // of spurious output on thread status, as well as not pop a ProcessIOHandler. SetPublicState(state, false); if (PrivateStateThreadIsValid ()) ResumePrivateStateThread (); else StartPrivateStateThread (); m_stop_info_override_callback = GetTarget().GetArchitecture().GetStopInfoOverrideCallback(); // Target was stopped at entry as was intended. Need to notify the listeners // about it. if (state == eStateStopped && launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) HandlePrivateEvent(event_sp); } else if (state == eStateExited) { // We exited while trying to launch somehow. Don't call DidLaunch as that's // not likely to work, and return an invalid pid. HandlePrivateEvent (event_sp); } } } } else { error.SetErrorStringWithFormat("file doesn't exist: '%s'", local_exec_file_path); } } return error; } Error Process::LoadCore () { Error error = DoLoadCore(); if (error.Success()) { Listener listener ("lldb.process.load_core_listener"); HijackProcessEvents(&listener); if (PrivateStateThreadIsValid ()) ResumePrivateStateThread (); else StartPrivateStateThread (); DynamicLoader *dyld = GetDynamicLoader (); if (dyld) dyld->DidAttach(); GetJITLoaders().DidAttach(); SystemRuntime *system_runtime = GetSystemRuntime (); if (system_runtime) system_runtime->DidAttach(); m_os_ap.reset (OperatingSystem::FindPlugin (this, NULL)); // We successfully loaded a core file, now pretend we stopped so we can // show all of the threads in the core file and explore the crashed // state. SetPrivateState (eStateStopped); // Wait indefinitely for a stopped event since we just posted one above... lldb::EventSP event_sp; listener.WaitForEvent (NULL, event_sp); StateType state = ProcessEventData::GetStateFromEvent(event_sp.get()); if (!StateIsStoppedState (state, false)) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::Halt() failed to stop, state is: %s", StateAsCString(state)); error.SetErrorString ("Did not get stopped event after loading the core file."); } RestoreProcessEvents (); } return error; } DynamicLoader * Process::GetDynamicLoader () { if (m_dyld_ap.get() == NULL) m_dyld_ap.reset (DynamicLoader::FindPlugin(this, NULL)); return m_dyld_ap.get(); } const lldb::DataBufferSP Process::GetAuxvData() { return DataBufferSP (); } JITLoaderList & Process::GetJITLoaders () { if (!m_jit_loaders_ap) { m_jit_loaders_ap.reset(new JITLoaderList()); JITLoader::LoadPlugins(this, *m_jit_loaders_ap); } return *m_jit_loaders_ap; } SystemRuntime * Process::GetSystemRuntime () { if (m_system_runtime_ap.get() == NULL) m_system_runtime_ap.reset (SystemRuntime::FindPlugin(this)); return m_system_runtime_ap.get(); } Process::AttachCompletionHandler::AttachCompletionHandler (Process *process, uint32_t exec_count) : NextEventAction (process), m_exec_count (exec_count) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::AttachCompletionHandler::%s process=%p, exec_count=%" PRIu32, __FUNCTION__, static_cast(process), exec_count); } Process::NextEventAction::EventActionResult Process::AttachCompletionHandler::PerformAction (lldb::EventSP &event_sp) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); StateType state = ProcessEventData::GetStateFromEvent (event_sp.get()); if (log) log->Printf ("Process::AttachCompletionHandler::%s called with state %s (%d)", __FUNCTION__, StateAsCString(state), static_cast (state)); switch (state) { case eStateAttaching: return eEventActionSuccess; case eStateRunning: case eStateConnected: return eEventActionRetry; case eStateStopped: case eStateCrashed: { // During attach, prior to sending the eStateStopped event, // lldb_private::Process subclasses must set the new process ID. assert (m_process->GetID() != LLDB_INVALID_PROCESS_ID); // We don't want these events to be reported, so go set the ShouldReportStop here: m_process->GetThreadList().SetShouldReportStop (eVoteNo); if (m_exec_count > 0) { --m_exec_count; if (log) log->Printf ("Process::AttachCompletionHandler::%s state %s: reduced remaining exec count to %" PRIu32 ", requesting resume", __FUNCTION__, StateAsCString(state), m_exec_count); RequestResume(); return eEventActionRetry; } else { if (log) log->Printf ("Process::AttachCompletionHandler::%s state %s: no more execs expected to start, continuing with attach", __FUNCTION__, StateAsCString(state)); m_process->CompleteAttach (); return eEventActionSuccess; } } break; default: case eStateExited: case eStateInvalid: break; } m_exit_string.assign ("No valid Process"); return eEventActionExit; } Process::NextEventAction::EventActionResult Process::AttachCompletionHandler::HandleBeingInterrupted() { return eEventActionSuccess; } const char * Process::AttachCompletionHandler::GetExitString () { return m_exit_string.c_str(); } Listener & ProcessAttachInfo::GetListenerForProcess (Debugger &debugger) { if (m_listener_sp) return *m_listener_sp; else return debugger.GetListener(); } Error Process::Attach (ProcessAttachInfo &attach_info) { m_abi_sp.reset(); m_process_input_reader.reset(); m_dyld_ap.reset(); m_jit_loaders_ap.reset(); m_system_runtime_ap.reset(); m_os_ap.reset(); m_stop_info_override_callback = NULL; lldb::pid_t attach_pid = attach_info.GetProcessID(); Error error; if (attach_pid == LLDB_INVALID_PROCESS_ID) { char process_name[PATH_MAX]; if (attach_info.GetExecutableFile().GetPath (process_name, sizeof(process_name))) { const bool wait_for_launch = attach_info.GetWaitForLaunch(); if (wait_for_launch) { error = WillAttachToProcessWithName(process_name, wait_for_launch); if (error.Success()) { if (m_public_run_lock.TrySetRunning()) { m_should_detach = true; const bool restarted = false; SetPublicState (eStateAttaching, restarted); // Now attach using these arguments. error = DoAttachToProcessWithName (process_name, attach_info); } else { // This shouldn't happen error.SetErrorString("failed to acquire process run lock"); } if (error.Fail()) { if (GetID() != LLDB_INVALID_PROCESS_ID) { SetID (LLDB_INVALID_PROCESS_ID); if (error.AsCString() == NULL) error.SetErrorString("attach failed"); SetExitStatus(-1, error.AsCString()); } } else { SetNextEventAction(new Process::AttachCompletionHandler(this, attach_info.GetResumeCount())); StartPrivateStateThread(); } return error; } } else { ProcessInstanceInfoList process_infos; PlatformSP platform_sp (GetTarget().GetPlatform ()); if (platform_sp) { ProcessInstanceInfoMatch match_info; match_info.GetProcessInfo() = attach_info; match_info.SetNameMatchType (eNameMatchEquals); platform_sp->FindProcesses (match_info, process_infos); const uint32_t num_matches = process_infos.GetSize(); if (num_matches == 1) { attach_pid = process_infos.GetProcessIDAtIndex(0); // Fall through and attach using the above process ID } else { match_info.GetProcessInfo().GetExecutableFile().GetPath (process_name, sizeof(process_name)); if (num_matches > 1) { StreamString s; ProcessInstanceInfo::DumpTableHeader (s, platform_sp.get(), true, false); for (size_t i = 0; i < num_matches; i++) { process_infos.GetProcessInfoAtIndex(i).DumpAsTableRow(s, platform_sp.get(), true, false); } error.SetErrorStringWithFormat ("more than one process named %s:\n%s", process_name, s.GetData()); } else error.SetErrorStringWithFormat ("could not find a process named %s", process_name); } } else { error.SetErrorString ("invalid platform, can't find processes by name"); return error; } } } else { error.SetErrorString ("invalid process name"); } } if (attach_pid != LLDB_INVALID_PROCESS_ID) { error = WillAttachToProcessWithID(attach_pid); if (error.Success()) { if (m_public_run_lock.TrySetRunning()) { // Now attach using these arguments. m_should_detach = true; const bool restarted = false; SetPublicState (eStateAttaching, restarted); error = DoAttachToProcessWithID (attach_pid, attach_info); } else { // This shouldn't happen error.SetErrorString("failed to acquire process run lock"); } if (error.Success()) { SetNextEventAction(new Process::AttachCompletionHandler(this, attach_info.GetResumeCount())); StartPrivateStateThread(); } else { if (GetID() != LLDB_INVALID_PROCESS_ID) SetID (LLDB_INVALID_PROCESS_ID); const char *error_string = error.AsCString(); if (error_string == NULL) error_string = "attach failed"; SetExitStatus(-1, error_string); } } } return error; } void Process::CompleteAttach () { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::%s()", __FUNCTION__); // Let the process subclass figure out at much as it can about the process // before we go looking for a dynamic loader plug-in. ArchSpec process_arch; DidAttach(process_arch); if (process_arch.IsValid()) { GetTarget().SetArchitecture(process_arch); if (log) { const char *triple_str = process_arch.GetTriple().getTriple().c_str (); log->Printf ("Process::%s replacing process architecture with DidAttach() architecture: %s", __FUNCTION__, triple_str ? triple_str : ""); } } // We just attached. If we have a platform, ask it for the process architecture, and if it isn't // the same as the one we've already set, switch architectures. PlatformSP platform_sp (GetTarget().GetPlatform ()); assert (platform_sp.get()); if (platform_sp) { const ArchSpec &target_arch = GetTarget().GetArchitecture(); if (target_arch.IsValid() && !platform_sp->IsCompatibleArchitecture (target_arch, false, NULL)) { ArchSpec platform_arch; platform_sp = platform_sp->GetPlatformForArchitecture (target_arch, &platform_arch); if (platform_sp) { GetTarget().SetPlatform (platform_sp); GetTarget().SetArchitecture(platform_arch); if (log) log->Printf ("Process::%s switching platform to %s and architecture to %s based on info from attach", __FUNCTION__, platform_sp->GetName().AsCString (""), platform_arch.GetTriple().getTriple().c_str ()); } } else if (!process_arch.IsValid()) { ProcessInstanceInfo process_info; platform_sp->GetProcessInfo (GetID(), process_info); const ArchSpec &process_arch = process_info.GetArchitecture(); if (process_arch.IsValid() && !GetTarget().GetArchitecture().IsExactMatch(process_arch)) { GetTarget().SetArchitecture (process_arch); if (log) log->Printf ("Process::%s switching architecture to %s based on info the platform retrieved for pid %" PRIu64, __FUNCTION__, process_arch.GetTriple().getTriple().c_str (), GetID ()); } } } // We have completed the attach, now it is time to find the dynamic loader // plug-in DynamicLoader *dyld = GetDynamicLoader (); if (dyld) { dyld->DidAttach(); if (log) { ModuleSP exe_module_sp = GetTarget().GetExecutableModule (); log->Printf ("Process::%s after DynamicLoader::DidAttach(), target executable is %s (using %s plugin)", __FUNCTION__, exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str () : "", dyld->GetPluginName().AsCString ("")); } } GetJITLoaders().DidAttach(); SystemRuntime *system_runtime = GetSystemRuntime (); if (system_runtime) { system_runtime->DidAttach(); if (log) { ModuleSP exe_module_sp = GetTarget().GetExecutableModule (); log->Printf ("Process::%s after SystemRuntime::DidAttach(), target executable is %s (using %s plugin)", __FUNCTION__, exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str () : "", system_runtime->GetPluginName().AsCString("")); } } m_os_ap.reset (OperatingSystem::FindPlugin (this, NULL)); // Figure out which one is the executable, and set that in our target: const ModuleList &target_modules = GetTarget().GetImages(); Mutex::Locker modules_locker(target_modules.GetMutex()); size_t num_modules = target_modules.GetSize(); ModuleSP new_executable_module_sp; for (size_t i = 0; i < num_modules; i++) { ModuleSP module_sp (target_modules.GetModuleAtIndexUnlocked (i)); if (module_sp && module_sp->IsExecutable()) { if (GetTarget().GetExecutableModulePointer() != module_sp.get()) new_executable_module_sp = module_sp; break; } } if (new_executable_module_sp) { GetTarget().SetExecutableModule (new_executable_module_sp, false); if (log) { ModuleSP exe_module_sp = GetTarget().GetExecutableModule (); log->Printf ("Process::%s after looping through modules, target executable is %s", __FUNCTION__, exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str () : ""); } } m_stop_info_override_callback = process_arch.GetStopInfoOverrideCallback(); } Error Process::ConnectRemote (Stream *strm, const char *remote_url) { m_abi_sp.reset(); m_process_input_reader.reset(); // Find the process and its architecture. Make sure it matches the architecture // of the current Target, and if not adjust it. Error error (DoConnectRemote (strm, remote_url)); if (error.Success()) { if (GetID() != LLDB_INVALID_PROCESS_ID) { EventSP event_sp; StateType state = WaitForProcessStopPrivate(NULL, event_sp); if (state == eStateStopped || state == eStateCrashed) { // If we attached and actually have a process on the other end, then // this ended up being the equivalent of an attach. CompleteAttach (); // This delays passing the stopped event to listeners till // CompleteAttach gets a chance to complete... HandlePrivateEvent (event_sp); } } if (PrivateStateThreadIsValid ()) ResumePrivateStateThread (); else StartPrivateStateThread (); } return error; } Error Process::PrivateResume () { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_STEP)); if (log) log->Printf("Process::PrivateResume() m_stop_id = %u, public state: %s private state: %s", m_mod_id.GetStopID(), StateAsCString(m_public_state.GetValue()), StateAsCString(m_private_state.GetValue())); Error error (WillResume()); // Tell the process it is about to resume before the thread list if (error.Success()) { // Now let the thread list know we are about to resume so it // can let all of our threads know that they are about to be // resumed. Threads will each be called with // Thread::WillResume(StateType) where StateType contains the state // that they are supposed to have when the process is resumed // (suspended/running/stepping). Threads should also check // their resume signal in lldb::Thread::GetResumeSignal() // to see if they are supposed to start back up with a signal. if (m_thread_list.WillResume()) { // Last thing, do the PreResumeActions. if (!RunPreResumeActions()) { error.SetErrorStringWithFormat ("Process::PrivateResume PreResumeActions failed, not resuming."); } else { m_mod_id.BumpResumeID(); error = DoResume(); if (error.Success()) { DidResume(); m_thread_list.DidResume(); if (log) log->Printf ("Process thinks the process has resumed."); } } } else { // Somebody wanted to run without running (e.g. we were faking a step from one frame of a set of inlined // frames that share the same PC to another.) So generate a continue & a stopped event, // and let the world handle them. if (log) log->Printf ("Process::PrivateResume() asked to simulate a start & stop."); SetPrivateState(eStateRunning); SetPrivateState(eStateStopped); } } else if (log) log->Printf ("Process::PrivateResume() got an error \"%s\".", error.AsCString("")); return error; } Error Process::Halt (bool clear_thread_plans, bool use_run_lock) { if (! StateIsRunningState(m_public_state.GetValue())) return Error("Process is not running."); // Don't clear the m_clear_thread_plans_on_stop, only set it to true if // in case it was already set and some thread plan logic calls halt on its // own. m_clear_thread_plans_on_stop |= clear_thread_plans; Listener halt_listener ("lldb.process.halt_listener"); HijackProcessEvents(&halt_listener); EventSP event_sp; SendAsyncInterrupt(); if (m_public_state.GetValue() == eStateAttaching) { // Don't hijack and eat the eStateExited as the code that was doing // the attach will be waiting for this event... RestoreProcessEvents(); SetExitStatus(SIGKILL, "Cancelled async attach."); Destroy (false); return Error(); } // Wait for 10 second for the process to stop. TimeValue timeout_time; timeout_time = TimeValue::Now(); timeout_time.OffsetWithSeconds(10); StateType state = WaitForProcessToStop(&timeout_time, &event_sp, true, &halt_listener, nullptr, use_run_lock); RestoreProcessEvents(); if (state == eStateInvalid || ! event_sp) { // We timed out and didn't get a stop event... return Error("Halt timed out. State = %s", StateAsCString(GetState())); } BroadcastEvent(event_sp); return Error(); } Error Process::StopForDestroyOrDetach(lldb::EventSP &exit_event_sp) { Error error; if (m_public_state.GetValue() == eStateRunning) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::%s() About to stop.", __FUNCTION__); ListenerSP listener_sp (new Listener("lldb.Process.StopForDestroyOrDetach.hijack")); HijackProcessEvents(listener_sp.get()); SendAsyncInterrupt(); // Consume the interrupt event. TimeValue timeout (TimeValue::Now()); timeout.OffsetWithSeconds(10); StateType state = WaitForProcessToStop (&timeout, &exit_event_sp, true, listener_sp.get()); RestoreProcessEvents(); // If the process exited while we were waiting for it to stop, put the exited event into // the shared pointer passed in and return. Our caller doesn't need to do anything else, since // they don't have a process anymore... if (state == eStateExited || m_private_state.GetValue() == eStateExited) { if (log) log->Printf("Process::%s() Process exited while waiting to stop.", __FUNCTION__); return error; } else exit_event_sp.reset(); // It is ok to consume any non-exit stop events if (state != eStateStopped) { if (log) log->Printf("Process::%s() failed to stop, state is: %s", __FUNCTION__, StateAsCString(state)); // If we really couldn't stop the process then we should just error out here, but if the // lower levels just bobbled sending the event and we really are stopped, then continue on. StateType private_state = m_private_state.GetValue(); if (private_state != eStateStopped) { return error; } } } return error; } Error Process::Detach (bool keep_stopped) { EventSP exit_event_sp; Error error; m_destroy_in_process = true; error = WillDetach(); if (error.Success()) { if (DetachRequiresHalt()) { error = StopForDestroyOrDetach (exit_event_sp); if (!error.Success()) { m_destroy_in_process = false; return error; } else if (exit_event_sp) { // We shouldn't need to do anything else here. There's no process left to detach from... StopPrivateStateThread(); m_destroy_in_process = false; return error; } } m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); error = DoDetach(keep_stopped); if (error.Success()) { DidDetach(); StopPrivateStateThread(); } else { return error; } } m_destroy_in_process = false; // If we exited when we were waiting for a process to stop, then // forward the event here so we don't lose the event if (exit_event_sp) { // Directly broadcast our exited event because we shut down our // private state thread above BroadcastEvent(exit_event_sp); } // If we have been interrupted (to kill us) in the middle of running, we may not end up propagating // the last events through the event system, in which case we might strand the write lock. Unlock // it here so when we do to tear down the process we don't get an error destroying the lock. m_public_run_lock.SetStopped(); return error; } Error Process::Destroy (bool force_kill) { // Tell ourselves we are in the process of destroying the process, so that we don't do any unnecessary work // that might hinder the destruction. Remember to set this back to false when we are done. That way if the attempt // failed and the process stays around for some reason it won't be in a confused state. if (force_kill) m_should_detach = false; if (GetShouldDetach()) { // FIXME: This will have to be a process setting: bool keep_stopped = false; Detach(keep_stopped); } m_destroy_in_process = true; Error error (WillDestroy()); if (error.Success()) { EventSP exit_event_sp; if (DestroyRequiresHalt()) { error = StopForDestroyOrDetach(exit_event_sp); } if (m_public_state.GetValue() != eStateRunning) { // Ditch all thread plans, and remove all our breakpoints: in case we have to restart the target to // kill it, we don't want it hitting a breakpoint... // Only do this if we've stopped, however, since if we didn't manage to halt it above, then // we're not going to have much luck doing this now. m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); } error = DoDestroy(); if (error.Success()) { DidDestroy(); StopPrivateStateThread(); } m_stdio_communication.Disconnect(); m_stdio_communication.StopReadThread(); m_stdin_forward = false; if (m_process_input_reader) { m_process_input_reader->SetIsDone(true); m_process_input_reader->Cancel(); m_process_input_reader.reset(); } // If we exited when we were waiting for a process to stop, then // forward the event here so we don't lose the event if (exit_event_sp) { // Directly broadcast our exited event because we shut down our // private state thread above BroadcastEvent(exit_event_sp); } // If we have been interrupted (to kill us) in the middle of running, we may not end up propagating // the last events through the event system, in which case we might strand the write lock. Unlock // it here so when we do to tear down the process we don't get an error destroying the lock. m_public_run_lock.SetStopped(); } m_destroy_in_process = false; return error; } Error Process::Signal (int signal) { Error error (WillSignal()); if (error.Success()) { error = DoSignal(signal); if (error.Success()) DidSignal(); } return error; } void Process::SetUnixSignals(UnixSignalsSP &&signals_sp) { assert (signals_sp && "null signals_sp"); m_unix_signals_sp = signals_sp; } const lldb::UnixSignalsSP & Process::GetUnixSignals () { assert (m_unix_signals_sp && "null m_unix_signals_sp"); return m_unix_signals_sp; } lldb::ByteOrder Process::GetByteOrder () const { return GetTarget().GetArchitecture().GetByteOrder(); } uint32_t Process::GetAddressByteSize () const { return GetTarget().GetArchitecture().GetAddressByteSize(); } bool Process::ShouldBroadcastEvent (Event *event_ptr) { const StateType state = Process::ProcessEventData::GetStateFromEvent (event_ptr); bool return_value = true; Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EVENTS | LIBLLDB_LOG_PROCESS)); switch (state) { case eStateDetached: case eStateExited: case eStateUnloaded: m_stdio_communication.SynchronizeWithReadThread(); m_stdio_communication.Disconnect(); m_stdio_communication.StopReadThread(); m_stdin_forward = false; // fall-through case eStateConnected: case eStateAttaching: case eStateLaunching: // These events indicate changes in the state of the debugging session, always report them. return_value = true; break; case eStateInvalid: // We stopped for no apparent reason, don't report it. return_value = false; break; case eStateRunning: case eStateStepping: // If we've started the target running, we handle the cases where we // are already running and where there is a transition from stopped to // running differently. // running -> running: Automatically suppress extra running events // stopped -> running: Report except when there is one or more no votes // and no yes votes. SynchronouslyNotifyStateChanged (state); if (m_force_next_event_delivery) return_value = true; else { switch (m_last_broadcast_state) { case eStateRunning: case eStateStepping: // We always suppress multiple runnings with no PUBLIC stop in between. return_value = false; break; default: // TODO: make this work correctly. For now always report // run if we aren't running so we don't miss any running // events. If I run the lldb/test/thread/a.out file and // break at main.cpp:58, run and hit the breakpoints on // multiple threads, then somehow during the stepping over // of all breakpoints no run gets reported. // This is a transition from stop to run. switch (m_thread_list.ShouldReportRun (event_ptr)) { case eVoteYes: case eVoteNoOpinion: return_value = true; break; case eVoteNo: return_value = false; break; } break; } } break; case eStateStopped: case eStateCrashed: case eStateSuspended: { // We've stopped. First see if we're going to restart the target. // If we are going to stop, then we always broadcast the event. // If we aren't going to stop, let the thread plans decide if we're going to report this event. // If no thread has an opinion, we don't report it. m_stdio_communication.SynchronizeWithReadThread(); RefreshStateAfterStop (); if (ProcessEventData::GetInterruptedFromEvent (event_ptr)) { if (log) log->Printf ("Process::ShouldBroadcastEvent (%p) stopped due to an interrupt, state: %s", static_cast(event_ptr), StateAsCString(state)); // Even though we know we are going to stop, we should let the threads have a look at the stop, // so they can properly set their state. m_thread_list.ShouldStop (event_ptr); return_value = true; } else { bool was_restarted = ProcessEventData::GetRestartedFromEvent (event_ptr); bool should_resume = false; // It makes no sense to ask "ShouldStop" if we've already been restarted... // Asking the thread list is also not likely to go well, since we are running again. // So in that case just report the event. if (!was_restarted) should_resume = m_thread_list.ShouldStop (event_ptr) == false; if (was_restarted || should_resume || m_resume_requested) { Vote stop_vote = m_thread_list.ShouldReportStop (event_ptr); if (log) - log->Printf ("Process::ShouldBroadcastEvent: should_stop: %i state: %s was_restarted: %i stop_vote: %d.", + log->Printf ("Process::ShouldBroadcastEvent: should_resume: %i state: %s was_restarted: %i stop_vote: %d.", should_resume, StateAsCString(state), was_restarted, stop_vote); switch (stop_vote) { case eVoteYes: return_value = true; break; case eVoteNoOpinion: case eVoteNo: return_value = false; break; } if (!was_restarted) { if (log) log->Printf ("Process::ShouldBroadcastEvent (%p) Restarting process from state: %s", static_cast(event_ptr), StateAsCString(state)); ProcessEventData::SetRestartedInEvent(event_ptr, true); PrivateResume (); } } else { return_value = true; SynchronouslyNotifyStateChanged (state); } } } break; } // Forcing the next event delivery is a one shot deal. So reset it here. m_force_next_event_delivery = false; // We do some coalescing of events (for instance two consecutive running events get coalesced.) // But we only coalesce against events we actually broadcast. So we use m_last_broadcast_state // to track that. NB - you can't use "m_public_state.GetValue()" for that purpose, as was originally done, // because the PublicState reflects the last event pulled off the queue, and there may be several // events stacked up on the queue unserviced. So the PublicState may not reflect the last broadcasted event // yet. m_last_broadcast_state gets updated here. if (return_value) m_last_broadcast_state = state; if (log) log->Printf ("Process::ShouldBroadcastEvent (%p) => new state: %s, last broadcast state: %s - %s", static_cast(event_ptr), StateAsCString(state), StateAsCString(m_last_broadcast_state), return_value ? "YES" : "NO"); return return_value; } bool Process::StartPrivateStateThread (bool is_secondary_thread) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS)); bool already_running = PrivateStateThreadIsValid (); if (log) log->Printf ("Process::%s()%s ", __FUNCTION__, already_running ? " already running" : " starting private state thread"); if (!is_secondary_thread && already_running) return true; // Create a thread that watches our internal state and controls which // events make it to clients (into the DCProcess event queue). char thread_name[1024]; if (HostInfo::GetMaxThreadNameLength() <= 30) { // On platforms with abbreviated thread name lengths, choose thread names that fit within the limit. if (already_running) snprintf(thread_name, sizeof(thread_name), "intern-state-OV"); else snprintf(thread_name, sizeof(thread_name), "intern-state"); } else { if (already_running) snprintf(thread_name, sizeof(thread_name), "", GetID()); else snprintf(thread_name, sizeof(thread_name), "", GetID()); } // Create the private state thread, and start it running. PrivateStateThreadArgs args = {this, is_secondary_thread}; m_private_state_thread = ThreadLauncher::LaunchThread(thread_name, Process::PrivateStateThread, (void *) &args, NULL, 8 * 1024 * 1024); if (m_private_state_thread.IsJoinable()) { ResumePrivateStateThread(); return true; } else return false; } void Process::PausePrivateStateThread () { ControlPrivateStateThread (eBroadcastInternalStateControlPause); } void Process::ResumePrivateStateThread () { ControlPrivateStateThread (eBroadcastInternalStateControlResume); } void Process::StopPrivateStateThread () { if (PrivateStateThreadIsValid ()) ControlPrivateStateThread (eBroadcastInternalStateControlStop); else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Went to stop the private state thread, but it was already invalid."); } } void Process::ControlPrivateStateThread (uint32_t signal) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); assert (signal == eBroadcastInternalStateControlStop || signal == eBroadcastInternalStateControlPause || signal == eBroadcastInternalStateControlResume); if (log) log->Printf ("Process::%s (signal = %d)", __FUNCTION__, signal); // Signal the private state thread. First we should copy this is case the // thread starts exiting since the private state thread will NULL this out // when it exits HostThread private_state_thread(m_private_state_thread); if (private_state_thread.IsJoinable()) { TimeValue timeout_time; bool timed_out; m_private_state_control_broadcaster.BroadcastEvent (signal, NULL); timeout_time = TimeValue::Now(); timeout_time.OffsetWithSeconds(2); if (log) log->Printf ("Sending control event of type: %d.", signal); m_private_state_control_wait.WaitForValueEqualTo (true, &timeout_time, &timed_out); m_private_state_control_wait.SetValue (false, eBroadcastNever); if (signal == eBroadcastInternalStateControlStop) { if (timed_out) { Error error = private_state_thread.Cancel(); if (log) log->Printf ("Timed out responding to the control event, cancel got error: \"%s\".", error.AsCString()); } else { if (log) log->Printf ("The control event killed the private state thread without having to cancel."); } thread_result_t result = NULL; private_state_thread.Join(&result); m_private_state_thread.Reset(); } } else { if (log) log->Printf ("Private state thread already dead, no need to signal it to stop."); } } void Process::SendAsyncInterrupt () { if (PrivateStateThreadIsValid()) m_private_state_broadcaster.BroadcastEvent (Process::eBroadcastBitInterrupt, NULL); else BroadcastEvent (Process::eBroadcastBitInterrupt, NULL); } void Process::HandlePrivateEvent (EventSP &event_sp) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); m_resume_requested = false; const StateType new_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); // First check to see if anybody wants a shot at this event: if (m_next_event_action_ap.get() != NULL) { NextEventAction::EventActionResult action_result = m_next_event_action_ap->PerformAction(event_sp); if (log) log->Printf ("Ran next event action, result was %d.", action_result); switch (action_result) { case NextEventAction::eEventActionSuccess: SetNextEventAction(NULL); break; case NextEventAction::eEventActionRetry: break; case NextEventAction::eEventActionExit: // Handle Exiting Here. If we already got an exited event, // we should just propagate it. Otherwise, swallow this event, // and set our state to exit so the next event will kill us. if (new_state != eStateExited) { // FIXME: should cons up an exited event, and discard this one. SetExitStatus(0, m_next_event_action_ap->GetExitString()); SetNextEventAction(NULL); return; } SetNextEventAction(NULL); break; } } // See if we should broadcast this state to external clients? const bool should_broadcast = ShouldBroadcastEvent (event_sp.get()); if (should_broadcast) { const bool is_hijacked = IsHijackedForEvent(eBroadcastBitStateChanged); if (log) { log->Printf ("Process::%s (pid = %" PRIu64 ") broadcasting new state %s (old state %s) to %s", __FUNCTION__, GetID(), StateAsCString(new_state), StateAsCString (GetState ()), is_hijacked ? "hijacked" : "public"); } Process::ProcessEventData::SetUpdateStateOnRemoval(event_sp.get()); if (StateIsRunningState (new_state)) { // Only push the input handler if we aren't fowarding events, // as this means the curses GUI is in use... // Or don't push it if we are launching since it will come up stopped. if (!GetTarget().GetDebugger().IsForwardingEvents() && new_state != eStateLaunching && new_state != eStateAttaching) { PushProcessIOHandler (); m_iohandler_sync.SetValue(m_iohandler_sync.GetValue()+1, eBroadcastAlways); if (log) log->Printf("Process::%s updated m_iohandler_sync to %d", __FUNCTION__, m_iohandler_sync.GetValue()); } } else if (StateIsStoppedState(new_state, false)) { if (!Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) { // If the lldb_private::Debugger is handling the events, we don't // want to pop the process IOHandler here, we want to do it when // we receive the stopped event so we can carefully control when // the process IOHandler is popped because when we stop we want to // display some text stating how and why we stopped, then maybe some // process/thread/frame info, and then we want the "(lldb) " prompt // to show up. If we pop the process IOHandler here, then we will // cause the command interpreter to become the top IOHandler after // the process pops off and it will update its prompt right away... // See the Debugger.cpp file where it calls the function as // "process_sp->PopProcessIOHandler()" to see where I am talking about. // Otherwise we end up getting overlapping "(lldb) " prompts and // garbled output. // // If we aren't handling the events in the debugger (which is indicated // by "m_target.GetDebugger().IsHandlingEvents()" returning false) or we // are hijacked, then we always pop the process IO handler manually. // Hijacking happens when the internal process state thread is running // thread plans, or when commands want to run in synchronous mode // and they call "process->WaitForProcessToStop()". An example of something // that will hijack the events is a simple expression: // // (lldb) expr (int)puts("hello") // // This will cause the internal process state thread to resume and halt // the process (and _it_ will hijack the eBroadcastBitStateChanged // events) and we do need the IO handler to be pushed and popped // correctly. if (is_hijacked || GetTarget().GetDebugger().IsHandlingEvents() == false) PopProcessIOHandler (); } } BroadcastEvent (event_sp); } else { if (log) { log->Printf ("Process::%s (pid = %" PRIu64 ") suppressing state %s (old state %s): should_broadcast == false", __FUNCTION__, GetID(), StateAsCString(new_state), StateAsCString (GetState ())); } } } Error Process::HaltPrivate() { EventSP event_sp; Error error (WillHalt()); if (error.Fail()) return error; // Ask the process subclass to actually halt our process bool caused_stop; error = DoHalt(caused_stop); DidHalt(); return error; } thread_result_t Process::PrivateStateThread (void *arg) { PrivateStateThreadArgs *real_args = static_cast (arg); thread_result_t result = real_args->process->RunPrivateStateThread(real_args->is_secondary_thread); return result; } thread_result_t Process::RunPrivateStateThread (bool is_secondary_thread) { bool control_only = true; m_private_state_control_wait.SetValue (false, eBroadcastNever); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") thread starting...", __FUNCTION__, static_cast(this), GetID()); bool exit_now = false; bool interrupt_requested = false; while (!exit_now) { EventSP event_sp; WaitForEventsPrivate (NULL, event_sp, control_only); if (event_sp->BroadcasterIs(&m_private_state_control_broadcaster)) { if (log) log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") got a control event: %d", __FUNCTION__, static_cast(this), GetID(), event_sp->GetType()); switch (event_sp->GetType()) { case eBroadcastInternalStateControlStop: exit_now = true; break; // doing any internal state management below case eBroadcastInternalStateControlPause: control_only = true; break; case eBroadcastInternalStateControlResume: control_only = false; break; } m_private_state_control_wait.SetValue (true, eBroadcastAlways); continue; } else if (event_sp->GetType() == eBroadcastBitInterrupt) { if (m_public_state.GetValue() == eStateAttaching) { if (log) log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") woke up with an interrupt while attaching - forwarding interrupt.", __FUNCTION__, static_cast(this), GetID()); BroadcastEvent (eBroadcastBitInterrupt, NULL); } else if(StateIsRunningState(m_last_broadcast_state)) { if (log) log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") woke up with an interrupt - Halting.", __FUNCTION__, static_cast(this), GetID()); Error error = HaltPrivate(); if (error.Fail() && log) log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") failed to halt the process: %s", __FUNCTION__, static_cast(this), GetID(), error.AsCString()); // Halt should generate a stopped event. Make a note of the fact that we were // doing the interrupt, so we can set the interrupted flag after we receive the // event. We deliberately set this to true even if HaltPrivate failed, so that we // can interrupt on the next natural stop. interrupt_requested = true; } else { // This can happen when someone (e.g. Process::Halt) sees that we are running and // sends an interrupt request, but the process actually stops before we receive // it. In that case, we can just ignore the request. We use // m_last_broadcast_state, because the Stopped event may not have been popped of // the event queue yet, which is when the public state gets updated. if (log) log->Printf("Process::%s ignoring interrupt as we have already stopped.", __FUNCTION__); } continue; } const StateType internal_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); if (internal_state != eStateInvalid) { if (m_clear_thread_plans_on_stop && StateIsStoppedState(internal_state, true)) { m_clear_thread_plans_on_stop = false; m_thread_list.DiscardThreadPlans(); } if (interrupt_requested) { if (StateIsStoppedState (internal_state, true)) { // We requested the interrupt, so mark this as such in the stop event so // clients can tell an interrupted process from a natural stop ProcessEventData::SetInterruptedInEvent (event_sp.get(), true); interrupt_requested = false; } else if (log) { log->Printf("Process::%s interrupt_requested, but a non-stopped state '%s' received.", __FUNCTION__, StateAsCString(internal_state)); } } HandlePrivateEvent (event_sp); } if (internal_state == eStateInvalid || internal_state == eStateExited || internal_state == eStateDetached ) { if (log) log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") about to exit with internal state %s...", __FUNCTION__, static_cast(this), GetID(), StateAsCString(internal_state)); break; } } // Verify log is still enabled before attempting to write to it... if (log) log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, static_cast(this), GetID()); // If we are a secondary thread, then the primary thread we are working for will have already // acquired the public_run_lock, and isn't done with what it was doing yet, so don't // try to change it on the way out. if (!is_secondary_thread) m_public_run_lock.SetStopped(); m_private_state_control_wait.SetValue (true, eBroadcastAlways); m_private_state_thread.Reset(); return NULL; } //------------------------------------------------------------------ // Process Event Data //------------------------------------------------------------------ Process::ProcessEventData::ProcessEventData () : EventData (), m_process_wp (), m_state (eStateInvalid), m_restarted (false), m_update_state (0), m_interrupted (false) { } Process::ProcessEventData::ProcessEventData (const ProcessSP &process_sp, StateType state) : EventData (), m_process_wp (), m_state (state), m_restarted (false), m_update_state (0), m_interrupted (false) { if (process_sp) m_process_wp = process_sp; } Process::ProcessEventData::~ProcessEventData() = default; const ConstString & Process::ProcessEventData::GetFlavorString () { static ConstString g_flavor ("Process::ProcessEventData"); return g_flavor; } const ConstString & Process::ProcessEventData::GetFlavor () const { return ProcessEventData::GetFlavorString (); } void Process::ProcessEventData::DoOnRemoval (Event *event_ptr) { ProcessSP process_sp(m_process_wp.lock()); if (!process_sp) return; // This function gets called twice for each event, once when the event gets pulled // off of the private process event queue, and then any number of times, first when it gets pulled off of // the public event queue, then other times when we're pretending that this is where we stopped at the // end of expression evaluation. m_update_state is used to distinguish these // three cases; it is 0 when we're just pulling it off for private handling, // and > 1 for expression evaluation, and we don't want to do the breakpoint command handling then. if (m_update_state != 1) return; process_sp->SetPublicState (m_state, Process::ProcessEventData::GetRestartedFromEvent(event_ptr)); // If this is a halt event, even if the halt stopped with some reason other than a plain interrupt (e.g. we had // already stopped for a breakpoint when the halt request came through) don't do the StopInfo actions, as they may // end up restarting the process. if (m_interrupted) return; // If we're stopped and haven't restarted, then do the StopInfo actions here: if (m_state == eStateStopped && ! m_restarted) { // Let process subclasses know we are about to do a public stop and // do anything they might need to in order to speed up register and // memory accesses. process_sp->WillPublicStop(); ThreadList &curr_thread_list = process_sp->GetThreadList(); uint32_t num_threads = curr_thread_list.GetSize(); uint32_t idx; // The actions might change one of the thread's stop_info's opinions about whether we should // stop the process, so we need to query that as we go. // One other complication here, is that we try to catch any case where the target has run (except for expressions) // and immediately exit, but if we get that wrong (which is possible) then the thread list might have changed, and // that would cause our iteration here to crash. We could make a copy of the thread list, but we'd really like // to also know if it has changed at all, so we make up a vector of the thread ID's and check what we get back // against this list & bag out if anything differs. std::vector thread_index_array(num_threads); for (idx = 0; idx < num_threads; ++idx) thread_index_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetIndexID(); // Use this to track whether we should continue from here. We will only continue the target running if // no thread says we should stop. Of course if some thread's PerformAction actually sets the target running, // then it doesn't matter what the other threads say... bool still_should_stop = false; // Sometimes - for instance if we have a bug in the stub we are talking to, we stop but no thread has a // valid stop reason. In that case we should just stop, because we have no way of telling what the right // thing to do is, and it's better to let the user decide than continue behind their backs. bool does_anybody_have_an_opinion = false; for (idx = 0; idx < num_threads; ++idx) { curr_thread_list = process_sp->GetThreadList(); if (curr_thread_list.GetSize() != num_threads) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Number of threads changed from %u to %u while processing event.", num_threads, curr_thread_list.GetSize()); break; } lldb::ThreadSP thread_sp = curr_thread_list.GetThreadAtIndex(idx); if (thread_sp->GetIndexID() != thread_index_array[idx]) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS)); if (log) log->Printf("The thread at position %u changed from %u to %u while processing event.", idx, thread_index_array[idx], thread_sp->GetIndexID()); break; } StopInfoSP stop_info_sp = thread_sp->GetStopInfo (); if (stop_info_sp && stop_info_sp->IsValid()) { does_anybody_have_an_opinion = true; bool this_thread_wants_to_stop; if (stop_info_sp->GetOverrideShouldStop()) { this_thread_wants_to_stop = stop_info_sp->GetOverriddenShouldStopValue(); } else { stop_info_sp->PerformAction(event_ptr); // The stop action might restart the target. If it does, then we want to mark that in the // event so that whoever is receiving it will know to wait for the running event and reflect // that state appropriately. // We also need to stop processing actions, since they aren't expecting the target to be running. // FIXME: we might have run. if (stop_info_sp->HasTargetRunSinceMe()) { SetRestarted (true); break; } this_thread_wants_to_stop = stop_info_sp->ShouldStop(event_ptr); } if (still_should_stop == false) still_should_stop = this_thread_wants_to_stop; } } if (!GetRestarted()) { if (!still_should_stop && does_anybody_have_an_opinion) { // We've been asked to continue, so do that here. SetRestarted(true); // Use the public resume method here, since this is just // extending a public resume. process_sp->PrivateResume(); } else { // If we didn't restart, run the Stop Hooks here: // They might also restart the target, so watch for that. process_sp->GetTarget().RunStopHooks(); if (process_sp->GetPrivateState() == eStateRunning) SetRestarted(true); } } } } void Process::ProcessEventData::Dump (Stream *s) const { ProcessSP process_sp(m_process_wp.lock()); if (process_sp) s->Printf(" process = %p (pid = %" PRIu64 "), ", static_cast(process_sp.get()), process_sp->GetID()); else s->PutCString(" process = NULL, "); s->Printf("state = %s", StateAsCString(GetState())); } const Process::ProcessEventData * Process::ProcessEventData::GetEventDataFromEvent (const Event *event_ptr) { if (event_ptr) { const EventData *event_data = event_ptr->GetData(); if (event_data && event_data->GetFlavor() == ProcessEventData::GetFlavorString()) return static_cast (event_ptr->GetData()); } return NULL; } ProcessSP Process::ProcessEventData::GetProcessFromEvent (const Event *event_ptr) { ProcessSP process_sp; const ProcessEventData *data = GetEventDataFromEvent (event_ptr); if (data) process_sp = data->GetProcessSP(); return process_sp; } StateType Process::ProcessEventData::GetStateFromEvent (const Event *event_ptr) { const ProcessEventData *data = GetEventDataFromEvent (event_ptr); if (data == NULL) return eStateInvalid; else return data->GetState(); } bool Process::ProcessEventData::GetRestartedFromEvent (const Event *event_ptr) { const ProcessEventData *data = GetEventDataFromEvent (event_ptr); if (data == NULL) return false; else return data->GetRestarted(); } void Process::ProcessEventData::SetRestartedInEvent (Event *event_ptr, bool new_value) { ProcessEventData *data = const_cast(GetEventDataFromEvent (event_ptr)); if (data != NULL) data->SetRestarted(new_value); } size_t Process::ProcessEventData::GetNumRestartedReasons(const Event *event_ptr) { ProcessEventData *data = const_cast(GetEventDataFromEvent (event_ptr)); if (data != NULL) return data->GetNumRestartedReasons(); else return 0; } const char * Process::ProcessEventData::GetRestartedReasonAtIndex(const Event *event_ptr, size_t idx) { ProcessEventData *data = const_cast(GetEventDataFromEvent (event_ptr)); if (data != NULL) return data->GetRestartedReasonAtIndex(idx); else return NULL; } void Process::ProcessEventData::AddRestartedReason (Event *event_ptr, const char *reason) { ProcessEventData *data = const_cast(GetEventDataFromEvent (event_ptr)); if (data != NULL) data->AddRestartedReason(reason); } bool Process::ProcessEventData::GetInterruptedFromEvent (const Event *event_ptr) { const ProcessEventData *data = GetEventDataFromEvent (event_ptr); if (data == NULL) return false; else return data->GetInterrupted (); } void Process::ProcessEventData::SetInterruptedInEvent (Event *event_ptr, bool new_value) { ProcessEventData *data = const_cast(GetEventDataFromEvent (event_ptr)); if (data != NULL) data->SetInterrupted(new_value); } bool Process::ProcessEventData::SetUpdateStateOnRemoval (Event *event_ptr) { ProcessEventData *data = const_cast(GetEventDataFromEvent (event_ptr)); if (data) { data->SetUpdateStateOnRemoval(); return true; } return false; } lldb::TargetSP Process::CalculateTarget () { return m_target_sp.lock(); } void Process::CalculateExecutionContext (ExecutionContext &exe_ctx) { exe_ctx.SetTargetPtr (&GetTarget()); exe_ctx.SetProcessPtr (this); exe_ctx.SetThreadPtr(NULL); exe_ctx.SetFramePtr (NULL); } //uint32_t //Process::ListProcessesMatchingName (const char *name, StringList &matches, std::vector &pids) //{ // return 0; //} // //ArchSpec //Process::GetArchSpecForExistingProcess (lldb::pid_t pid) //{ // return Host::GetArchSpecForExistingProcess (pid); //} // //ArchSpec //Process::GetArchSpecForExistingProcess (const char *process_name) //{ // return Host::GetArchSpecForExistingProcess (process_name); //} // void Process::AppendSTDOUT (const char * s, size_t len) { Mutex::Locker locker (m_stdio_communication_mutex); m_stdout_data.append (s, len); BroadcastEventIfUnique (eBroadcastBitSTDOUT, new ProcessEventData (shared_from_this(), GetState())); } void Process::AppendSTDERR (const char * s, size_t len) { Mutex::Locker locker (m_stdio_communication_mutex); m_stderr_data.append (s, len); BroadcastEventIfUnique (eBroadcastBitSTDERR, new ProcessEventData (shared_from_this(), GetState())); } void Process::BroadcastAsyncProfileData(const std::string &one_profile_data) { Mutex::Locker locker (m_profile_data_comm_mutex); m_profile_data.push_back(one_profile_data); BroadcastEventIfUnique (eBroadcastBitProfileData, new ProcessEventData (shared_from_this(), GetState())); } size_t Process::GetAsyncProfileData (char *buf, size_t buf_size, Error &error) { Mutex::Locker locker(m_profile_data_comm_mutex); if (m_profile_data.empty()) return 0; std::string &one_profile_data = m_profile_data.front(); size_t bytes_available = one_profile_data.size(); if (bytes_available > 0) { Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::GetProfileData (buf = %p, size = %" PRIu64 ")", static_cast(buf), static_cast(buf_size)); if (bytes_available > buf_size) { memcpy(buf, one_profile_data.c_str(), buf_size); one_profile_data.erase(0, buf_size); bytes_available = buf_size; } else { memcpy(buf, one_profile_data.c_str(), bytes_available); m_profile_data.erase(m_profile_data.begin()); } } return bytes_available; } //------------------------------------------------------------------ // Process STDIO //------------------------------------------------------------------ size_t Process::GetSTDOUT (char *buf, size_t buf_size, Error &error) { Mutex::Locker locker(m_stdio_communication_mutex); size_t bytes_available = m_stdout_data.size(); if (bytes_available > 0) { Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::GetSTDOUT (buf = %p, size = %" PRIu64 ")", static_cast(buf), static_cast(buf_size)); if (bytes_available > buf_size) { memcpy(buf, m_stdout_data.c_str(), buf_size); m_stdout_data.erase(0, buf_size); bytes_available = buf_size; } else { memcpy(buf, m_stdout_data.c_str(), bytes_available); m_stdout_data.clear(); } } return bytes_available; } size_t Process::GetSTDERR (char *buf, size_t buf_size, Error &error) { Mutex::Locker locker(m_stdio_communication_mutex); size_t bytes_available = m_stderr_data.size(); if (bytes_available > 0) { Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::GetSTDERR (buf = %p, size = %" PRIu64 ")", static_cast(buf), static_cast(buf_size)); if (bytes_available > buf_size) { memcpy(buf, m_stderr_data.c_str(), buf_size); m_stderr_data.erase(0, buf_size); bytes_available = buf_size; } else { memcpy(buf, m_stderr_data.c_str(), bytes_available); m_stderr_data.clear(); } } return bytes_available; } void Process::STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len) { Process *process = (Process *) baton; process->AppendSTDOUT (static_cast(src), src_len); } class IOHandlerProcessSTDIO : public IOHandler { public: IOHandlerProcessSTDIO (Process *process, int write_fd) : IOHandler(process->GetTarget().GetDebugger(), IOHandler::Type::ProcessIO), m_process (process), m_read_file (), m_write_file (write_fd, false), m_pipe () { m_pipe.CreateNew(false); m_read_file.SetDescriptor(GetInputFD(), false); } ~IOHandlerProcessSTDIO() override = default; // Each IOHandler gets to run until it is done. It should read data // from the "in" and place output into "out" and "err and return // when done. void Run () override { if (!m_read_file.IsValid() || !m_write_file.IsValid() || !m_pipe.CanRead() || !m_pipe.CanWrite()) { SetIsDone(true); return; } SetIsDone(false); const int read_fd = m_read_file.GetDescriptor(); TerminalState terminal_state; terminal_state.Save (read_fd, false); Terminal terminal(read_fd); terminal.SetCanonical(false); terminal.SetEcho(false); // FD_ZERO, FD_SET are not supported on windows #ifndef _WIN32 const int pipe_read_fd = m_pipe.GetReadFileDescriptor(); while (!GetIsDone()) { fd_set read_fdset; FD_ZERO (&read_fdset); FD_SET (read_fd, &read_fdset); FD_SET (pipe_read_fd, &read_fdset); const int nfds = std::max(read_fd, pipe_read_fd) + 1; int num_set_fds = select (nfds, &read_fdset, NULL, NULL, NULL); if (num_set_fds < 0) { const int select_errno = errno; if (select_errno != EINTR) SetIsDone(true); } else if (num_set_fds > 0) { char ch = 0; size_t n; if (FD_ISSET (read_fd, &read_fdset)) { n = 1; if (m_read_file.Read(&ch, n).Success() && n == 1) { if (m_write_file.Write(&ch, n).Fail() || n != 1) SetIsDone(true); } else SetIsDone(true); } if (FD_ISSET (pipe_read_fd, &read_fdset)) { size_t bytes_read; // Consume the interrupt byte Error error = m_pipe.Read(&ch, 1, bytes_read); if (error.Success()) { switch (ch) { case 'q': SetIsDone(true); break; case 'i': if (StateIsRunningState(m_process->GetState())) m_process->SendAsyncInterrupt(); break; } } } } } #endif terminal_state.Restore(); } void Cancel () override { char ch = 'q'; // Send 'q' for quit size_t bytes_written = 0; m_pipe.Write(&ch, 1, bytes_written); } bool Interrupt () override { // Do only things that are safe to do in an interrupt context (like in // a SIGINT handler), like write 1 byte to a file descriptor. This will // interrupt the IOHandlerProcessSTDIO::Run() and we can look at the byte // that was written to the pipe and then call m_process->SendAsyncInterrupt() // from a much safer location in code. if (m_active) { char ch = 'i'; // Send 'i' for interrupt size_t bytes_written = 0; Error result = m_pipe.Write(&ch, 1, bytes_written); return result.Success(); } else { // This IOHandler might be pushed on the stack, but not being run currently // so do the right thing if we aren't actively watching for STDIN by sending // the interrupt to the process. Otherwise the write to the pipe above would // do nothing. This can happen when the command interpreter is running and // gets a "expression ...". It will be on the IOHandler thread and sending // the input is complete to the delegate which will cause the expression to // run, which will push the process IO handler, but not run it. if (StateIsRunningState(m_process->GetState())) { m_process->SendAsyncInterrupt(); return true; } } return false; } void GotEOF() override { } protected: Process *m_process; File m_read_file; // Read from this file (usually actual STDIN for LLDB File m_write_file; // Write to this file (usually the master pty for getting io to debuggee) Pipe m_pipe; }; void Process::SetSTDIOFileDescriptor (int fd) { // First set up the Read Thread for reading/handling process I/O std::unique_ptr conn_ap (new ConnectionFileDescriptor (fd, true)); if (conn_ap.get()) { m_stdio_communication.SetConnection (conn_ap.release()); if (m_stdio_communication.IsConnected()) { m_stdio_communication.SetReadThreadBytesReceivedCallback (STDIOReadThreadBytesReceived, this); m_stdio_communication.StartReadThread(); // Now read thread is set up, set up input reader. if (!m_process_input_reader.get()) m_process_input_reader.reset (new IOHandlerProcessSTDIO (this, fd)); } } } bool Process::ProcessIOHandlerIsActive () { IOHandlerSP io_handler_sp (m_process_input_reader); if (io_handler_sp) return GetTarget().GetDebugger().IsTopIOHandler (io_handler_sp); return false; } bool Process::PushProcessIOHandler () { IOHandlerSP io_handler_sp (m_process_input_reader); if (io_handler_sp) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::%s pushing IO handler", __FUNCTION__); io_handler_sp->SetIsDone(false); GetTarget().GetDebugger().PushIOHandler (io_handler_sp); return true; } return false; } bool Process::PopProcessIOHandler () { IOHandlerSP io_handler_sp (m_process_input_reader); if (io_handler_sp) return GetTarget().GetDebugger().PopIOHandler (io_handler_sp); return false; } // The process needs to know about installed plug-ins void Process::SettingsInitialize () { Thread::SettingsInitialize (); } void Process::SettingsTerminate () { Thread::SettingsTerminate (); } namespace { // RestorePlanState is used to record the "is private", "is master" and "okay to discard" fields of // the plan we are running, and reset it on Clean or on destruction. // It will only reset the state once, so you can call Clean and then monkey with the state and it // won't get reset on you again. class RestorePlanState { public: RestorePlanState (lldb::ThreadPlanSP thread_plan_sp) : m_thread_plan_sp(thread_plan_sp), m_already_reset(false) { if (m_thread_plan_sp) { m_private = m_thread_plan_sp->GetPrivate(); m_is_master = m_thread_plan_sp->IsMasterPlan(); m_okay_to_discard = m_thread_plan_sp->OkayToDiscard(); } } ~RestorePlanState() { Clean(); } void Clean () { if (!m_already_reset && m_thread_plan_sp) { m_already_reset = true; m_thread_plan_sp->SetPrivate(m_private); m_thread_plan_sp->SetIsMasterPlan (m_is_master); m_thread_plan_sp->SetOkayToDiscard(m_okay_to_discard); } } private: lldb::ThreadPlanSP m_thread_plan_sp; bool m_already_reset; bool m_private; bool m_is_master; bool m_okay_to_discard; }; } // anonymous namespace ExpressionResults Process::RunThreadPlan (ExecutionContext &exe_ctx, lldb::ThreadPlanSP &thread_plan_sp, const EvaluateExpressionOptions &options, Stream &errors) { ExpressionResults return_value = eExpressionSetupError; if (thread_plan_sp.get() == NULL) { errors.Printf("RunThreadPlan called with empty thread plan."); return eExpressionSetupError; } if (!thread_plan_sp->ValidatePlan(NULL)) { errors.Printf ("RunThreadPlan called with an invalid thread plan."); return eExpressionSetupError; } if (exe_ctx.GetProcessPtr() != this) { errors.Printf("RunThreadPlan called on wrong process."); return eExpressionSetupError; } Thread *thread = exe_ctx.GetThreadPtr(); if (thread == NULL) { errors.Printf("RunThreadPlan called with invalid thread."); return eExpressionSetupError; } // We need to change some of the thread plan attributes for the thread plan runner. This will restore them // when we are done: RestorePlanState thread_plan_restorer(thread_plan_sp); // We rely on the thread plan we are running returning "PlanCompleted" if when it successfully completes. // For that to be true the plan can't be private - since private plans suppress themselves in the // GetCompletedPlan call. thread_plan_sp->SetPrivate(false); // The plans run with RunThreadPlan also need to be terminal master plans or when they are done we will end // up asking the plan above us whether we should stop, which may give the wrong answer. thread_plan_sp->SetIsMasterPlan (true); thread_plan_sp->SetOkayToDiscard(false); if (m_private_state.GetValue() != eStateStopped) { errors.Printf ("RunThreadPlan called while the private state was not stopped."); return eExpressionSetupError; } // Save the thread & frame from the exe_ctx for restoration after we run const uint32_t thread_idx_id = thread->GetIndexID(); StackFrameSP selected_frame_sp = thread->GetSelectedFrame(); if (!selected_frame_sp) { thread->SetSelectedFrame(0); selected_frame_sp = thread->GetSelectedFrame(); if (!selected_frame_sp) { errors.Printf("RunThreadPlan called without a selected frame on thread %d", thread_idx_id); return eExpressionSetupError; } } StackID ctx_frame_id = selected_frame_sp->GetStackID(); // N.B. Running the target may unset the currently selected thread and frame. We don't want to do that either, // so we should arrange to reset them as well. lldb::ThreadSP selected_thread_sp = GetThreadList().GetSelectedThread(); uint32_t selected_tid; StackID selected_stack_id; if (selected_thread_sp) { selected_tid = selected_thread_sp->GetIndexID(); selected_stack_id = selected_thread_sp->GetSelectedFrame()->GetStackID(); } else { selected_tid = LLDB_INVALID_THREAD_ID; } HostThread backup_private_state_thread; lldb::StateType old_state = eStateInvalid; lldb::ThreadPlanSP stopper_base_plan_sp; Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS)); if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) { // Yikes, we are running on the private state thread! So we can't wait for public events on this thread, since // we are the thread that is generating public events. // The simplest thing to do is to spin up a temporary thread to handle private state thread events while // we are fielding public events here. if (log) log->Printf ("Running thread plan on private state thread, spinning up another state thread to handle the events."); backup_private_state_thread = m_private_state_thread; // One other bit of business: we want to run just this thread plan and anything it pushes, and then stop, // returning control here. // But in the normal course of things, the plan above us on the stack would be given a shot at the stop // event before deciding to stop, and we don't want that. So we insert a "stopper" base plan on the stack // before the plan we want to run. Since base plans always stop and return control to the user, that will // do just what we want. stopper_base_plan_sp.reset(new ThreadPlanBase (*thread)); thread->QueueThreadPlan (stopper_base_plan_sp, false); // Have to make sure our public state is stopped, since otherwise the reporting logic below doesn't work correctly. old_state = m_public_state.GetValue(); m_public_state.SetValueNoLock(eStateStopped); // Now spin up the private state thread: StartPrivateStateThread(true); } thread->QueueThreadPlan(thread_plan_sp, false); // This used to pass "true" does that make sense? if (options.GetDebug()) { // In this case, we aren't actually going to run, we just want to stop right away. // Flush this thread so we will refetch the stacks and show the correct backtrace. // FIXME: To make this prettier we should invent some stop reason for this, but that // is only cosmetic, and this functionality is only of use to lldb developers who can // live with not pretty... thread->Flush(); return eExpressionStoppedForDebug; } Listener listener("lldb.process.listener.run-thread-plan"); lldb::EventSP event_to_broadcast_sp; { // This process event hijacker Hijacks the Public events and its destructor makes sure that the process events get // restored on exit to the function. // // If the event needs to propagate beyond the hijacker (e.g., the process exits during execution), then the event // is put into event_to_broadcast_sp for rebroadcasting. ProcessEventHijacker run_thread_plan_hijacker (*this, &listener); if (log) { StreamString s; thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); log->Printf ("Process::RunThreadPlan(): Resuming thread %u - 0x%4.4" PRIx64 " to run thread plan \"%s\".", thread->GetIndexID(), thread->GetID(), s.GetData()); } bool got_event; lldb::EventSP event_sp; lldb::StateType stop_state = lldb::eStateInvalid; TimeValue* timeout_ptr = NULL; TimeValue real_timeout; bool before_first_timeout = true; // This is set to false the first time that we have to halt the target. bool do_resume = true; bool handle_running_event = true; const uint64_t default_one_thread_timeout_usec = 250000; // This is just for accounting: uint32_t num_resumes = 0; uint32_t timeout_usec = options.GetTimeoutUsec(); uint32_t one_thread_timeout_usec; uint32_t all_threads_timeout_usec = 0; // If we are going to run all threads the whole time, or if we are only going to run one thread, // then we don't need the first timeout. So we set the final timeout, and pretend we are after the // first timeout already. if (!options.GetStopOthers() || !options.GetTryAllThreads()) { before_first_timeout = false; one_thread_timeout_usec = 0; all_threads_timeout_usec = timeout_usec; } else { uint32_t option_one_thread_timeout = options.GetOneThreadTimeoutUsec(); // If the overall wait is forever, then we only need to set the one thread timeout: if (timeout_usec == 0) { if (option_one_thread_timeout != 0) one_thread_timeout_usec = option_one_thread_timeout; else one_thread_timeout_usec = default_one_thread_timeout_usec; } else { // Otherwise, if the one thread timeout is set, make sure it isn't longer than the overall timeout, // and use it, otherwise use half the total timeout, bounded by the default_one_thread_timeout_usec. uint64_t computed_one_thread_timeout; if (option_one_thread_timeout != 0) { if (timeout_usec < option_one_thread_timeout) { errors.Printf("RunThreadPlan called without one thread timeout greater than total timeout"); return eExpressionSetupError; } computed_one_thread_timeout = option_one_thread_timeout; } else { computed_one_thread_timeout = timeout_usec / 2; if (computed_one_thread_timeout > default_one_thread_timeout_usec) computed_one_thread_timeout = default_one_thread_timeout_usec; } one_thread_timeout_usec = computed_one_thread_timeout; all_threads_timeout_usec = timeout_usec - one_thread_timeout_usec; } } if (log) log->Printf ("Stop others: %u, try all: %u, before_first: %u, one thread: %" PRIu32 " - all threads: %" PRIu32 ".\n", options.GetStopOthers(), options.GetTryAllThreads(), before_first_timeout, one_thread_timeout_usec, all_threads_timeout_usec); // This isn't going to work if there are unfetched events on the queue. // Are there cases where we might want to run the remaining events here, and then try to // call the function? That's probably being too tricky for our own good. Event *other_events = listener.PeekAtNextEvent(); if (other_events != NULL) { errors.Printf("Calling RunThreadPlan with pending events on the queue."); return eExpressionSetupError; } // We also need to make sure that the next event is delivered. We might be calling a function as part of // a thread plan, in which case the last delivered event could be the running event, and we don't want // event coalescing to cause us to lose OUR running event... ForceNextEventDelivery(); // This while loop must exit out the bottom, there's cleanup that we need to do when we are done. // So don't call return anywhere within it. #ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT // It's pretty much impossible to write test cases for things like: // One thread timeout expires, I go to halt, but the process already stopped // on the function call stop breakpoint. Turning on this define will make us not // fetch the first event till after the halt. So if you run a quick function, it will have // completed, and the completion event will be waiting, when you interrupt for halt. // The expression evaluation should still succeed. bool miss_first_event = true; #endif TimeValue one_thread_timeout; TimeValue final_timeout; while (1) { // We usually want to resume the process if we get to the top of the loop. // The only exception is if we get two running events with no intervening // stop, which can happen, we will just wait for then next stop event. if (log) log->Printf ("Top of while loop: do_resume: %i handle_running_event: %i before_first_timeout: %i.", do_resume, handle_running_event, before_first_timeout); if (do_resume || handle_running_event) { // Do the initial resume and wait for the running event before going further. if (do_resume) { num_resumes++; Error resume_error = PrivateResume (); if (!resume_error.Success()) { errors.Printf("Error resuming inferior the %d time: \"%s\".\n", num_resumes, resume_error.AsCString()); return_value = eExpressionSetupError; break; } } TimeValue resume_timeout = TimeValue::Now(); resume_timeout.OffsetWithMicroSeconds(500000); got_event = listener.WaitForEvent(&resume_timeout, event_sp); if (!got_event) { if (log) log->Printf ("Process::RunThreadPlan(): didn't get any event after resume %d, exiting.", num_resumes); errors.Printf("Didn't get any event after resume %d, exiting.", num_resumes); return_value = eExpressionSetupError; break; } stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); if (stop_state != eStateRunning) { bool restarted = false; if (stop_state == eStateStopped) { restarted = Process::ProcessEventData::GetRestartedFromEvent(event_sp.get()); if (log) log->Printf("Process::RunThreadPlan(): didn't get running event after " "resume %d, got %s instead (restarted: %i, do_resume: %i, handle_running_event: %i).", num_resumes, StateAsCString(stop_state), restarted, do_resume, handle_running_event); } if (restarted) { // This is probably an overabundance of caution, I don't think I should ever get a stopped & restarted // event here. But if I do, the best thing is to Halt and then get out of here. const bool clear_thread_plans = false; const bool use_run_lock = false; Halt(clear_thread_plans, use_run_lock); } errors.Printf("Didn't get running event after initial resume, got %s instead.", StateAsCString(stop_state)); return_value = eExpressionSetupError; break; } if (log) log->PutCString ("Process::RunThreadPlan(): resuming succeeded."); // We need to call the function synchronously, so spin waiting for it to return. // If we get interrupted while executing, we're going to lose our context, and // won't be able to gather the result at this point. // We set the timeout AFTER the resume, since the resume takes some time and we // don't want to charge that to the timeout. } else { if (log) log->PutCString ("Process::RunThreadPlan(): waiting for next event."); } if (before_first_timeout) { if (options.GetTryAllThreads()) { one_thread_timeout = TimeValue::Now(); one_thread_timeout.OffsetWithMicroSeconds(one_thread_timeout_usec); timeout_ptr = &one_thread_timeout; } else { if (timeout_usec == 0) timeout_ptr = NULL; else { final_timeout = TimeValue::Now(); final_timeout.OffsetWithMicroSeconds (timeout_usec); timeout_ptr = &final_timeout; } } } else { if (timeout_usec == 0) timeout_ptr = NULL; else { final_timeout = TimeValue::Now(); final_timeout.OffsetWithMicroSeconds (all_threads_timeout_usec); timeout_ptr = &final_timeout; } } do_resume = true; handle_running_event = true; // Now wait for the process to stop again: event_sp.reset(); if (log) { if (timeout_ptr) { log->Printf ("Process::RunThreadPlan(): about to wait - now is %" PRIu64 " - endpoint is %" PRIu64, TimeValue::Now().GetAsMicroSecondsSinceJan1_1970(), timeout_ptr->GetAsMicroSecondsSinceJan1_1970()); } else { log->Printf ("Process::RunThreadPlan(): about to wait forever."); } } #ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT // See comment above... if (miss_first_event) { usleep(1000); miss_first_event = false; got_event = false; } else #endif got_event = listener.WaitForEvent (timeout_ptr, event_sp); if (got_event) { if (event_sp.get()) { bool keep_going = false; if (event_sp->GetType() == eBroadcastBitInterrupt) { const bool clear_thread_plans = false; const bool use_run_lock = false; Halt(clear_thread_plans, use_run_lock); return_value = eExpressionInterrupted; errors.Printf ("Execution halted by user interrupt."); if (log) log->Printf ("Process::RunThreadPlan(): Got interrupted by eBroadcastBitInterrupted, exiting."); break; } else { stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); if (log) log->Printf("Process::RunThreadPlan(): in while loop, got event: %s.", StateAsCString(stop_state)); switch (stop_state) { case lldb::eStateStopped: { // We stopped, figure out what we are going to do now. ThreadSP thread_sp = GetThreadList().FindThreadByIndexID (thread_idx_id); if (!thread_sp) { // Ooh, our thread has vanished. Unlikely that this was successful execution... if (log) log->Printf ("Process::RunThreadPlan(): execution completed but our thread (index-id=%u) has vanished.", thread_idx_id); return_value = eExpressionInterrupted; } else { // If we were restarted, we just need to go back up to fetch another event. if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) { if (log) { log->Printf ("Process::RunThreadPlan(): Got a stop and restart, so we'll continue waiting."); } keep_going = true; do_resume = false; handle_running_event = true; } else { StopInfoSP stop_info_sp (thread_sp->GetStopInfo ()); StopReason stop_reason = eStopReasonInvalid; if (stop_info_sp) stop_reason = stop_info_sp->GetStopReason(); // FIXME: We only check if the stop reason is plan complete, should we make sure that // it is OUR plan that is complete? if (stop_reason == eStopReasonPlanComplete) { if (log) log->PutCString ("Process::RunThreadPlan(): execution completed successfully."); // Restore the plan state so it will get reported as intended when we are done. thread_plan_restorer.Clean(); return_value = eExpressionCompleted; } else { // Something restarted the target, so just wait for it to stop for real. if (stop_reason == eStopReasonBreakpoint) { if (log) log->Printf ("Process::RunThreadPlan() stopped for breakpoint: %s.", stop_info_sp->GetDescription()); return_value = eExpressionHitBreakpoint; if (!options.DoesIgnoreBreakpoints()) { // Restore the plan state and then force Private to false. We are // going to stop because of this plan so we need it to become a public // plan or it won't report correctly when we continue to its termination // later on. thread_plan_restorer.Clean(); if (thread_plan_sp) thread_plan_sp->SetPrivate(false); event_to_broadcast_sp = event_sp; } } else { if (log) log->PutCString ("Process::RunThreadPlan(): thread plan didn't successfully complete."); if (!options.DoesUnwindOnError()) event_to_broadcast_sp = event_sp; return_value = eExpressionInterrupted; } } } } } break; case lldb::eStateRunning: // This shouldn't really happen, but sometimes we do get two running events without an // intervening stop, and in that case we should just go back to waiting for the stop. do_resume = false; keep_going = true; handle_running_event = false; break; default: if (log) log->Printf("Process::RunThreadPlan(): execution stopped with unexpected state: %s.", StateAsCString(stop_state)); if (stop_state == eStateExited) event_to_broadcast_sp = event_sp; errors.Printf ("Execution stopped with unexpected state.\n"); return_value = eExpressionInterrupted; break; } } if (keep_going) continue; else break; } else { if (log) log->PutCString ("Process::RunThreadPlan(): got_event was true, but the event pointer was null. How odd..."); return_value = eExpressionInterrupted; break; } } else { // If we didn't get an event that means we've timed out... // We will interrupt the process here. Depending on what we were asked to do we will // either exit, or try with all threads running for the same timeout. if (log) { if (options.GetTryAllThreads()) { if (before_first_timeout) { if (timeout_usec != 0) { log->Printf ("Process::RunThreadPlan(): Running function with one thread timeout timed out, " "running for %" PRIu32 " usec with all threads enabled.", all_threads_timeout_usec); } else { log->Printf ("Process::RunThreadPlan(): Running function with one thread timeout timed out, " "running forever with all threads enabled."); } } else log->Printf ("Process::RunThreadPlan(): Restarting function with all threads enabled " "and timeout: %u timed out, abandoning execution.", timeout_usec); } else log->Printf ("Process::RunThreadPlan(): Running function with timeout: %u timed out, " "abandoning execution.", timeout_usec); } // It is possible that between the time we issued the Halt, and we get around to calling Halt the target // could have stopped. That's fine, Halt will figure that out and send the appropriate Stopped event. // BUT it is also possible that we stopped & restarted (e.g. hit a signal with "stop" set to false.) In // that case, we'll get the stopped & restarted event, and we should go back to waiting for the Halt's // stopped event. That's what this while loop does. bool back_to_top = true; uint32_t try_halt_again = 0; bool do_halt = true; const uint32_t num_retries = 5; while (try_halt_again < num_retries) { Error halt_error; if (do_halt) { if (log) log->Printf ("Process::RunThreadPlan(): Running Halt."); const bool clear_thread_plans = false; const bool use_run_lock = false; Halt(clear_thread_plans, use_run_lock); } if (halt_error.Success()) { if (log) log->PutCString ("Process::RunThreadPlan(): Halt succeeded."); real_timeout = TimeValue::Now(); real_timeout.OffsetWithMicroSeconds(500000); got_event = listener.WaitForEvent(&real_timeout, event_sp); if (got_event) { stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); if (log) { log->Printf ("Process::RunThreadPlan(): Stopped with event: %s", StateAsCString(stop_state)); if (stop_state == lldb::eStateStopped && Process::ProcessEventData::GetInterruptedFromEvent(event_sp.get())) log->PutCString (" Event was the Halt interruption event."); } if (stop_state == lldb::eStateStopped) { // Between the time we initiated the Halt and the time we delivered it, the process could have // already finished its job. Check that here: if (thread->IsThreadPlanDone (thread_plan_sp.get())) { if (log) log->PutCString ("Process::RunThreadPlan(): Even though we timed out, the call plan was done. " "Exiting wait loop."); return_value = eExpressionCompleted; back_to_top = false; break; } if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) { if (log) log->PutCString ("Process::RunThreadPlan(): Went to halt but got a restarted event, there must be an un-restarted stopped event so try again... " "Exiting wait loop."); try_halt_again++; do_halt = false; continue; } if (!options.GetTryAllThreads()) { if (log) log->PutCString ("Process::RunThreadPlan(): try_all_threads was false, we stopped so now we're quitting."); return_value = eExpressionInterrupted; back_to_top = false; break; } if (before_first_timeout) { // Set all the other threads to run, and return to the top of the loop, which will continue; before_first_timeout = false; thread_plan_sp->SetStopOthers (false); if (log) log->PutCString ("Process::RunThreadPlan(): about to resume."); back_to_top = true; break; } else { // Running all threads failed, so return Interrupted. if (log) log->PutCString("Process::RunThreadPlan(): running all threads timed out."); return_value = eExpressionInterrupted; back_to_top = false; break; } } } else { if (log) log->PutCString("Process::RunThreadPlan(): halt said it succeeded, but I got no event. " "I'm getting out of here passing Interrupted."); return_value = eExpressionInterrupted; back_to_top = false; break; } } else { try_halt_again++; continue; } } if (!back_to_top || try_halt_again > num_retries) break; else continue; } } // END WAIT LOOP // If we had to start up a temporary private state thread to run this thread plan, shut it down now. if (backup_private_state_thread.IsJoinable()) { StopPrivateStateThread(); Error error; m_private_state_thread = backup_private_state_thread; if (stopper_base_plan_sp) { thread->DiscardThreadPlansUpToPlan(stopper_base_plan_sp); } if (old_state != eStateInvalid) m_public_state.SetValueNoLock(old_state); } if (return_value != eExpressionCompleted && log) { // Print a backtrace into the log so we can figure out where we are: StreamString s; s.PutCString("Thread state after unsuccessful completion: \n"); thread->GetStackFrameStatus (s, 0, UINT32_MAX, true, UINT32_MAX); log->PutCString(s.GetData()); } // Restore the thread state if we are going to discard the plan execution. There are three cases where this // could happen: // 1) The execution successfully completed // 2) We hit a breakpoint, and ignore_breakpoints was true // 3) We got some other error, and discard_on_error was true bool should_unwind = (return_value == eExpressionInterrupted && options.DoesUnwindOnError()) || (return_value == eExpressionHitBreakpoint && options.DoesIgnoreBreakpoints()); if (return_value == eExpressionCompleted || should_unwind) { thread_plan_sp->RestoreThreadState(); } // Now do some processing on the results of the run: if (return_value == eExpressionInterrupted || return_value == eExpressionHitBreakpoint) { if (log) { StreamString s; if (event_sp) event_sp->Dump (&s); else { log->PutCString ("Process::RunThreadPlan(): Stop event that interrupted us is NULL."); } StreamString ts; const char *event_explanation = NULL; do { if (!event_sp) { event_explanation = ""; break; } else if (event_sp->GetType() == eBroadcastBitInterrupt) { event_explanation = ""; break; } else { const Process::ProcessEventData *event_data = Process::ProcessEventData::GetEventDataFromEvent (event_sp.get()); if (!event_data) { event_explanation = ""; break; } Process *process = event_data->GetProcessSP().get(); if (!process) { event_explanation = ""; break; } ThreadList &thread_list = process->GetThreadList(); uint32_t num_threads = thread_list.GetSize(); uint32_t thread_index; ts.Printf("<%u threads> ", num_threads); for (thread_index = 0; thread_index < num_threads; ++thread_index) { Thread *thread = thread_list.GetThreadAtIndex(thread_index).get(); if (!thread) { ts.Printf(" "); continue; } ts.Printf("<0x%4.4" PRIx64 " ", thread->GetID()); RegisterContext *register_context = thread->GetRegisterContext().get(); if (register_context) ts.Printf("[ip 0x%" PRIx64 "] ", register_context->GetPC()); else ts.Printf("[ip unknown] "); // Show the private stop info here, the public stop info will be from the last natural stop. lldb::StopInfoSP stop_info_sp = thread->GetPrivateStopInfo(); if (stop_info_sp) { const char *stop_desc = stop_info_sp->GetDescription(); if (stop_desc) ts.PutCString (stop_desc); } ts.Printf(">"); } event_explanation = ts.GetData(); } } while (0); if (event_explanation) log->Printf("Process::RunThreadPlan(): execution interrupted: %s %s", s.GetData(), event_explanation); else log->Printf("Process::RunThreadPlan(): execution interrupted: %s", s.GetData()); } if (should_unwind) { if (log) log->Printf ("Process::RunThreadPlan: ExecutionInterrupted - discarding thread plans up to %p.", static_cast(thread_plan_sp.get())); thread->DiscardThreadPlansUpToPlan (thread_plan_sp); } else { if (log) log->Printf ("Process::RunThreadPlan: ExecutionInterrupted - for plan: %p not discarding.", static_cast(thread_plan_sp.get())); } } else if (return_value == eExpressionSetupError) { if (log) log->PutCString("Process::RunThreadPlan(): execution set up error."); if (options.DoesUnwindOnError()) { thread->DiscardThreadPlansUpToPlan (thread_plan_sp); } } else { if (thread->IsThreadPlanDone (thread_plan_sp.get())) { if (log) log->PutCString("Process::RunThreadPlan(): thread plan is done"); return_value = eExpressionCompleted; } else if (thread->WasThreadPlanDiscarded (thread_plan_sp.get())) { if (log) log->PutCString("Process::RunThreadPlan(): thread plan was discarded"); return_value = eExpressionDiscarded; } else { if (log) log->PutCString("Process::RunThreadPlan(): thread plan stopped in mid course"); if (options.DoesUnwindOnError() && thread_plan_sp) { if (log) log->PutCString("Process::RunThreadPlan(): discarding thread plan 'cause unwind_on_error is set."); thread->DiscardThreadPlansUpToPlan (thread_plan_sp); } } } // Thread we ran the function in may have gone away because we ran the target // Check that it's still there, and if it is put it back in the context. Also restore the // frame in the context if it is still present. thread = GetThreadList().FindThreadByIndexID(thread_idx_id, true).get(); if (thread) { exe_ctx.SetFrameSP (thread->GetFrameWithStackID (ctx_frame_id)); } // Also restore the current process'es selected frame & thread, since this function calling may // be done behind the user's back. if (selected_tid != LLDB_INVALID_THREAD_ID) { if (GetThreadList().SetSelectedThreadByIndexID (selected_tid) && selected_stack_id.IsValid()) { // We were able to restore the selected thread, now restore the frame: Mutex::Locker lock(GetThreadList().GetMutex()); StackFrameSP old_frame_sp = GetThreadList().GetSelectedThread()->GetFrameWithStackID(selected_stack_id); if (old_frame_sp) GetThreadList().GetSelectedThread()->SetSelectedFrame(old_frame_sp.get()); } } } // If the process exited during the run of the thread plan, notify everyone. if (event_to_broadcast_sp) { if (log) log->PutCString("Process::RunThreadPlan(): rebroadcasting event."); BroadcastEvent(event_to_broadcast_sp); } return return_value; } const char * Process::ExecutionResultAsCString (ExpressionResults result) { const char *result_name; switch (result) { case eExpressionCompleted: result_name = "eExpressionCompleted"; break; case eExpressionDiscarded: result_name = "eExpressionDiscarded"; break; case eExpressionInterrupted: result_name = "eExpressionInterrupted"; break; case eExpressionHitBreakpoint: result_name = "eExpressionHitBreakpoint"; break; case eExpressionSetupError: result_name = "eExpressionSetupError"; break; case eExpressionParseError: result_name = "eExpressionParseError"; break; case eExpressionResultUnavailable: result_name = "eExpressionResultUnavailable"; break; case eExpressionTimedOut: result_name = "eExpressionTimedOut"; break; case eExpressionStoppedForDebug: result_name = "eExpressionStoppedForDebug"; break; } return result_name; } void Process::GetStatus (Stream &strm) { const StateType state = GetState(); if (StateIsStoppedState(state, false)) { if (state == eStateExited) { int exit_status = GetExitStatus(); const char *exit_description = GetExitDescription(); strm.Printf ("Process %" PRIu64 " exited with status = %i (0x%8.8x) %s\n", GetID(), exit_status, exit_status, exit_description ? exit_description : ""); } else { if (state == eStateConnected) strm.Printf ("Connected to remote target.\n"); else strm.Printf ("Process %" PRIu64 " %s\n", GetID(), StateAsCString (state)); } } else { strm.Printf ("Process %" PRIu64 " is running.\n", GetID()); } } size_t Process::GetThreadStatus (Stream &strm, bool only_threads_with_stop_reason, uint32_t start_frame, uint32_t num_frames, uint32_t num_frames_with_source) { size_t num_thread_infos_dumped = 0; // You can't hold the thread list lock while calling Thread::GetStatus. That very well might run code (e.g. if we need it // to get return values or arguments.) For that to work the process has to be able to acquire it. So instead copy the thread // ID's, and look them up one by one: uint32_t num_threads; std::vector thread_id_array; //Scope for thread list locker; { Mutex::Locker locker (GetThreadList().GetMutex()); ThreadList &curr_thread_list = GetThreadList(); num_threads = curr_thread_list.GetSize(); uint32_t idx; thread_id_array.resize(num_threads); for (idx = 0; idx < num_threads; ++idx) thread_id_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetID(); } for (uint32_t i = 0; i < num_threads; i++) { ThreadSP thread_sp(GetThreadList().FindThreadByID(thread_id_array[i])); if (thread_sp) { if (only_threads_with_stop_reason) { StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); if (stop_info_sp.get() == NULL || !stop_info_sp->IsValid()) continue; } thread_sp->GetStatus (strm, start_frame, num_frames, num_frames_with_source); ++num_thread_infos_dumped; } else { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::GetThreadStatus - thread 0x" PRIu64 " vanished while running Thread::GetStatus."); } } return num_thread_infos_dumped; } void Process::AddInvalidMemoryRegion (const LoadRange ®ion) { m_memory_cache.AddInvalidRange(region.GetRangeBase(), region.GetByteSize()); } bool Process::RemoveInvalidMemoryRange (const LoadRange ®ion) { return m_memory_cache.RemoveInvalidRange(region.GetRangeBase(), region.GetByteSize()); } void Process::AddPreResumeAction (PreResumeActionCallback callback, void *baton) { m_pre_resume_actions.push_back(PreResumeCallbackAndBaton (callback, baton)); } bool Process::RunPreResumeActions () { bool result = true; while (!m_pre_resume_actions.empty()) { struct PreResumeCallbackAndBaton action = m_pre_resume_actions.back(); m_pre_resume_actions.pop_back(); bool this_result = action.callback (action.baton); if (result == true) result = this_result; } return result; } void Process::ClearPreResumeActions () { m_pre_resume_actions.clear(); } ProcessRunLock & Process::GetRunLock() { if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) return m_private_run_lock; else return m_public_run_lock; } void Process::Flush () { m_thread_list.Flush(); m_extended_thread_list.Flush(); m_extended_thread_stop_id = 0; m_queue_list.Clear(); m_queue_list_stop_id = 0; } void Process::DidExec () { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::%s()", __FUNCTION__); Target &target = GetTarget(); target.CleanupProcess (); target.ClearModules(false); m_dynamic_checkers_ap.reset(); m_abi_sp.reset(); m_system_runtime_ap.reset(); m_os_ap.reset(); m_dyld_ap.reset(); m_jit_loaders_ap.reset(); m_image_tokens.clear(); m_allocated_memory_cache.Clear(); m_language_runtimes.clear(); m_instrumentation_runtimes.clear(); m_thread_list.DiscardThreadPlans(); m_memory_cache.Clear(true); m_stop_info_override_callback = NULL; DoDidExec(); CompleteAttach (); // Flush the process (threads and all stack frames) after running CompleteAttach() // in case the dynamic loader loaded things in new locations. Flush(); // After we figure out what was loaded/unloaded in CompleteAttach, // we need to let the target know so it can do any cleanup it needs to. target.DidExec(); } addr_t Process::ResolveIndirectFunction(const Address *address, Error &error) { if (address == nullptr) { error.SetErrorString("Invalid address argument"); return LLDB_INVALID_ADDRESS; } addr_t function_addr = LLDB_INVALID_ADDRESS; addr_t addr = address->GetLoadAddress(&GetTarget()); std::map::const_iterator iter = m_resolved_indirect_addresses.find(addr); if (iter != m_resolved_indirect_addresses.end()) { function_addr = (*iter).second; } else { if (!InferiorCall(this, address, function_addr)) { Symbol *symbol = address->CalculateSymbolContextSymbol(); error.SetErrorStringWithFormat ("Unable to call resolver for indirect function %s", symbol ? symbol->GetName().AsCString() : ""); function_addr = LLDB_INVALID_ADDRESS; } else { m_resolved_indirect_addresses.insert(std::pair(addr, function_addr)); } } return function_addr; } void Process::ModulesDidLoad (ModuleList &module_list) { SystemRuntime *sys_runtime = GetSystemRuntime(); if (sys_runtime) { sys_runtime->ModulesDidLoad (module_list); } GetJITLoaders().ModulesDidLoad (module_list); // Give runtimes a chance to be created. InstrumentationRuntime::ModulesDidLoad(module_list, this, m_instrumentation_runtimes); // Tell runtimes about new modules. for (auto pos = m_instrumentation_runtimes.begin(); pos != m_instrumentation_runtimes.end(); ++pos) { InstrumentationRuntimeSP runtime = pos->second; runtime->ModulesDidLoad(module_list); } // Let any language runtimes we have already created know // about the modules that loaded. // Iterate over a copy of this language runtime list in case // the language runtime ModulesDidLoad somehow causes the language // riuntime to be unloaded. LanguageRuntimeCollection language_runtimes(m_language_runtimes); for (const auto &pair: language_runtimes) { // We must check language_runtime_sp to make sure it is not // NULL as we might cache the fact that we didn't have a // language runtime for a language. LanguageRuntimeSP language_runtime_sp = pair.second; if (language_runtime_sp) language_runtime_sp->ModulesDidLoad(module_list); } LoadOperatingSystemPlugin(false); } void Process::PrintWarning (uint64_t warning_type, const void *repeat_key, const char *fmt, ...) { bool print_warning = true; StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream(); if (stream_sp.get() == nullptr) return; if (warning_type == eWarningsOptimization && GetWarningsOptimization() == false) { return; } if (repeat_key != nullptr) { WarningsCollection::iterator it = m_warnings_issued.find (warning_type); if (it == m_warnings_issued.end()) { m_warnings_issued[warning_type] = WarningsPointerSet(); m_warnings_issued[warning_type].insert (repeat_key); } else { if (it->second.find (repeat_key) != it->second.end()) { print_warning = false; } else { it->second.insert (repeat_key); } } } if (print_warning) { va_list args; va_start (args, fmt); stream_sp->PrintfVarArg (fmt, args); va_end (args); } } void Process::PrintWarningOptimization (const SymbolContext &sc) { if (GetWarningsOptimization() == true && sc.module_sp.get() && sc.module_sp->GetFileSpec().GetFilename().IsEmpty() == false && sc.function && sc.function->GetIsOptimized() == true) { PrintWarning (Process::Warnings::eWarningsOptimization, sc.module_sp.get(), "%s was compiled with optimization - stepping may behave oddly; variables may not be available.\n", sc.module_sp->GetFileSpec().GetFilename().GetCString()); } } ThreadCollectionSP Process::GetHistoryThreads(lldb::addr_t addr) { ThreadCollectionSP threads; const MemoryHistorySP &memory_history = MemoryHistory::FindPlugin(shared_from_this()); if (! memory_history.get()) { return threads; } threads.reset(new ThreadCollection(memory_history->GetHistoryThreads(addr))); return threads; } InstrumentationRuntimeSP Process::GetInstrumentationRuntime(lldb::InstrumentationRuntimeType type) { InstrumentationRuntimeCollection::iterator pos; pos = m_instrumentation_runtimes.find (type); if (pos == m_instrumentation_runtimes.end()) { return InstrumentationRuntimeSP(); } else return (*pos).second; } bool Process::GetModuleSpec(const FileSpec& module_file_spec, const ArchSpec& arch, ModuleSpec& module_spec) { module_spec.Clear(); return false; } size_t Process::AddImageToken(lldb::addr_t image_ptr) { m_image_tokens.push_back(image_ptr); return m_image_tokens.size() - 1; } lldb::addr_t Process::GetImagePtrFromToken(size_t token) const { if (token < m_image_tokens.size()) return m_image_tokens[token]; return LLDB_INVALID_IMAGE_TOKEN; } void Process::ResetImageToken(size_t token) { if (token < m_image_tokens.size()) m_image_tokens[token] = LLDB_INVALID_IMAGE_TOKEN; } Index: projects/clang380-import/contrib/llvm/tools/lldb/tools/lldb-mi/MICmdCmdSymbol.cpp =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb/tools/lldb-mi/MICmdCmdSymbol.cpp (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb/tools/lldb-mi/MICmdCmdSymbol.cpp (revision 293283) @@ -1,261 +1,253 @@ //===-- MICmdCmdSymbol.cpp --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Overview: CMICmdCmdSymbolListLines implementation. // Third Party Headers: #include "lldb/API/SBCommandInterpreter.h" // In-house headers: #include "MICmdArgValFile.h" #include "MICmdCmdSymbol.h" #include "MICmnLLDBDebugSessionInfo.h" #include "MICmnMIResultRecord.h" #include "MICmnMIValueList.h" #include "MICmnMIValueTuple.h" #include "MIUtilParse.h" //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdSymbolListLines constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdSymbolListLines::CMICmdCmdSymbolListLines() : m_constStrArgNameFile("file") { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "symbol-list-lines"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdSymbolListLines::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdSymbolListLines destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdSymbolListLines::~CMICmdCmdSymbolListLines() { } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdSymbolListLines::ParseArgs() { m_setCmdArgs.Add(new CMICmdArgValFile(m_constStrArgNameFile, true, true)); return ParseValidateCmdOptions(); } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Synopsis: -symbol-list-lines file // Ref: http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Symbol-Query.html#GDB_002fMI-Symbol-Query // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdSymbolListLines::Execute() { CMICMDBASE_GETOPTION(pArgFile, File, m_constStrArgNameFile); const CMIUtilString &strFilePath(pArgFile->GetValue()); - // FIXME: this won't work for header files! To try and use existing - // commands to get this to work for header files would be too slow. - // Instead, this code should be rewritten to use APIs and/or support - // should be added to lldb which would work for header files. - const CMIUtilString strCmd(CMIUtilString::Format("target modules dump line-table \"%s\"", strFilePath.AddSlashes().c_str())); + const CMIUtilString strCmd(CMIUtilString::Format("source info --file \"%s\"", strFilePath.AddSlashes().c_str())); CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); const lldb::ReturnStatus rtn = rSessionInfo.GetDebugger().GetCommandInterpreter().HandleCommand(strCmd.c_str(), m_lldbResult); MIunused(rtn); return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Helper function for parsing the header returned from lldb for the command: // target modules dump line-table // where the header is of the format: // Line table for /path/to/file in `/path/to/module // Args: input - (R) Input string to parse. // file - (W) String representing the file. // Return: bool - True = input was parsed successfully, false = input could not be parsed. // Throws: None. //-- static bool ParseLLDBLineAddressHeader(const char *input, CMIUtilString &file) { // Match LineEntry using regex. static MIUtilParse::CRegexParser g_lineentry_header_regex( - "^ *Line table for (.+) in `(.+)$"); - // ^1=file ^2=module + "^ *Lines found for file (.+) in compilation unit (.+) in `(.+)$"); + // ^1=file ^2=cu ^3=module - MIUtilParse::CRegexParser::Match match(3); + MIUtilParse::CRegexParser::Match match(4); const bool ok = g_lineentry_header_regex.Execute(input, match); if (ok) file = match.GetMatchAtIndex(1); return ok; } //++ ------------------------------------------------------------------------------------ // Details: Helper function for parsing a line entry returned from lldb for the command: // target modules dump line-table // where the line entry is of the format: // 0x0000000100000e70: /path/to/file:3002[:4] // addr file line column(opt) // Args: input - (R) Input string to parse. // addr - (W) String representing the pc address. // file - (W) String representing the file. // line - (W) String representing the line. // Return: bool - True = input was parsed successfully, false = input could not be parsed. // Throws: None. //-- static bool ParseLLDBLineAddressEntry(const char *input, CMIUtilString &addr, CMIUtilString &file, CMIUtilString &line) { // Note: Ambiguities arise because the column is optional, and // because : can appear in filenames or as a byte in a multibyte // UTF8 character. We keep those cases to a minimum by using regex // to work on the string from both the left and right, so that what // is remains is assumed to be the filename. // Match LineEntry using regex. static MIUtilParse::CRegexParser g_lineentry_nocol_regex( - "^ *(0x[0-9a-fA-F]+): (.+):([0-9]+)$"); + "^ *\\[(0x[0-9a-fA-F]+)-(0x[0-9a-fA-F]+)\\): (.+):([0-9]+)$"); static MIUtilParse::CRegexParser g_lineentry_col_regex( - "^ *(0x[0-9a-fA-F]+): (.+):([0-9]+):[0-9]+$"); - // ^1=addr ^2=f ^3=line ^4=:col(opt) + "^ *\\[(0x[0-9a-fA-F]+)-(0x[0-9a-fA-F]+)\\): (.+):([0-9]+):[0-9]+$"); + // ^1=start ^2=end ^3=f ^4=line ^5=:col(opt) - MIUtilParse::CRegexParser::Match match(5); + MIUtilParse::CRegexParser::Match match(6); // First try matching the LineEntry with the column, // then try without the column. const bool ok = g_lineentry_col_regex.Execute(input, match) || g_lineentry_nocol_regex.Execute(input, match); if (ok) { addr = match.GetMatchAtIndex(1); - file = match.GetMatchAtIndex(2); - line = match.GetMatchAtIndex(3); + file = match.GetMatchAtIndex(3); + line = match.GetMatchAtIndex(4); } return ok; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdSymbolListLines::Acknowledge() { if (m_lldbResult.GetErrorSize() > 0) { const char *pLldbErr = m_lldbResult.GetError(); const CMIUtilString strMsg(CMIUtilString(pLldbErr).StripCRAll()); const CMICmnMIValueConst miValueConst(strMsg); const CMICmnMIValueResult miValueResult("message", miValueConst); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; } else { CMIUtilString::VecString_t vecLines; const CMIUtilString strLldbMsg(m_lldbResult.GetOutput()); const MIuint nLines(strLldbMsg.SplitLines(vecLines)); // Parse the file from the header. const CMIUtilString &rWantFile(vecLines[0]); CMIUtilString strWantFile; if (!ParseLLDBLineAddressHeader(rWantFile.c_str(), strWantFile)) { // Unexpected error - parsing failed. // MI print "%s^error,msg=\"Command '-symbol-list-lines'. Error: Line address header is absent or has an unknown format.\"" const CMICmnMIValueConst miValueConst(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_SOME_ERROR), m_cmdData.strMiCmd.c_str(), "Line address header is absent or has an unknown format.")); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } // Parse the line address entries. CMICmnMIValueList miValueList(true); for (MIuint i = 1; i < nLines; ++i) { // String looks like: // 0x0000000100000e70: /path/to/file:3[:4] const CMIUtilString &rLine(vecLines[i]); CMIUtilString strAddr; CMIUtilString strFile; CMIUtilString strLine; if (!ParseLLDBLineAddressEntry(rLine.c_str(), strAddr, strFile, strLine)) - continue; - - // Skip entries which don't match the desired source. - if (strWantFile != strFile) continue; const CMICmnMIValueConst miValueConst(strAddr); const CMICmnMIValueResult miValueResult("pc", miValueConst); CMICmnMIValueTuple miValueTuple(miValueResult); const CMICmnMIValueConst miValueConst2(strLine); const CMICmnMIValueResult miValueResult2("line", miValueConst2); miValueTuple.Add(miValueResult2); miValueList.Add(miValueTuple); } // MI print "%s^done,lines=[{pc=\"%d\",line=\"%d\"}...]" const CMICmnMIValueResult miValueResult("lines", miValueList); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); m_miResultRecord = miRecordResult; } return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdSymbolListLines::CreateSelf() { return new CMICmdCmdSymbolListLines(); } Index: projects/clang380-import/contrib/llvm/tools/lldb =================================================================== --- projects/clang380-import/contrib/llvm/tools/lldb (revision 293282) +++ projects/clang380-import/contrib/llvm/tools/lldb (revision 293283) Property changes on: projects/clang380-import/contrib/llvm/tools/lldb ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /vendor/lldb/dist:r287520-292931,292933-293115,293117-293281