diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:12:36 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:12:36 +0000 |
commit | ef5d0b5e97ec8e6fa395d377b09aa7755e345b4f (patch) | |
tree | 27916256fdeeb57d10d2f3d6948be5d71a703215 /tools | |
parent | 76e0736e7fcfeb179779e49c05604464b1ccd704 (diff) |
Notes
Diffstat (limited to 'tools')
56 files changed, 4130 insertions, 264 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 7b26f5907a86..75c9c15c487d 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -8,4 +8,5 @@ add_subdirectory(lldb-mi) if (LLDB_CAN_USE_LLDB_SERVER) add_subdirectory(lldb-server) endif() -add_subdirectory(intel-mpx) +add_subdirectory(intel-features) +add_subdirectory(lldb-test) diff --git a/tools/argdumper/CMakeLists.txt b/tools/argdumper/CMakeLists.txt index 42f1dd51582a..9b22103bc8af 100644 --- a/tools/argdumper/CMakeLists.txt +++ b/tools/argdumper/CMakeLists.txt @@ -1,10 +1,6 @@ -include(${LLDB_PROJECT_ROOT}/cmake/LLDBDependencies.cmake) - add_lldb_tool(lldb-argdumper INCLUDE_IN_FRAMEWORK argdumper.cpp LINK_LIBS - lldbCore lldbUtility ) - diff --git a/tools/debugserver/source/CMakeLists.txt b/tools/debugserver/source/CMakeLists.txt index bdca1602f4a8..d99880cd97f3 100644 --- a/tools/debugserver/source/CMakeLists.txt +++ b/tools/debugserver/source/CMakeLists.txt @@ -1,4 +1,5 @@ include(CheckCXXCompilerFlag) +include(CheckLibraryExists) include_directories(${CMAKE_CURRENT_BINARY_DIR}/..) include_directories(${LLDB_SOURCE_DIR}/source) include_directories(MacOSX/DarwinLog) @@ -26,6 +27,8 @@ if (CXX_SUPPORTS_NO_EXTENDED_OFFSETOF) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-extended-offsetof") endif () +check_library_exists(compression compression_encode_buffer "" HAVE_LIBCOMPRESSION) + add_subdirectory(MacOSX) set(generated_mach_interfaces @@ -94,32 +97,108 @@ add_library(lldbDebugserverCommon ${lldbDebugserverCommonSources}) if (APPLE) if(IOS) - find_library(COCOA_LIBRARY UIKit) - target_link_libraries(lldbDebugserverCommon INTERFACE ${COCOA_LIBRARY} ${CORE_FOUNDATION_LIBRARY} ${FOUNDATION_LIBRARY}) + find_library(BACKBOARD_LIBRARY BackBoardServices + PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks) + find_library(FRONTBOARD_LIBRARY FrontBoardServices + PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks) + find_library(SPRINGBOARD_LIBRARY SpringBoardServices + PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks) + find_library(MOBILESERVICES_LIBRARY MobileCoreServices + PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks) + find_library(LOCKDOWN_LIBRARY lockdown) + + if(NOT BACKBOARD_LIBRARY) + set(SKIP_DEBUGSERVER True) + endif() else() find_library(COCOA_LIBRARY Cocoa) - target_link_libraries(lldbDebugserverCommon INTERFACE ${COCOA_LIBRARY}) endif() endif() -target_link_libraries(lldbDebugserverCommon +if(HAVE_LIBCOMPRESSION) + set(LIBCOMPRESSION compression) +endif() + +if(NOT SKIP_DEBUGSERVER) + target_link_libraries(lldbDebugserverCommon + INTERFACE ${COCOA_LIBRARY} + ${CORE_FOUNDATION_LIBRARY} + ${FOUNDATION_LIBRARY} + ${BACKBOARD_LIBRARY} + ${FRONTBOARD_LIBRARY} + ${SPRINGBOARD_LIBRARY} + ${MOBILESERVICES_LIBRARY} + ${LOCKDOWN_LIBRARY} + lldbDebugserverArchSupport + lldbDebugserverDarwin_DarwinLog + ${LIBCOMPRESSION}) + if(HAVE_LIBCOMPRESSION) + set_property(TARGET lldbDebugserverCommon APPEND PROPERTY + COMPILE_DEFINITIONS HAVE_LIBCOMPRESSION) + endif() + set(LLVM_OPTIONAL_SOURCES ${lldbDebugserverCommonSources}) + add_lldb_tool(debugserver INCLUDE_IN_FRAMEWORK + debugserver.cpp + + LINK_LIBS + lldbDebugserverCommon + ) + if(IOS) + set_property(TARGET lldbDebugserverCommon APPEND PROPERTY COMPILE_DEFINITIONS + WITH_LOCKDOWN + WITH_FBS + WITH_BKS + ) + set_property(TARGET debugserver APPEND PROPERTY COMPILE_DEFINITIONS + WITH_LOCKDOWN + WITH_FBS + WITH_BKS + ) + set_property(TARGET lldbDebugserverCommon APPEND PROPERTY COMPILE_FLAGS + -F${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks + ) + endif() +endif() + +if(IOS) + add_library(lldbDebugserverCommon_NonUI ${lldbDebugserverCommonSources}) + target_link_libraries(lldbDebugserverCommon_NonUI INTERFACE ${COCOA_LIBRARY} ${CORE_FOUNDATION_LIBRARY} ${FOUNDATION_LIBRARY} lldbDebugserverArchSupport - lldbDebugserverDarwin_DarwinLog) + lldbDebugserverDarwin_DarwinLog + ${LIBCOMPRESSION}) + if(HAVE_LIBCOMPRESSION) + set_property(TARGET lldbDebugserverCommon_NonUI APPEND PROPERTY + COMPILE_DEFINITIONS HAVE_LIBCOMPRESSION) + endif() -set(LLVM_OPTIONAL_SOURCES ${lldbDebugserverCommonSources}) -add_lldb_tool(debugserver INCLUDE_IN_FRAMEWORK - debugserver.cpp + add_lldb_tool(debugserver-nonui + debugserver.cpp - LINK_LIBS - lldbDebugserverCommon - ) + LINK_LIBS + lldbDebugserverCommon_NonUI + ) +endif() + +set(entitlements_xml ${CMAKE_CURRENT_SOURCE_DIR}/debugserver-macosx-entitlements.plist) +if(IOS) + set(entitlements_xml ${CMAKE_CURRENT_SOURCE_DIR}/debugserver-entitlements.plist) +endif() set(LLDB_CODESIGN_IDENTITY "lldb_codesign" CACHE STRING "Identity used for code signing. Set to empty string to skip the signing step.") +set(LLDB_USE_ENTITLEMENTS_Default On) +if("${LLDB_CODESIGN_IDENTITY}" STREQUAL "lldb_codesign") + set(LLDB_USE_ENTITLEMENTS_Default Off) +endif() +option(LLDB_USE_ENTITLEMENTS "Use entitlements when codesigning (Defaults Off when using lldb_codesign identity, otherwise On)" ${LLDB_USE_ENTITLEMENTS_Default}) + if (NOT ("${LLDB_CODESIGN_IDENTITY}" STREQUAL "")) + if(LLDB_USE_ENTITLEMENTS) + set(entitlements_flags --entitlements ${entitlements_xml}) + endif() execute_process( COMMAND xcrun -f codesign_allocate OUTPUT_STRIP_TRAILING_WHITESPACE @@ -129,9 +208,20 @@ if (NOT ("${LLDB_CODESIGN_IDENTITY}" STREQUAL "")) POST_BUILD COMMAND ${CMAKE_COMMAND} -E env CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --force --sign ${LLDB_CODESIGN_IDENTITY} + ${entitlements_flags} $<TARGET_FILE:debugserver> WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin ) + if(IOS) + add_custom_command(TARGET debugserver-nonui + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} + codesign --force --sign ${LLDB_CODESIGN_IDENTITY} + ${entitlements_flags} + $<TARGET_FILE:debugserver> + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin + ) + endif() endif() diff --git a/tools/debugserver/source/DNB.cpp b/tools/debugserver/source/DNB.cpp index 9c6c44d18df1..1168f1fc0a1e 100644 --- a/tools/debugserver/source/DNB.cpp +++ b/tools/debugserver/source/DNB.cpp @@ -368,7 +368,7 @@ nub_process_t DNBProcessLaunch( if (launch_err.Fail()) { const char *launch_err_str = launch_err.AsString(); if (launch_err_str) { - strncpy(err_str, launch_err_str, err_len - 1); + strlcpy(err_str, launch_err_str, err_len - 1); err_str[err_len - 1] = '\0'; // Make sure the error string is terminated } @@ -1698,7 +1698,7 @@ nub_bool_t DNBResolveExecutablePath(const char *path, char *resolved_path, if (realpath(path, max_path)) { // Found the path relatively... - ::strncpy(resolved_path, max_path, resolved_path_size); + ::strlcpy(resolved_path, max_path, resolved_path_size); return strlen(resolved_path) + 1 < resolved_path_size; } else { // Not a relative path, check the PATH environment variable if the @@ -1722,7 +1722,7 @@ nub_bool_t DNBResolveExecutablePath(const char *path, char *resolved_path, result += path; struct stat s; if (stat(result.c_str(), &s) == 0) { - ::strncpy(resolved_path, result.c_str(), resolved_path_size); + ::strlcpy(resolved_path, result.c_str(), resolved_path_size); return result.size() + 1 < resolved_path_size; } } diff --git a/tools/debugserver/source/DNBDataRef.cpp b/tools/debugserver/source/DNBDataRef.cpp index d7dce1ab7338..8c12b0afac69 100644 --- a/tools/debugserver/source/DNBDataRef.cpp +++ b/tools/debugserver/source/DNBDataRef.cpp @@ -60,7 +60,7 @@ uint16_t DNBDataRef::Get16(offset_t *offset_ptr) const { uint16_t val = 0; if (ValidOffsetForDataOfSize(*offset_ptr, sizeof(val))) { const uint8_t *p = m_start + *offset_ptr; - val = *(uint16_t *)p; + memcpy(&val, p, sizeof(uint16_t)); if (m_swap) val = OSSwapInt16(val); @@ -78,7 +78,7 @@ uint32_t DNBDataRef::Get32(offset_t *offset_ptr) const { uint32_t val = 0; if (ValidOffsetForDataOfSize(*offset_ptr, sizeof(val))) { const uint8_t *p = m_start + *offset_ptr; - val = *(uint32_t *)p; + memcpy(&val, p, sizeof(uint32_t)); if (m_swap) val = OSSwapInt32(val); @@ -95,7 +95,7 @@ uint64_t DNBDataRef::Get64(offset_t *offset_ptr) const { uint64_t val = 0; if (ValidOffsetForDataOfSize(*offset_ptr, sizeof(val))) { const uint8_t *p = m_start + *offset_ptr; - val = *(uint64_t *)p; + memcpy(&val, p, sizeof(uint64_t)); if (m_swap) val = OSSwapInt64(val); @@ -123,7 +123,7 @@ uint32_t DNBDataRef::GetMax32(offset_t *offset_ptr, uint32_t byte_size) const { return Get32(offset_ptr); break; default: - assert(!"GetMax32 unhandled case!"); + assert(false && "GetMax32 unhandled case!"); break; } return 0; @@ -150,7 +150,7 @@ uint64_t DNBDataRef::GetMax64(offset_t *offset_ptr, uint32_t size) const { return Get64(offset_ptr); break; default: - assert(!"GetMax64 unhandled case!"); + assert(false && "GetMax64 unhandled case!"); break; } return 0; @@ -174,7 +174,7 @@ const char *DNBDataRef::GetCStr(offset_t *offset_ptr, uint32_t fixed_length) const { const char *s = NULL; if (m_start < m_end) { - s = (char *)m_start + *offset_ptr; + s = (const char *)m_start + *offset_ptr; // Advance the offset if (fixed_length) diff --git a/tools/debugserver/source/DNBRegisterInfo.cpp b/tools/debugserver/source/DNBRegisterInfo.cpp index fadcc5ddb06e..b85b39378183 100644 --- a/tools/debugserver/source/DNBRegisterInfo.cpp +++ b/tools/debugserver/source/DNBRegisterInfo.cpp @@ -35,7 +35,7 @@ bool DNBRegisterValueClass::IsValid() const { do { \ if (pos < end) { \ if (i > 0) { \ - strncpy(pos, ", ", end - pos); \ + strlcpy(pos, ", ", end - pos); \ pos += 2; \ } \ } \ @@ -69,7 +69,7 @@ void DNBRegisterValueClass::Dump(const char *pre, const char *post) const { value.v_uint64[1]); break; default: - strncpy(str, "0x", 3); + strlcpy(str, "0x", 3); pos = str + 2; for (uint32_t i = 0; i < info.size; ++i) { if (pos < end) diff --git a/tools/debugserver/source/JSON.cpp b/tools/debugserver/source/JSON.cpp index c914f2498f09..439918b6fcc4 100644 --- a/tools/debugserver/source/JSON.cpp +++ b/tools/debugserver/source/JSON.cpp @@ -58,7 +58,6 @@ uint64_t JSONNumber::GetAsUnsigned() const { case DataType::Double: return (uint64_t)m_data.m_double; } - assert("Unhandled data type"); } int64_t JSONNumber::GetAsSigned() const { @@ -70,7 +69,6 @@ int64_t JSONNumber::GetAsSigned() const { case DataType::Double: return (int64_t)m_data.m_double; } - assert("Unhandled data type"); } double JSONNumber::GetAsDouble() const { @@ -82,7 +80,6 @@ double JSONNumber::GetAsDouble() const { case DataType::Double: return m_data.m_double; } - assert("Unhandled data type"); } void JSONNumber::Write(std::ostream &s) { diff --git a/tools/debugserver/source/MacOSX/MachException.cpp b/tools/debugserver/source/MacOSX/MachException.cpp index 5f085867db2c..cc309e47d86b 100644 --- a/tools/debugserver/source/MacOSX/MachException.cpp +++ b/tools/debugserver/source/MacOSX/MachException.cpp @@ -47,13 +47,13 @@ extern "C" kern_return_t catch_mach_exception_raise_state_identity( extern "C" boolean_t mach_exc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); -// Any access to the g_message variable should be done by locking the -// g_message_mutex first, using the g_message variable, then unlocking -// the g_message_mutex. See MachException::Message::CatchExceptionRaise() -// for sample code. - +// Note: g_message points to the storage allocated to catch the data from +// catching the current exception raise. It's populated when we catch a raised +// exception which can't immediately be replied to. +// +// If it becomes possible to catch exceptions from multiple threads +// simultaneously, accesses to g_message would need to be mutually exclusive. static MachException::Data *g_message = NULL; -// static pthread_mutex_t g_message_mutex = PTHREAD_MUTEX_INITIALIZER; extern "C" kern_return_t catch_mach_exception_raise_state( mach_port_t exc_port, exception_type_t exc_type, @@ -113,8 +113,7 @@ catch_mach_exception_raise(mach_port_t exc_port, mach_port_t thread_port, g_message->task_port = task_port; g_message->thread_port = thread_port; g_message->exc_type = exc_type; - for (mach_msg_type_number_t i=0; i<exc_data_count; ++i) - g_message->exc_data.push_back(exc_data[i]); + g_message->AppendExceptionData(exc_data, exc_data_count); return KERN_SUCCESS; } else if (!MachTask::IsValid(g_message->task_port)) { // Our original exception port isn't valid anymore check for a SIGTRAP @@ -126,8 +125,7 @@ catch_mach_exception_raise(mach_port_t exc_port, mach_port_t thread_port, g_message->task_port = task_port; g_message->thread_port = thread_port; g_message->exc_type = exc_type; - for (mach_msg_type_number_t i=0; i<exc_data_count; ++i) - g_message->exc_data.push_back(exc_data[i]); + g_message->AppendExceptionData(exc_data, exc_data_count); return KERN_SUCCESS; } } @@ -272,9 +270,6 @@ kern_return_t MachException::Message::Receive(mach_port_t port, bool MachException::Message::CatchExceptionRaise(task_t task) { bool success = false; - // locker will keep a mutex locked until it goes out of scope - // PThreadMutex::Locker locker(&g_message_mutex); - // DNBLogThreaded("calling mach_exc_server"); state.task_port = task; g_message = &state; // The exc_server function is the MIG generated server handling function diff --git a/tools/debugserver/source/MacOSX/MachException.h b/tools/debugserver/source/MacOSX/MachException.h index a45a41e01f42..e1af12def10a 100644 --- a/tools/debugserver/source/MacOSX/MachException.h +++ b/tools/debugserver/source/MacOSX/MachException.h @@ -71,6 +71,15 @@ public: return (exc_type == EXC_BREAKPOINT || ((exc_type == EXC_SOFTWARE) && exc_data[0] == 1)); } + void AppendExceptionData(mach_exception_data_t Data, + mach_msg_type_number_t Count) { + mach_exception_data_type_t Buf; + for (mach_msg_type_number_t i = 0; i < Count; ++i) { + // Perform an unaligned copy. + memcpy(&Buf, Data + i, sizeof(mach_exception_data_type_t)); + exc_data.push_back(Buf); + } + } void Dump() const; void DumpStopReason() const; bool GetStopInfo(struct DNBThreadStopInfo *stop_info) const; diff --git a/tools/debugserver/source/MacOSX/MachProcess.mm b/tools/debugserver/source/MacOSX/MachProcess.mm index a93f724849b8..d4dff223bde0 100644 --- a/tools/debugserver/source/MacOSX/MachProcess.mm +++ b/tools/debugserver/source/MacOSX/MachProcess.mm @@ -112,7 +112,9 @@ static bool CallBoardSystemServiceOpenApplication(NSString *bundleIDNSStr, if (!cstr) cstr = "<Unknown Bundle ID>"; - DNBLog("About to launch process for bundle ID: %s", cstr); + NSString *description = [options description]; + DNBLog("About to launch process for bundle ID: %s - options:\n%s", cstr, + [description UTF8String]); [system_service openApplication:bundleIDNSStr options:options @@ -188,6 +190,28 @@ static bool CallBoardSystemServiceOpenApplication(NSString *bundleIDNSStr, } #endif +#if defined(WITH_BKS) || defined(WITH_FBS) +static void SplitEventData(const char *data, std::vector<std::string> &elements) +{ + elements.clear(); + if (!data) + return; + + const char *start = data; + + while (*start != '\0') { + const char *token = strchr(start, ':'); + if (!token) { + elements.push_back(std::string(start)); + return; + } + if (token != start) + elements.push_back(std::string(start, token - start)); + start = ++token; + } +} +#endif + #ifdef WITH_BKS #import <Foundation/Foundation.h> extern "C" { @@ -222,21 +246,31 @@ static void SetBKSError(NSInteger error_code, DNBError &error) { static bool BKSAddEventDataToOptions(NSMutableDictionary *options, const char *event_data, DNBError &option_error) { - if (strcmp(event_data, "BackgroundContentFetching") == 0) { - DNBLog("Setting ActivateForEvent key in options dictionary."); - NSDictionary *event_details = [NSDictionary dictionary]; - NSDictionary *event_dictionary = [NSDictionary - dictionaryWithObject:event_details - forKey: - BKSActivateForEventOptionTypeBackgroundContentFetching]; - [options setObject:event_dictionary - forKey:BKSOpenApplicationOptionKeyActivateForEvent]; - return true; - } else { - DNBLogError("Unrecognized event type: %s. Ignoring.", event_data); - option_error.SetErrorString("Unrecognized event data."); - return false; + std::vector<std::string> values; + SplitEventData(event_data, values); + bool found_one = false; + for (std::string value : values) + { + if (value.compare("BackgroundContentFetching") == 0) { + DNBLog("Setting ActivateForEvent key in options dictionary."); + NSDictionary *event_details = [NSDictionary dictionary]; + NSDictionary *event_dictionary = [NSDictionary + dictionaryWithObject:event_details + forKey: + BKSActivateForEventOptionTypeBackgroundContentFetching]; + [options setObject:event_dictionary + forKey:BKSOpenApplicationOptionKeyActivateForEvent]; + found_one = true; + } else if (value.compare("ActivateSuspended") == 0) { + DNBLog("Setting ActivateSuspended key in options dictionary."); + [options setObject:@YES forKey: BKSOpenApplicationOptionKeyActivateSuspended]; + found_one = true; + } else { + DNBLogError("Unrecognized event type: %s. Ignoring.", value.c_str()); + option_error.SetErrorString("Unrecognized event data"); + } } + return found_one; } static NSMutableDictionary *BKSCreateOptionsDictionary( @@ -322,21 +356,31 @@ static void SetFBSError(NSInteger error_code, DNBError &error) { static bool FBSAddEventDataToOptions(NSMutableDictionary *options, const char *event_data, DNBError &option_error) { - if (strcmp(event_data, "BackgroundContentFetching") == 0) { - DNBLog("Setting ActivateForEvent key in options dictionary."); - NSDictionary *event_details = [NSDictionary dictionary]; - NSDictionary *event_dictionary = [NSDictionary - dictionaryWithObject:event_details - forKey: - FBSActivateForEventOptionTypeBackgroundContentFetching]; - [options setObject:event_dictionary - forKey:FBSOpenApplicationOptionKeyActivateForEvent]; - return true; - } else { - DNBLogError("Unrecognized event type: %s. Ignoring.", event_data); - option_error.SetErrorString("Unrecognized event data."); - return false; + std::vector<std::string> values; + SplitEventData(event_data, values); + bool found_one = false; + for (std::string value : values) + { + if (value.compare("BackgroundContentFetching") == 0) { + DNBLog("Setting ActivateForEvent key in options dictionary."); + NSDictionary *event_details = [NSDictionary dictionary]; + NSDictionary *event_dictionary = [NSDictionary + dictionaryWithObject:event_details + forKey: + FBSActivateForEventOptionTypeBackgroundContentFetching]; + [options setObject:event_dictionary + forKey:FBSOpenApplicationOptionKeyActivateForEvent]; + found_one = true; + } else if (value.compare("ActivateSuspended") == 0) { + DNBLog("Setting ActivateSuspended key in options dictionary."); + [options setObject:@YES forKey: FBSOpenApplicationOptionKeyActivateSuspended]; + found_one = true; + } else { + DNBLogError("Unrecognized event type: %s. Ignoring.", value.c_str()); + option_error.SetErrorString("Unrecognized event data."); + } } + return found_one; } static NSMutableDictionary * @@ -3105,7 +3149,8 @@ pid_t MachProcess::PosixSpawnChildForPTraceDebugging( ::chdir(working_directory); err.SetError(::posix_spawnp(&pid, path, &file_actions, &attr, - (char *const *)argv, (char *const *)envp), + const_cast<char *const *>(argv), + const_cast<char *const *>(envp)), DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = " @@ -3117,8 +3162,9 @@ pid_t MachProcess::PosixSpawnChildForPTraceDebugging( if (working_directory) ::chdir(working_directory); - err.SetError(::posix_spawnp(&pid, path, NULL, &attr, (char *const *)argv, - (char *const *)envp), + err.SetError(::posix_spawnp(&pid, path, NULL, &attr, + const_cast<char *const *>(argv), + const_cast<char *const *>(envp)), DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = " @@ -3215,7 +3261,7 @@ pid_t MachProcess::ForkChildForPTraceDebugging(const char *path, ::sleep(1); // Turn this process into - ::execv(path, (char *const *)argv); + ::execv(path, const_cast<char *const *>(argv)); } // Exit with error code. Child process should have taken // over in above exec call and if the exec fails it will diff --git a/tools/debugserver/source/MacOSX/MachTask.mm b/tools/debugserver/source/MacOSX/MachTask.mm index bd7047ecdff7..1d177bd53cb7 100644 --- a/tools/debugserver/source/MacOSX/MachTask.mm +++ b/tools/debugserver/source/MacOSX/MachTask.mm @@ -188,7 +188,7 @@ nub_size_t MachTask::WriteMemory(nub_addr_t addr, nub_size_t size, (uint64_t)addr, (uint64_t)size, buf, (uint64_t)n); if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8)) { - DNBDataRef data((uint8_t *)buf, n, false); + DNBDataRef data((const uint8_t *)buf, n, false); data.Dump(0, static_cast<DNBDataRef::offset_t>(n), addr, DNBDataRef::TypeUInt8, 16); } diff --git a/tools/debugserver/source/MacOSX/MachThread.cpp b/tools/debugserver/source/MacOSX/MachThread.cpp index 5686e42e4a49..fc97825786a0 100644 --- a/tools/debugserver/source/MacOSX/MachThread.cpp +++ b/tools/debugserver/source/MacOSX/MachThread.cpp @@ -164,15 +164,15 @@ const char *MachThread::GetBasicInfoAsString() const { // size_t run_state_str_size = sizeof(run_state_str); // switch (basicInfo.run_state) // { - // case TH_STATE_RUNNING: strncpy(run_state_str, "running", + // case TH_STATE_RUNNING: strlcpy(run_state_str, "running", // run_state_str_size); break; - // case TH_STATE_STOPPED: strncpy(run_state_str, "stopped", + // case TH_STATE_STOPPED: strlcpy(run_state_str, "stopped", // run_state_str_size); break; - // case TH_STATE_WAITING: strncpy(run_state_str, "waiting", + // case TH_STATE_WAITING: strlcpy(run_state_str, "waiting", // run_state_str_size); break; - // case TH_STATE_UNINTERRUPTIBLE: strncpy(run_state_str, + // case TH_STATE_UNINTERRUPTIBLE: strlcpy(run_state_str, // "uninterruptible", run_state_str_size); break; - // case TH_STATE_HALTED: strncpy(run_state_str, "halted", + // case TH_STATE_HALTED: strlcpy(run_state_str, "halted", // run_state_str_size); break; // default: snprintf(run_state_str, // run_state_str_size, "%d", basicInfo.run_state); break; // ??? diff --git a/tools/debugserver/source/MacOSX/OsLogger.cpp b/tools/debugserver/source/MacOSX/OsLogger.cpp index 6cc04a8a7c83..40aeec73f0aa 100644 --- a/tools/debugserver/source/MacOSX/OsLogger.cpp +++ b/tools/debugserver/source/MacOSX/OsLogger.cpp @@ -56,9 +56,7 @@ void DarwinLogCallback(void *baton, uint32_t flags, const char *format, } } -DNBCallbackLog OsLogger::GetLogFunction() { - return _os_log_impl ? DarwinLogCallback : nullptr; -} +DNBCallbackLog OsLogger::GetLogFunction() { return DarwinLogCallback; } #else diff --git a/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp b/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp index 6cc5ae6c36c4..e0e8e27a1c2d 100644 --- a/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp +++ b/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp @@ -1086,7 +1086,7 @@ const DNBRegisterInfo DNBArchImplI386::g_fpu_registers_no_avx[] = { {e_regSetFPU, fpu_fsw, "fstat", NULL, Uint, Hex, FPU_SIZE_UINT(fsw), FPU_OFFSET(fsw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL}, - {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, FPU_SIZE_UINT(ftw), + {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */, FPU_OFFSET(ftw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL}, {e_regSetFPU, fpu_fop, "fop", NULL, Uint, Hex, FPU_SIZE_UINT(fop), @@ -1177,7 +1177,7 @@ const DNBRegisterInfo DNBArchImplI386::g_fpu_registers_avx[] = { {e_regSetFPU, fpu_fsw, "fstat", NULL, Uint, Hex, FPU_SIZE_UINT(fsw), AVX_OFFSET(fsw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL}, - {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, FPU_SIZE_UINT(ftw), + {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */, AVX_OFFSET(ftw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL}, {e_regSetFPU, fpu_fop, "fop", NULL, Uint, Hex, FPU_SIZE_UINT(fop), @@ -1414,7 +1414,7 @@ bool DNBArchImplI386::GetRegisterValue(uint32_t set, uint32_t reg, *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw)); return true; case fpu_ftw: - value->value.uint8 = m_state.context.fpu.no_avx.__fpu_ftw; + memcpy (&value->value.uint16, &m_state.context.fpu.no_avx.__fpu_ftw, 2); return true; case fpu_fop: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_fop; @@ -1607,7 +1607,7 @@ bool DNBArchImplI386::SetRegisterValue(uint32_t set, uint32_t reg, success = true; break; case fpu_ftw: - m_state.context.fpu.no_avx.__fpu_ftw = value->value.uint8; + memcpy (&m_state.context.fpu.no_avx.__fpu_ftw, &value->value.uint16, 2); success = true; break; case fpu_fop: @@ -1886,7 +1886,7 @@ nub_size_t DNBArchImplI386::SetRegisterContext(const void *buf, if (size > buf_len) size = buf_len; - uint8_t *p = (uint8_t *)buf; + const uint8_t *p = (const uint8_t *)buf; // Copy the GPR registers memcpy(&m_state.context.gpr, p, sizeof(GPR)); p += sizeof(GPR); @@ -1927,7 +1927,7 @@ nub_size_t DNBArchImplI386::SetRegisterContext(const void *buf, p += sizeof(EXC); // make sure we end up with exactly what we think we should have - size_t bytes_written = p - (uint8_t *)buf; + size_t bytes_written = p - (const uint8_t *)buf; UNUSED_IF_ASSERT_DISABLED(bytes_written); assert(bytes_written == size); kern_return_t kret; diff --git a/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp b/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp index 416b21f29958..86843fd97c06 100644 --- a/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp +++ b/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp @@ -1380,7 +1380,7 @@ const DNBRegisterInfo DNBArchImplX86_64::g_fpu_registers_no_avx[] = { FPU_OFFSET(fcw), -1U, -1U, -1U, -1U, NULL, NULL}, {e_regSetFPU, fpu_fsw, "fstat", NULL, Uint, Hex, FPU_SIZE_UINT(fsw), FPU_OFFSET(fsw), -1U, -1U, -1U, -1U, NULL, NULL}, - {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, FPU_SIZE_UINT(ftw), + {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */, FPU_OFFSET(ftw), -1U, -1U, -1U, -1U, NULL, NULL}, {e_regSetFPU, fpu_fop, "fop", NULL, Uint, Hex, FPU_SIZE_UINT(fop), FPU_OFFSET(fop), -1U, -1U, -1U, -1U, NULL, NULL}, @@ -1495,7 +1495,7 @@ const DNBRegisterInfo DNBArchImplX86_64::g_fpu_registers_avx[] = { AVX_OFFSET(fcw), -1U, -1U, -1U, -1U, NULL, NULL}, {e_regSetFPU, fpu_fsw, "fstat", NULL, Uint, Hex, FPU_SIZE_UINT(fsw), AVX_OFFSET(fsw), -1U, -1U, -1U, -1U, NULL, NULL}, - {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, FPU_SIZE_UINT(ftw), + {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */, AVX_OFFSET(ftw), -1U, -1U, -1U, -1U, NULL, NULL}, {e_regSetFPU, fpu_fop, "fop", NULL, Uint, Hex, FPU_SIZE_UINT(fop), AVX_OFFSET(fop), -1U, -1U, -1U, -1U, NULL, NULL}, @@ -1776,7 +1776,7 @@ bool DNBArchImplX86_64::GetRegisterValue(uint32_t set, uint32_t reg, *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw)); return true; case fpu_ftw: - value->value.uint8 = m_state.context.fpu.no_avx.__fpu_ftw; + memcpy (&value->value.uint16, &m_state.context.fpu.no_avx.__fpu_ftw, 2); return true; case fpu_fop: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_fop; @@ -1932,7 +1932,7 @@ bool DNBArchImplX86_64::SetRegisterValue(uint32_t set, uint32_t reg, success = true; break; case fpu_ftw: - m_state.context.fpu.no_avx.__fpu_ftw = value->value.uint8; + memcpy (&m_state.context.fpu.no_avx.__fpu_ftw, &value->value.uint8, 2); success = true; break; case fpu_fop: @@ -2164,7 +2164,7 @@ nub_size_t DNBArchImplX86_64::SetRegisterContext(const void *buf, if (size > buf_len) size = static_cast<uint32_t>(buf_len); - uint8_t *p = (uint8_t *)buf; + const uint8_t *p = (const uint8_t *)buf; // Copy the GPR registers memcpy(&m_state.context.gpr, p, sizeof(GPR)); p += sizeof(GPR); @@ -2205,7 +2205,7 @@ nub_size_t DNBArchImplX86_64::SetRegisterContext(const void *buf, p += sizeof(EXC); // make sure we end up with exactly what we think we should have - size_t bytes_written = p - (uint8_t *)buf; + size_t bytes_written = p - (const uint8_t *)buf; UNUSED_IF_ASSERT_DISABLED(bytes_written); assert(bytes_written == size); diff --git a/tools/debugserver/source/RNBRemote.cpp b/tools/debugserver/source/RNBRemote.cpp index cf6b4b626914..0a2eef3feacd 100644 --- a/tools/debugserver/source/RNBRemote.cpp +++ b/tools/debugserver/source/RNBRemote.cpp @@ -710,29 +710,29 @@ std::string RNBRemote::CompressString(const std::string &orig) { size_t compressed_size = 0; #if defined(HAVE_LIBCOMPRESSION) - if (compression_decode_buffer && - compression_type == compression_types::lz4) { + if (compression_type == compression_types::lz4) { compressed_size = compression_encode_buffer( - encoded_data.data(), encoded_data_buf_size, (uint8_t *)orig.c_str(), - orig.size(), nullptr, COMPRESSION_LZ4_RAW); + encoded_data.data(), encoded_data_buf_size, + (const uint8_t *)orig.c_str(), orig.size(), nullptr, + COMPRESSION_LZ4_RAW); } - if (compression_decode_buffer && - compression_type == compression_types::zlib_deflate) { + if (compression_type == compression_types::zlib_deflate) { compressed_size = compression_encode_buffer( - encoded_data.data(), encoded_data_buf_size, (uint8_t *)orig.c_str(), - orig.size(), nullptr, COMPRESSION_ZLIB); + encoded_data.data(), encoded_data_buf_size, + (const uint8_t *)orig.c_str(), orig.size(), nullptr, + COMPRESSION_ZLIB); } - if (compression_decode_buffer && - compression_type == compression_types::lzma) { + if (compression_type == compression_types::lzma) { compressed_size = compression_encode_buffer( - encoded_data.data(), encoded_data_buf_size, (uint8_t *)orig.c_str(), - orig.size(), nullptr, COMPRESSION_LZMA); + encoded_data.data(), encoded_data_buf_size, + (const uint8_t *)orig.c_str(), orig.size(), nullptr, + COMPRESSION_LZMA); } - if (compression_decode_buffer && - compression_type == compression_types::lzfse) { + if (compression_type == compression_types::lzfse) { compressed_size = compression_encode_buffer( - encoded_data.data(), encoded_data_buf_size, (uint8_t *)orig.c_str(), - orig.size(), nullptr, COMPRESSION_LZFSE); + encoded_data.data(), encoded_data_buf_size, + (const uint8_t *)orig.c_str(), orig.size(), nullptr, + COMPRESSION_LZFSE); } #endif @@ -2247,7 +2247,7 @@ rnb_err_t set_logging(const char *p) { continue; } char *fn = (char *) alloca (c - p + 1); - strncpy (fn, p, c - p); + strlcpy (fn, p, c - p); fn[c - p] = '\0'; // A file name of "asl" is special and is another way to indicate @@ -2862,7 +2862,7 @@ rnb_err_t RNBRemote::SendStopReplyPacketForThread(nub_thread_t tid) { else { // the thread name contains special chars, send as hex bytes ostrm << std::hex << "hexname:"; - uint8_t *u_thread_name = (uint8_t *)thread_name; + const uint8_t *u_thread_name = (const uint8_t *)thread_name; for (size_t i = 0; i < thread_name_len; i++) ostrm << RAWHEX8(u_thread_name[i]); ostrm << ';'; @@ -3049,7 +3049,7 @@ rnb_err_t RNBRemote::HandlePacket_last_signal(const char *unused) { // If we have an empty exit packet, lets fill one in to be safe. if (!pid_exited_packet[0]) { - strncpy(pid_exited_packet, "W00", sizeof(pid_exited_packet) - 1); + strlcpy(pid_exited_packet, "W00", sizeof(pid_exited_packet) - 1); pid_exited_packet[sizeof(pid_exited_packet) - 1] = '\0'; } @@ -3621,7 +3621,7 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) { #if defined(HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is // available - if (enable_compression && compression_decode_buffer != NULL) { + if (enable_compression) { strcat(buf, ";SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;" "DefaultCompressionMinSize="); char numbuf[16]; @@ -3667,7 +3667,7 @@ rnb_err_t RNBRemote::HandlePacket_v(const char *p) { return RNBRemote::HandlePacket_s("s"); } else if (strstr(p, "vCont") == p) { DNBThreadResumeActions thread_actions; - char *c = (char *)(p += strlen("vCont")); + char *c = const_cast<char *>(p += strlen("vCont")); char *c_end = c + strlen(c); if (*c == '?') return SendPacket("vCont;c;C;s;S"); @@ -4307,24 +4307,22 @@ rnb_err_t RNBRemote::HandlePacket_QEnableCompression(const char *p) { } #if defined(HAVE_LIBCOMPRESSION) - if (compression_decode_buffer != NULL) { - if (strstr(p, "type:zlib-deflate;") != nullptr) { - EnableCompressionNextSendPacket(compression_types::zlib_deflate); - m_compression_minsize = new_compression_minsize; - return SendPacket("OK"); - } else if (strstr(p, "type:lz4;") != nullptr) { - EnableCompressionNextSendPacket(compression_types::lz4); - m_compression_minsize = new_compression_minsize; - return SendPacket("OK"); - } else if (strstr(p, "type:lzma;") != nullptr) { - EnableCompressionNextSendPacket(compression_types::lzma); - m_compression_minsize = new_compression_minsize; - return SendPacket("OK"); - } else if (strstr(p, "type:lzfse;") != nullptr) { - EnableCompressionNextSendPacket(compression_types::lzfse); - m_compression_minsize = new_compression_minsize; - return SendPacket("OK"); - } + if (strstr(p, "type:zlib-deflate;") != nullptr) { + EnableCompressionNextSendPacket(compression_types::zlib_deflate); + m_compression_minsize = new_compression_minsize; + return SendPacket("OK"); + } else if (strstr(p, "type:lz4;") != nullptr) { + EnableCompressionNextSendPacket(compression_types::lz4); + m_compression_minsize = new_compression_minsize; + return SendPacket("OK"); + } else if (strstr(p, "type:lzma;") != nullptr) { + EnableCompressionNextSendPacket(compression_types::lzma); + m_compression_minsize = new_compression_minsize; + return SendPacket("OK"); + } else if (strstr(p, "type:lzfse;") != nullptr) { + EnableCompressionNextSendPacket(compression_types::lzfse); + m_compression_minsize = new_compression_minsize; + return SendPacket("OK"); } #endif diff --git a/tools/debugserver/source/RNBServices.cpp b/tools/debugserver/source/RNBServices.cpp index d0b7e099d238..9f90f349f3af 100644 --- a/tools/debugserver/source/RNBServices.cpp +++ b/tools/debugserver/source/RNBServices.cpp @@ -220,7 +220,7 @@ int ListApplications(std::string &plist, bool opt_runningApps, CFIndex size = ::CFDataGetLength(plistData.get()); const UInt8 *bytes = ::CFDataGetBytePtr(plistData.get()); if (bytes != NULL && size > 0) { - plist.assign((char *)bytes, size); + plist.assign((const char *)bytes, size); return 0; // Success } else { DNBLogError("empty application property list."); diff --git a/tools/debugserver/source/RNBSocket.cpp b/tools/debugserver/source/RNBSocket.cpp index f1db5e419ff2..88136c210acc 100644 --- a/tools/debugserver/source/RNBSocket.cpp +++ b/tools/debugserver/source/RNBSocket.cpp @@ -373,9 +373,10 @@ rnb_err_t RNBSocket::Write(const void *buffer, size_t length) { DNBLogThreadedIf( LOG_RNB_PACKETS, "putpkt: %*s", (int)length, - (char *) + (const char *) buffer); // All data is string based in debugserver, so this is safe - DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length, (char *)buffer); + DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length, + (const char *)buffer); return rnb_success; } diff --git a/tools/debugserver/source/debugserver.cpp b/tools/debugserver/source/debugserver.cpp index 318e72e971a5..8291f8d32b51 100644 --- a/tools/debugserver/source/debugserver.cpp +++ b/tools/debugserver/source/debugserver.cpp @@ -232,7 +232,7 @@ RNBRunLoopMode RNBRunLoopLaunchInferior(RNBRemote *remote, // were given and hope for the best if (!DNBResolveExecutablePath(inferior_argv[0], resolved_path, sizeof(resolved_path))) - ::strncpy(resolved_path, inferior_argv[0], sizeof(resolved_path)); + ::strlcpy(resolved_path, inferior_argv[0], sizeof(resolved_path)); char launch_err_str[PATH_MAX]; launch_err_str[0] = '\0'; @@ -676,7 +676,7 @@ static void PortWasBoundCallbackUnixSocket(const void *baton, in_port_t port) { } saddr_un.sun_family = AF_UNIX; - ::strncpy(saddr_un.sun_path, unix_socket_name, + ::strlcpy(saddr_un.sun_path, unix_socket_name, sizeof(saddr_un.sun_path) - 1); saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; saddr_un.sun_len = SUN_LEN(&saddr_un); @@ -1366,7 +1366,7 @@ int main(int argc, char *argv[]) { DNBLogDebug("host = '%s' port = %i", host.c_str(), port); } else if (argv[0][0] == '/') { port = INT32_MAX; - strncpy(str, argv[0], sizeof(str)); + strlcpy(str, argv[0], sizeof(str)); } else { show_usage_and_exit(2); } diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 6384d5a92c2c..1fac2e5d1687 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -1,5 +1,3 @@ -include(${LLDB_PROJECT_ROOT}/cmake/LLDBDependencies.cmake) - if ((CMAKE_SYSTEM_NAME MATCHES "Windows") OR (CMAKE_SYSTEM_NAME MATCHES "NetBSD" )) # These targets do not have getopt support, so they rely on the one provided by diff --git a/tools/driver/Driver.cpp b/tools/driver/Driver.cpp index 102ba775da91..7e1dec7a34fa 100644 --- a/tools/driver/Driver.cpp +++ b/tools/driver/Driver.cpp @@ -9,6 +9,7 @@ #include "Driver.h" +#include <atomic> #include <csignal> #include <fcntl.h> #include <limits.h> @@ -1177,17 +1178,16 @@ void sigwinch_handler(int signo) { } void sigint_handler(int signo) { - static bool g_interrupt_sent = false; + static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT; if (g_driver) { - if (!g_interrupt_sent) { - g_interrupt_sent = true; + if (!g_interrupt_sent.test_and_set()) { g_driver->GetDebugger().DispatchInputInterrupt(); - g_interrupt_sent = false; + g_interrupt_sent.clear(); return; } } - exit(signo); + _exit(signo); } void sigtstp_handler(int signo) { diff --git a/tools/intel-features/CMakeLists.txt b/tools/intel-features/CMakeLists.txt new file mode 100644 index 000000000000..b5316540fdf3 --- /dev/null +++ b/tools/intel-features/CMakeLists.txt @@ -0,0 +1,67 @@ +# Flags to control each individual feature +option(LLDB_BUILD_INTEL_MPX "Enable Building of Intel(R) Memory Protection Extensions" ON) +option(LLDB_BUILD_INTEL_PT "Enable Building of Intel(R) Processor Trace Tool" OFF) + +# Return if all features are OFF +if (NOT LLDB_BUILD_INTEL_MPX AND NOT LLDB_BUILD_INTEL_PT) + return() +endif() + +LIST (APPEND FEATURE_LIBS "") + +# Add feature specific subdirectories based on flags +if (LLDB_BUILD_INTEL_MPX AND CMAKE_SYSTEM_NAME MATCHES "Linux") + add_subdirectory(intel-mpx) + LIST (APPEND FEATURE_LIBS ${FEATURE_LIBS} lldbIntelMPX) + SET (CLI_WRAPPER_PREPROCESSORS "${CLI_WRAPPER_PREPROCESSORS} -DBUILD_INTEL_MPX") +endif() + +if (LLDB_BUILD_INTEL_PT) + add_subdirectory(intel-pt) + LIST (APPEND FEATURE_LIBS ${FEATURE_LIBS} lldbIntelPT) + SET (CLI_WRAPPER_PREPROCESSORS "${CLI_WRAPPER_PREPROCESSORS} -DBUILD_INTEL_PT") +endif() + +# Add python wrapper if python not disabled +if (NOT LLDB_DISABLE_PYTHON AND LLDB_BUILD_INTEL_PT) + set(LLDB_INTEL_FEATURES_PYTHON_WRAP + ${LLDB_BINARY_DIR}/tools/intel-features/scripts/IntelFeaturesPythonWrap.cpp) + set_source_files_properties(${LLDB_INTEL_FEATURES_PYTHON_WRAP} + PROPERTIES GENERATED 1) + + if (CLANG_CL) + set_source_files_properties(${LLDB_INTEL_FEATURES_PYTHON_WRAP} + PROPERTIES COMPILE_FLAGS -Wno-unused-function) + endif() + + if (LLVM_COMPILER_IS_GCC_COMPATIBLE AND + NOT "${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") + set_property(SOURCE ${LLDB_INTEL_FEATURES_PYTHON_WRAP} + APPEND_STRING PROPERTY COMPILE_FLAGS + " -Wno-sequence-point -Wno-cast-qual") + endif () + add_subdirectory(scripts) +endif() + +if (NOT CLI_WRAPPER_PREPROCESSORS) + return() +endif() + +set_source_files_properties(cli-wrapper.cpp PROPERTIES + COMPILE_FLAGS ${CLI_WRAPPER_PREPROCESSORS}) + +add_lldb_library(lldbIntelFeatures SHARED + cli-wrapper.cpp + ${LLDB_INTEL_FEATURES_PYTHON_WRAP} + + LINK_LIBS + ${FEATURE_LIBS} + ) + +# Add link dependencies for python wrapper +if (NOT LLDB_DISABLE_PYTHON AND LLDB_BUILD_INTEL_PT) + add_dependencies(lldbIntelFeatures intel-features-swig_wrapper) +endif() + +install(TARGETS lldbIntelFeatures + LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}) diff --git a/tools/intel-features/README.txt b/tools/intel-features/README.txt new file mode 100644 index 000000000000..d46bf1392dcf --- /dev/null +++ b/tools/intel-features/README.txt @@ -0,0 +1,73 @@ +**************************************************************************** +* README * +* * +* This file provides all the information regarding new CLI commands that * +* enable using various hardware features of Intel(R) architecture based * +* processors from LLDB's CLI. * +**************************************************************************** + + +============ +Introduction +============ +A shared library has been developed to use various hardware features of +Intel(R) architecture based processors through LLDB's command line. The library +currently comprises of hardware features namely Intel(R) Processor Trace and +Intel(R) Memory Protection Extensions. + + +============ +Details +============ +A C++ based cli wrapper (cli-wrapper.cpp) has been developed here that +agglomerates all cli commands for various hardware features. This wrapper is +build to generate a shared library (lldbIntelFeatures) to provide all these +commands. + +For each hardware feature, separate cli commands have been developed that are +provided by wrappers (cli-wrapper-pt.cpp and cli-wrapper-mpxtable.cpp) residing +in feature specific folders ("intel-pt" and "intel-mpx" respectively). + +For details regarding cli commands of each feature, please refer to these +feature specific wrappers. + + + +============ +How to Build +============ +The shared library (lldbIntelFeatures) has a cmake based build and can be built +while building LLDB with cmake. "cli-wrapper.cpp" file is compiled along with all +the feature specific source files (residing in feature specific folders). + +Furthermore, flexibility is provided to the user to include/exclude a particular +feature while building lldbIntelFeatures library. This is done by flags described +below: + + - LLDB_BUILD_INTEL_PT - The flag enables building of Intel(R) Processor Trace + feature (inside intel-pt folder). This flag defaults to "OFF" meaning the + feature is excluded while building lldbIntelFeatures library. Set it to "ON" + in order to include it. + + - LLDB_BUILD_INTEL_MPX - Enables building Intel(R) Memory Protection Extensions + feature (inside intel-mpx folder). This flag defaults to "ON" meaning + the feature is excluded while building lldbIntelFeatures library. + +Please refer to README files in feature specific folders to know about additional +flags that need to be set in order to build that feature successfully. + + +============ +How to Use +============ +All CLI commands provided by this shared library can be used through the LLDB's +CLI by executing "plugin load <shared_lib_name>" on LLDB CLI. shared_lib_name here +is lldbIntelFeatures + + + +============ +Description +============ +Please refer to README_CLI file of each feature to know about details of CLI +commands. diff --git a/tools/intel-features/cli-wrapper.cpp b/tools/intel-features/cli-wrapper.cpp new file mode 100644 index 000000000000..8b1471509663 --- /dev/null +++ b/tools/intel-features/cli-wrapper.cpp @@ -0,0 +1,43 @@ +//===-- cli-wrapper.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// CLI Wrapper for hardware features of Intel(R) architecture based processors +// to enable them to be used through LLDB's CLI. For details, please refer to +// cli wrappers of each individual feature, residing in their respective +// folders. +// +// Compile this into a shared lib and load by placing at appropriate locations +// on disk or by using "plugin load" command at the LLDB command line. +// +//===----------------------------------------------------------------------===// + +#ifdef BUILD_INTEL_MPX +#include "intel-mpx/cli-wrapper-mpxtable.h" +#endif + +#ifdef BUILD_INTEL_PT +#include "intel-pt/cli-wrapper-pt.h" +#endif + +#include "lldb/API/SBDebugger.h" + +namespace lldb { +bool PluginInitialize(lldb::SBDebugger debugger); +} + +bool lldb::PluginInitialize(lldb::SBDebugger debugger) { + +#ifdef BUILD_INTEL_PT + PTPluginInitialize(debugger); +#endif + +#ifdef BUILD_INTEL_MPX + MPXPluginInitialize(debugger); +#endif + + return true; +} diff --git a/tools/intel-features/intel-mpx/CMakeLists.txt b/tools/intel-features/intel-mpx/CMakeLists.txt new file mode 100644 index 000000000000..319f712b82bd --- /dev/null +++ b/tools/intel-features/intel-mpx/CMakeLists.txt @@ -0,0 +1,9 @@ +add_lldb_library(lldbIntelMPX + cli-wrapper-mpxtable.cpp + + LINK_LIBS + liblldb + + LINK_COMPONENTS + Support + ) diff --git a/tools/intel-mpx/IntelMPXTablePlugin.cpp b/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp index 0f86ce661def..e654f076fb3a 100644 --- a/tools/intel-mpx/IntelMPXTablePlugin.cpp +++ b/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp @@ -1,4 +1,5 @@ -//===-- IntelMPXTablePlugin.cpp----------------------------------*- C++ -*-===// +//===-- cli-wrapper-mpxtable.cpp----------------------------------*- C++ +//-*-===// // // The LLVM Compiler Infrastructure // @@ -12,6 +13,7 @@ #include <string> // Project includes +#include "cli-wrapper-mpxtable.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBMemoryRegionInfo.h" @@ -21,10 +23,6 @@ #include "llvm/ADT/Triple.h" -namespace lldb { -bool PluginInitialize(lldb::SBDebugger debugger); -} - static bool GetPtr(char *cptr, uint64_t &ptr, lldb::SBFrame &frame, lldb::SBCommandReturnObject &result) { if (!cptr) { @@ -285,8 +283,8 @@ static bool GetInitInfo(lldb::SBDebugger debugger, lldb::SBTarget &target, lldb::SBValue bndcfgu_val = frame.FindRegister("bndcfgu"); if (!bndcfgu_val.IsValid()) { - result.SetError( - "Cannot access register BNDCFGU. Does the target support MPX?"); + result.SetError("Cannot access register BNDCFGU. Does the target support " + "Intel(R) Memory Protection Extensions (Intel(R) MPX)?"); result.SetStatus(lldb::eReturnStatusFailed); return false; } @@ -409,17 +407,17 @@ public: } }; -bool lldb::PluginInitialize(lldb::SBDebugger debugger) { +bool MPXPluginInitialize(lldb::SBDebugger &debugger) { lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); lldb::SBCommand mpxTable = interpreter.AddMultiwordCommand( - "mpx-table", "A utility to access the MPX table entries."); + "mpx-table", "A utility to access the Intel(R) MPX table entries."); - const char *mpx_show_help = "Show the MPX table entry of a pointer.\n" - "mpx-table show <pointer>"; + const char *mpx_show_help = "Show the Intel(R) MPX table entry of a pointer." + "\nmpx-table show <pointer>"; mpxTable.AddCommand("show", new MPXTableShow(), mpx_show_help); const char *mpx_set_help = - "Set the MPX table entry of a pointer.\n" + "Set the Intel(R) MPX table entry of a pointer.\n" "mpx-table set <pointer> <lower bound> <upper bound>"; mpxTable.AddCommand("set", new MPXTableSet(), mpx_set_help); diff --git a/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.h b/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.h new file mode 100644 index 000000000000..00028a93fb43 --- /dev/null +++ b/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.h @@ -0,0 +1,12 @@ +//===-- cli-wrapper-mpxtable.h----------------------------------*- 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/SBDebugger.h" + +bool MPXPluginInitialize(lldb::SBDebugger &debugger); diff --git a/tools/intel-mpx/test/Makefile b/tools/intel-features/intel-mpx/test/Makefile index b18044407a70..b18044407a70 100644 --- a/tools/intel-mpx/test/Makefile +++ b/tools/intel-features/intel-mpx/test/Makefile diff --git a/tools/intel-mpx/test/README.txt b/tools/intel-features/intel-mpx/test/README.txt index 314e78d3e0eb..6797eff30a79 100644 --- a/tools/intel-mpx/test/README.txt +++ b/tools/intel-features/intel-mpx/test/README.txt @@ -2,5 +2,5 @@ In order to run this test, create the following directory: packages/Python/lldbsuite/test/functionalities/plugins/commands/mpxtablecmd -and copy into it the contents of this direcotry. +and copy into it the contents of this directory. diff --git a/tools/intel-mpx/test/TestMPXTable.py b/tools/intel-features/intel-mpx/test/TestMPXTable.py index f2468abd59a1..f571252e26f7 100644 --- a/tools/intel-mpx/test/TestMPXTable.py +++ b/tools/intel-features/intel-mpx/test/TestMPXTable.py @@ -24,16 +24,17 @@ class TestMPXTable(TestBase): @skipIf(compiler="clang") @skipIf(oslist=no_match(['linux'])) @skipIf(archs=no_match(['i386', 'x86_64'])) - @skipIf(compiler="gcc", compiler_version=["<", "5"]) #GCC version >= 5 supports Intel(R) MPX. + @skipIf(compiler="gcc", compiler_version=["<", "5"]) #GCC version >= 5 supports + #Intel(R) Memory Protection Extensions (Intel(R) MPX). def test_show_command(self): """Test 'mpx-table show' command""" self.build() lldb_exec_dir = os.environ["LLDB_IMPLIB_DIR"] lldb_lib_dir = os.path.join(lldb_exec_dir, os.pardir, "lib") - plugin_file = os.path.join(lldb_lib_dir, "liblldb-intel-mpxtable.so") + plugin_file = os.path.join(lldb_lib_dir, "liblldbIntelFeatures.so") if not os.path.isfile(plugin_file): - self.skipTest("Intel(R) mpx-table plugin missing.") + self.skipTest("features plugin missing.") plugin_command = " " seq = ("plugin", "load", plugin_file) plugin_command = plugin_command.join(seq) @@ -123,9 +124,9 @@ class TestMPXTable(TestBase): lldb_exec_dir = os.environ["LLDB_IMPLIB_DIR"] lldb_lib_dir = os.path.join(lldb_exec_dir, os.pardir, "lib") - plugin_file = os.path.join(lldb_lib_dir, "liblldb-intel-mpxtable.so") + plugin_file = os.path.join(lldb_lib_dir, "liblldbIntelFeatures.so") if not os.path.isfile(plugin_file): - self.skipTest("Intel(R) mpx-table plugin missing.") + self.skipTest("features plugin missing.") plugin_command = " " seq = ("plugin", "load", plugin_file) plugin_command = plugin_command.join(seq) diff --git a/tools/intel-mpx/test/main.cpp b/tools/intel-features/intel-mpx/test/main.cpp index 214332338d87..110befb1e250 100644 --- a/tools/intel-mpx/test/main.cpp +++ b/tools/intel-features/intel-mpx/test/main.cpp @@ -32,7 +32,8 @@ void func(int *ptr) { int main(int argc, char const *argv[]) { - // This call returns 0 only if the CPU and the kernel support Intel(R) MPX. + // This call returns 0 only if the CPU and the kernel support + // Intel(R) Memory Protection Extensions (Intel(R) MPX). if (prctl(PR_MPX_ENABLE_MANAGEMENT, 0, 0, 0, 0) != 0) return -1; diff --git a/tools/intel-features/intel-pt/CMakeLists.txt b/tools/intel-features/intel-pt/CMakeLists.txt new file mode 100644 index 000000000000..9750ed83ce2d --- /dev/null +++ b/tools/intel-features/intel-pt/CMakeLists.txt @@ -0,0 +1,31 @@ +if (NOT LIBIPT_INCLUDE_PATH) + message (FATAL_ERROR "libipt include path not provided") +endif() + +if (NOT EXISTS "${LIBIPT_INCLUDE_PATH}") + message (FATAL_ERROR "invalid libipt include path provided") +endif() +include_directories(${LIBIPT_INCLUDE_PATH}) + +if (NOT LIBIPT_LIBRARY_PATH) + find_library(LIBIPT_LIBRARY ipt) +else() + if (NOT EXISTS "${LIBIPT_LIBRARY_PATH}") + message (FATAL_ERROR "invalid libipt library path provided") + endif() + find_library(LIBIPT_LIBRARY ipt PATHS ${LIBIPT_LIBRARY_PATH}) +endif() + +if (NOT LIBIPT_LIBRARY) + message (FATAL_ERROR "libipt library not found") +endif() + +add_lldb_library(lldbIntelPT + PTDecoder.cpp + Decoder.cpp + cli-wrapper-pt.cpp + + LINK_LIBS + ${LIBIPT_LIBRARY} + liblldb + ) diff --git a/tools/intel-features/intel-pt/Decoder.cpp b/tools/intel-features/intel-pt/Decoder.cpp new file mode 100644 index 000000000000..0c385adc811c --- /dev/null +++ b/tools/intel-features/intel-pt/Decoder.cpp @@ -0,0 +1,904 @@ +//===-- Decoder.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Project includes +#include "Decoder.h" + +// C/C++ Includes +#include <cinttypes> +#include <cstring> + +// Other libraries and framework includes +#include "lldb/API/SBModule.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBThread.h" + +using namespace ptdecoder_private; + +// This function removes entries of all the processes/threads which were once +// registered in the class but are not alive anymore because they died or +// finished executing. +void Decoder::RemoveDeadProcessesAndThreads(lldb::SBProcess &sbprocess) { + lldb::SBTarget sbtarget = sbprocess.GetTarget(); + lldb::SBDebugger sbdebugger = sbtarget.GetDebugger(); + uint32_t num_targets = sbdebugger.GetNumTargets(); + + auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.begin(); + while (itr_process != m_mapProcessUID_mapThreadID_TraceInfo.end()) { + bool process_found = false; + lldb::SBTarget target; + lldb::SBProcess process; + for (uint32_t i = 0; i < num_targets; i++) { + target = sbdebugger.GetTargetAtIndex(i); + process = target.GetProcess(); + if (process.GetUniqueID() == itr_process->first) { + process_found = true; + break; + } + } + + // Remove the process's entry if it was not found in SBDebugger + if (!process_found) { + itr_process = m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process); + continue; + } + + // If the state of the process is exited or detached then remove process's + // entry. If not then remove entry for all those registered threads of this + // process that are not alive anymore. + lldb::StateType state = process.GetState(); + if ((state == lldb::StateType::eStateDetached) || + (state == lldb::StateType::eStateExited)) + itr_process = m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process); + else { + auto itr_thread = itr_process->second.begin(); + while (itr_thread != itr_process->second.end()) { + if (itr_thread->first == LLDB_INVALID_THREAD_ID) { + ++itr_thread; + continue; + } + + lldb::SBThread thread = process.GetThreadByID(itr_thread->first); + if (!thread.IsValid()) + itr_thread = itr_process->second.erase(itr_thread); + else + ++itr_thread; + } + ++itr_process; + } + } +} + +void Decoder::StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror) { + sberror.Clear(); + CheckDebuggerID(sbprocess, sberror); + if (!sberror.Success()) + return; + + std::lock_guard<std::mutex> guard( + m_mapProcessUID_mapThreadID_TraceInfo_mutex); + RemoveDeadProcessesAndThreads(sbprocess); + + if (sbtraceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) { + sberror.SetErrorStringWithFormat("SBTraceOptions::TraceType not set to " + "eTraceTypeProcessorTrace; ProcessID = " + "%" PRIu64, + sbprocess.GetProcessID()); + return; + } + lldb::SBStructuredData sbstructdata = sbtraceoptions.getTraceParams(sberror); + if (!sberror.Success()) + return; + + const char *trace_tech_key = "trace-tech"; + std::string trace_tech_value("intel-pt"); + lldb::SBStructuredData value = sbstructdata.GetValueForKey(trace_tech_key); + if (!value.IsValid()) { + sberror.SetErrorStringWithFormat( + "key \"%s\" not set in custom trace parameters", trace_tech_key); + return; + } + + char string_value[9]; + size_t bytes_written = value.GetStringValue( + string_value, sizeof(string_value) / sizeof(*string_value)); + if (!bytes_written || + (bytes_written > (sizeof(string_value) / sizeof(*string_value)))) { + sberror.SetErrorStringWithFormat( + "key \"%s\" not set in custom trace parameters", trace_tech_key); + return; + } + + std::size_t pos = + trace_tech_value.find((const char *)string_value, 0, bytes_written); + if ((pos == std::string::npos)) { + sberror.SetErrorStringWithFormat( + "key \"%s\" not set to \"%s\" in custom trace parameters", + trace_tech_key, trace_tech_value.c_str()); + return; + } + + // Start Tracing + lldb::SBError error; + uint32_t unique_id = sbprocess.GetUniqueID(); + lldb::tid_t tid = sbtraceoptions.getThreadID(); + lldb::SBTrace trace = sbprocess.StartTrace(sbtraceoptions, error); + if (!error.Success()) { + if (tid == LLDB_INVALID_THREAD_ID) + sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64, + error.GetCString(), + sbprocess.GetProcessID()); + else + sberror.SetErrorStringWithFormat( + "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64, + error.GetCString(), tid, sbprocess.GetProcessID()); + return; + } + + MapThreadID_TraceInfo &mapThreadID_TraceInfo = + m_mapProcessUID_mapThreadID_TraceInfo[unique_id]; + ThreadTraceInfo &trace_info = mapThreadID_TraceInfo[tid]; + trace_info.SetUniqueTraceInstance(trace); + trace_info.SetStopID(sbprocess.GetStopID()); +} + +void Decoder::StopProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBError &sberror, lldb::tid_t tid) { + sberror.Clear(); + CheckDebuggerID(sbprocess, sberror); + if (!sberror.Success()) { + return; + } + + std::lock_guard<std::mutex> guard( + m_mapProcessUID_mapThreadID_TraceInfo_mutex); + RemoveDeadProcessesAndThreads(sbprocess); + + uint32_t unique_id = sbprocess.GetUniqueID(); + auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id); + if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) { + sberror.SetErrorStringWithFormat( + "tracing not active for this process; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + lldb::SBError error; + if (tid == LLDB_INVALID_THREAD_ID) { + // This implies to stop tracing on the whole process + lldb::user_id_t id_to_be_ignored = LLDB_INVALID_UID; + auto itr_thread = itr_process->second.begin(); + while (itr_thread != itr_process->second.end()) { + // In the case when user started trace on the entire process and then + // registered newly spawned threads of this process in the class later, + // these newly spawned threads will have same trace id. If we stopped + // trace on the entire process then tracing stops automatically for these + // newly spawned registered threads. Stopping trace on them again will + // return error and therefore we need to skip stopping trace on them + // again. + lldb::SBTrace &trace = itr_thread->second.GetUniqueTraceInstance(); + lldb::user_id_t lldb_pt_user_id = trace.GetTraceUID(); + if (lldb_pt_user_id != id_to_be_ignored) { + trace.StopTrace(error, itr_thread->first); + if (!error.Success()) { + std::string error_string(error.GetCString()); + if ((error_string.find("tracing not active for this process") == + std::string::npos) && + (error_string.find("tracing not active for this thread") == + std::string::npos)) { + sberror.SetErrorStringWithFormat( + "%s; thread id=%" PRIu64 ", ProcessID = %" PRIu64, + error_string.c_str(), itr_thread->first, + sbprocess.GetProcessID()); + return; + } + } + + if (itr_thread->first == LLDB_INVALID_THREAD_ID) + id_to_be_ignored = lldb_pt_user_id; + } + itr_thread = itr_process->second.erase(itr_thread); + } + m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process); + } else { + // This implies to stop tracing on a single thread. + // if 'tid' is registered in the class then get the trace id and stop trace + // on it. If it is not then check if tracing was ever started on the entire + // process (because there is a possibility that trace is still running for + // 'tid' but it was not registered in the class because user had started + // trace on the whole process and 'tid' spawned later). In that case, get + // the trace id of the process trace instance and stop trace on this thread. + // If tracing was never started on the entire process then return error + // because there is no way tracing is active on 'tid'. + MapThreadID_TraceInfo &mapThreadID_TraceInfo = itr_process->second; + lldb::SBTrace trace; + auto itr = mapThreadID_TraceInfo.find(tid); + if (itr != mapThreadID_TraceInfo.end()) { + trace = itr->second.GetUniqueTraceInstance(); + } else { + auto itr = mapThreadID_TraceInfo.find(LLDB_INVALID_THREAD_ID); + if (itr != mapThreadID_TraceInfo.end()) { + trace = itr->second.GetUniqueTraceInstance(); + } else { + sberror.SetErrorStringWithFormat( + "tracing not active for this thread; thread id=%" PRIu64 + ", ProcessID = %" PRIu64, + tid, sbprocess.GetProcessID()); + return; + } + } + + // Stop Tracing + trace.StopTrace(error, tid); + if (!error.Success()) { + std::string error_string(error.GetCString()); + sberror.SetErrorStringWithFormat( + "%s; thread id=%" PRIu64 ", ProcessID = %" PRIu64, + error_string.c_str(), tid, sbprocess.GetProcessID()); + if (error_string.find("tracing not active") == std::string::npos) + return; + } + // Delete the entry of 'tid' from this class (if any) + mapThreadID_TraceInfo.erase(tid); + } +} + +void Decoder::ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess, + lldb::tid_t tid, lldb::SBError &sberror, + ThreadTraceInfo &threadTraceInfo) { + // Allocate trace data buffer and parse cpu info for 'tid' if it is registered + // for the first time in class + lldb::SBTrace &trace = threadTraceInfo.GetUniqueTraceInstance(); + Buffer &pt_buffer = threadTraceInfo.GetPTBuffer(); + lldb::SBError error; + if (pt_buffer.size() == 0) { + lldb::SBTraceOptions traceoptions; + traceoptions.setThreadID(tid); + trace.GetTraceConfig(traceoptions, error); + if (!error.Success()) { + sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64, + error.GetCString(), + sbprocess.GetProcessID()); + return; + } + if (traceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) { + sberror.SetErrorStringWithFormat("invalid TraceType received from LLDB " + "for this thread; thread id=%" PRIu64 + ", ProcessID = %" PRIu64, + tid, sbprocess.GetProcessID()); + return; + } + + threadTraceInfo.AllocatePTBuffer(traceoptions.getTraceBufferSize()); + lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror); + if (!sberror.Success()) + return; + CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo(); + ParseCPUInfo(pt_cpu, sbstructdata, sberror); + if (!sberror.Success()) + return; + } + + // Call LLDB API to get raw trace data for this thread + size_t bytes_written = trace.GetTraceData(error, (void *)pt_buffer.data(), + pt_buffer.size(), 0, tid); + if (!error.Success()) { + sberror.SetErrorStringWithFormat( + "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64, + error.GetCString(), tid, sbprocess.GetProcessID()); + return; + } + std::fill(pt_buffer.begin() + bytes_written, pt_buffer.end(), 0); + + // Get information of all the modules of the inferior + lldb::SBTarget sbtarget = sbprocess.GetTarget(); + ReadExecuteSectionInfos &readExecuteSectionInfos = + threadTraceInfo.GetReadExecuteSectionInfos(); + GetTargetModulesInfo(sbtarget, readExecuteSectionInfos, sberror); + if (!sberror.Success()) + return; +} + +void Decoder::DecodeProcessorTrace(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo &threadTraceInfo) { + // Initialize instruction decoder + struct pt_insn_decoder *decoder = nullptr; + struct pt_config config; + Buffer &pt_buffer = threadTraceInfo.GetPTBuffer(); + CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo(); + ReadExecuteSectionInfos &readExecuteSectionInfos = + threadTraceInfo.GetReadExecuteSectionInfos(); + + InitializePTInstDecoder(&decoder, &config, pt_cpu, pt_buffer, + readExecuteSectionInfos, sberror); + if (!sberror.Success()) + return; + + // Start raw trace decoding + Instructions &instruction_list = threadTraceInfo.GetInstructionLog(); + instruction_list.clear(); + DecodeTrace(decoder, instruction_list, sberror); +} + +// Raw trace decoding requires information of Read & Execute sections of each +// module of the inferior. This function updates internal state of the class to +// store this information. +void Decoder::GetTargetModulesInfo( + lldb::SBTarget &sbtarget, ReadExecuteSectionInfos &readExecuteSectionInfos, + lldb::SBError &sberror) { + if (!sbtarget.IsValid()) { + sberror.SetErrorStringWithFormat("Can't get target's modules info from " + "LLDB; process has an invalid target"); + return; + } + + lldb::SBFileSpec target_file_spec = sbtarget.GetExecutable(); + if (!target_file_spec.IsValid()) { + sberror.SetErrorStringWithFormat("Target has an invalid file spec"); + return; + } + + uint32_t num_modules = sbtarget.GetNumModules(); + readExecuteSectionInfos.clear(); + + // Store information of all RX sections of each module of inferior + for (uint32_t i = 0; i < num_modules; i++) { + lldb::SBModule module = sbtarget.GetModuleAtIndex(i); + if (!module.IsValid()) { + sberror.SetErrorStringWithFormat( + "Can't get module info [ %" PRIu32 + " ] of target \"%s\" from LLDB, invalid module", + i, target_file_spec.GetFilename()); + return; + } + + lldb::SBFileSpec module_file_spec = module.GetPlatformFileSpec(); + if (!module_file_spec.IsValid()) { + sberror.SetErrorStringWithFormat( + "Can't get module info [ %" PRIu32 + " ] of target \"%s\" from LLDB, invalid file spec", + i, target_file_spec.GetFilename()); + return; + } + + const char *image(module_file_spec.GetFilename()); + lldb::SBError error; + char image_complete_path[1024]; + uint32_t path_length = module_file_spec.GetPath( + image_complete_path, sizeof(image_complete_path)); + size_t num_sections = module.GetNumSections(); + + // Store information of only RX sections + for (size_t idx = 0; idx < num_sections; idx++) { + lldb::SBSection section = module.GetSectionAtIndex(idx); + uint32_t section_permission = section.GetPermissions(); + if ((section_permission & lldb::Permissions::ePermissionsReadable) && + (section_permission & lldb::Permissions::ePermissionsExecutable)) { + lldb::SBData section_data = section.GetSectionData(); + if (!section_data.IsValid()) { + sberror.SetErrorStringWithFormat( + "Can't get module info [ %" PRIu32 " ] \"%s\" of target " + "\"%s\" from LLDB, invalid " + "data in \"%s\" section", + i, image, target_file_spec.GetFilename(), section.GetName()); + return; + } + + // In case section has no data, skip it. + if (section_data.GetByteSize() == 0) + continue; + + if (!path_length) { + sberror.SetErrorStringWithFormat( + "Can't get module info [ %" PRIu32 " ] \"%s\" of target " + "\"%s\" from LLDB, module " + "has an invalid path length", + i, image, target_file_spec.GetFilename()); + return; + } + + std::string image_path(image_complete_path, path_length); + readExecuteSectionInfos.emplace_back( + section.GetLoadAddress(sbtarget), section.GetFileOffset(), + section_data.GetByteSize(), image_path); + } + } + } +} + +// Raw trace decoding requires information of the target cpu on which inferior +// is running. This function gets the Trace Configuration from LLDB, parses it +// for cpu model, family, stepping and vendor id info and updates the internal +// state of the class to store this information. +void Decoder::ParseCPUInfo(CPUInfo &pt_cpu, lldb::SBStructuredData &s, + lldb::SBError &sberror) { + lldb::SBStructuredData custom_trace_params = s.GetValueForKey("intel-pt"); + if (!custom_trace_params.IsValid()) { + sberror.SetErrorStringWithFormat("lldb couldn't provide cpuinfo"); + return; + } + + uint64_t family = 0, model = 0, stepping = 0; + char vendor[32]; + const char *key_family = "cpu_family"; + const char *key_model = "cpu_model"; + const char *key_stepping = "cpu_stepping"; + const char *key_vendor = "cpu_vendor"; + + // parse family + lldb::SBStructuredData struct_family = + custom_trace_params.GetValueForKey(key_family); + if (!struct_family.IsValid()) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters", key_family); + return; + } + family = struct_family.GetIntegerValue(0x10000); + if (family > UINT16_MAX) { + sberror.SetErrorStringWithFormat( + "invalid CPU family value extracted from custom trace parameters"); + return; + } + pt_cpu.family = (uint16_t)family; + + // parse model + lldb::SBStructuredData struct_model = + custom_trace_params.GetValueForKey(key_model); + if (!struct_model.IsValid()) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters; family=%" PRIu16, + key_model, pt_cpu.family); + return; + } + model = struct_model.GetIntegerValue(0x100); + if (model > UINT8_MAX) { + sberror.SetErrorStringWithFormat("invalid CPU model value extracted from " + "custom trace parameters; family=%" PRIu16, + pt_cpu.family); + return; + } + pt_cpu.model = (uint8_t)model; + + // parse stepping + lldb::SBStructuredData struct_stepping = + custom_trace_params.GetValueForKey(key_stepping); + if (!struct_stepping.IsValid()) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters; family=%" PRIu16 + ", model=%" PRIu8, + key_stepping, pt_cpu.family, pt_cpu.model); + return; + } + stepping = struct_stepping.GetIntegerValue(0x100); + if (stepping > UINT8_MAX) { + sberror.SetErrorStringWithFormat("invalid CPU stepping value extracted " + "from custom trace parameters; " + "family=%" PRIu16 ", model=%" PRIu8, + pt_cpu.family, pt_cpu.model); + return; + } + pt_cpu.stepping = (uint8_t)stepping; + + // parse vendor info + pt_cpu.vendor = pcv_unknown; + lldb::SBStructuredData struct_vendor = + custom_trace_params.GetValueForKey(key_vendor); + if (!struct_vendor.IsValid()) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters; family=%" PRIu16 + ", model=%" PRIu8 ", stepping=%" PRIu8, + key_vendor, pt_cpu.family, pt_cpu.model, pt_cpu.stepping); + return; + } + auto length = struct_vendor.GetStringValue(vendor, sizeof(vendor)); + if (length && strstr(vendor, "GenuineIntel")) + pt_cpu.vendor = pcv_intel; +} + +// Initialize trace decoder with pt_config structure and populate its image +// structure with inferior's memory image information. pt_config structure is +// initialized with trace buffer and cpu info of the inferior before storing it +// in trace decoder. +void Decoder::InitializePTInstDecoder( + struct pt_insn_decoder **decoder, struct pt_config *config, + const CPUInfo &pt_cpu, Buffer &pt_buffer, + const ReadExecuteSectionInfos &readExecuteSectionInfos, + lldb::SBError &sberror) const { + if (!decoder || !config) { + sberror.SetErrorStringWithFormat("internal error"); + return; + } + + // Load cpu info of inferior's target in pt_config struct + pt_config_init(config); + config->cpu = pt_cpu; + int errcode = pt_cpu_errata(&(config->errata), &(config->cpu)); + if (errcode < 0) { + sberror.SetErrorStringWithFormat("processor trace decoding library: " + "pt_cpu_errata() failed with error: " + "\"%s\"", + pt_errstr(pt_errcode(errcode))); + return; + } + + // Load trace buffer's starting and end address in pt_config struct + config->begin = pt_buffer.data(); + config->end = pt_buffer.data() + pt_buffer.size(); + + // Fill trace decoder with pt_config struct + *decoder = pt_insn_alloc_decoder(config); + if (*decoder == nullptr) { + sberror.SetErrorStringWithFormat("processor trace decoding library: " + "pt_insn_alloc_decoder() returned null " + "pointer"); + return; + } + + // Fill trace decoder's image with inferior's memory image information + struct pt_image *image = pt_insn_get_image(*decoder); + if (!image) { + sberror.SetErrorStringWithFormat("processor trace decoding library: " + "pt_insn_get_image() returned null " + "pointer"); + pt_insn_free_decoder(*decoder); + return; + } + + for (auto &itr : readExecuteSectionInfos) { + errcode = pt_image_add_file(image, itr.image_path.c_str(), itr.file_offset, + itr.size, nullptr, itr.load_address); + if (errcode < 0) { + sberror.SetErrorStringWithFormat("processor trace decoding library: " + "pt_image_add_file() failed with error: " + "\"%s\"", + pt_errstr(pt_errcode(errcode))); + pt_insn_free_decoder(*decoder); + return; + } + } +} + +// Start actual decoding of raw trace +void Decoder::DecodeTrace(struct pt_insn_decoder *decoder, + Instructions &instruction_list, + lldb::SBError &sberror) { + uint64_t decoder_offset = 0; + + while (1) { + struct pt_insn insn; + + // Try to sync the decoder. If it fails then get the decoder_offset and try + // to sync again. If the new_decoder_offset is same as decoder_offset then + // we will not succeed in syncing for any number of pt_insn_sync_forward() + // operations. Return in that case. Else keep resyncing until either end of + // trace stream is reached or pt_insn_sync_forward() passes. + int errcode = pt_insn_sync_forward(decoder); + if (errcode < 0) { + if (errcode == -pte_eos) + return; + + int errcode_off = pt_insn_get_offset(decoder, &decoder_offset); + if (errcode_off < 0) { + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\"", + pt_errstr(pt_errcode(errcode))); + instruction_list.emplace_back(sberror.GetCString()); + return; + } + + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\" [decoder_offset] => " + "[0x%" PRIu64 "]", + pt_errstr(pt_errcode(errcode)), decoder_offset); + instruction_list.emplace_back(sberror.GetCString()); + while (1) { + errcode = pt_insn_sync_forward(decoder); + if (errcode >= 0) + break; + + if (errcode == -pte_eos) + return; + + uint64_t new_decoder_offset = 0; + errcode_off = pt_insn_get_offset(decoder, &new_decoder_offset); + if (errcode_off < 0) { + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\"", + pt_errstr(pt_errcode(errcode))); + instruction_list.emplace_back(sberror.GetCString()); + return; + } else if (new_decoder_offset <= decoder_offset) { + // We tried resyncing the decoder and decoder didn't make any + // progress because the offset didn't change. We will not make any + // progress further. Hence, returning in this situation. + return; + } + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\" [decoder_offset] => " + "[0x%" PRIu64 "]", + pt_errstr(pt_errcode(errcode)), new_decoder_offset); + instruction_list.emplace_back(sberror.GetCString()); + decoder_offset = new_decoder_offset; + } + } + + while (1) { + errcode = pt_insn_next(decoder, &insn, sizeof(insn)); + if (errcode < 0) { + if (insn.iclass == ptic_error) + break; + + instruction_list.emplace_back(insn); + + if (errcode == -pte_eos) + return; + + Diagnose(decoder, errcode, sberror, &insn); + instruction_list.emplace_back(sberror.GetCString()); + break; + } + instruction_list.emplace_back(insn); + if (errcode & pts_eos) + return; + } + } +} + +// Function to diagnose and indicate errors during raw trace decoding +void Decoder::Diagnose(struct pt_insn_decoder *decoder, int decode_error, + lldb::SBError &sberror, const struct pt_insn *insn) { + int errcode; + uint64_t offset; + + errcode = pt_insn_get_offset(decoder, &offset); + if (insn) { + if (errcode < 0) + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\" [decoder_offset, " + "last_successful_decoded_ip] => [?, 0x%" PRIu64 "]", + pt_errstr(pt_errcode(decode_error)), insn->ip); + else + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\" [decoder_offset, " + "last_successful_decoded_ip] => [0x%" PRIu64 ", 0x%" PRIu64 "]", + pt_errstr(pt_errcode(decode_error)), offset, insn->ip); + } else { + if (errcode < 0) + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\"", + pt_errstr(pt_errcode(decode_error))); + else + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\" [decoder_offset] => " + "[0x%" PRIu64 "]", + pt_errstr(pt_errcode(decode_error)), offset); + } +} + +void Decoder::GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, + lldb::tid_t tid, uint32_t offset, + uint32_t count, + InstructionList &result_list, + lldb::SBError &sberror) { + sberror.Clear(); + CheckDebuggerID(sbprocess, sberror); + if (!sberror.Success()) { + return; + } + + std::lock_guard<std::mutex> guard( + m_mapProcessUID_mapThreadID_TraceInfo_mutex); + RemoveDeadProcessesAndThreads(sbprocess); + + ThreadTraceInfo *threadTraceInfo = nullptr; + FetchAndDecode(sbprocess, tid, sberror, &threadTraceInfo); + if (!sberror.Success()) { + return; + } + if (threadTraceInfo == nullptr) { + sberror.SetErrorStringWithFormat("internal error"); + return; + } + + // Return instruction log by populating 'result_list' + Instructions &insn_list = threadTraceInfo->GetInstructionLog(); + uint64_t sum = (uint64_t)offset + 1; + if (((insn_list.size() <= offset) && (count <= sum) && + ((sum - count) >= insn_list.size())) || + (count < 1)) { + sberror.SetErrorStringWithFormat( + "Instruction Log not available for offset=%" PRIu32 + " and count=%" PRIu32 ", ProcessID = %" PRIu64, + offset, count, sbprocess.GetProcessID()); + return; + } + + Instructions::iterator itr_first = + (insn_list.size() <= offset) ? insn_list.begin() + : insn_list.begin() + insn_list.size() - sum; + Instructions::iterator itr_last = + (count <= sum) ? insn_list.begin() + insn_list.size() - (sum - count) + : insn_list.end(); + Instructions::iterator itr = itr_first; + while (itr != itr_last) { + result_list.AppendInstruction(*itr); + ++itr; + } +} + +void Decoder::GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + TraceOptions &options, + lldb::SBError &sberror) { + sberror.Clear(); + CheckDebuggerID(sbprocess, sberror); + if (!sberror.Success()) { + return; + } + + std::lock_guard<std::mutex> guard( + m_mapProcessUID_mapThreadID_TraceInfo_mutex); + RemoveDeadProcessesAndThreads(sbprocess); + + ThreadTraceInfo *threadTraceInfo = nullptr; + FetchAndDecode(sbprocess, tid, sberror, &threadTraceInfo); + if (!sberror.Success()) { + return; + } + if (threadTraceInfo == nullptr) { + sberror.SetErrorStringWithFormat("internal error"); + return; + } + + // Get SBTraceOptions from LLDB for 'tid', populate 'traceoptions' with it + lldb::SBTrace &trace = threadTraceInfo->GetUniqueTraceInstance(); + lldb::SBTraceOptions traceoptions; + lldb::SBError error; + traceoptions.setThreadID(tid); + trace.GetTraceConfig(traceoptions, error); + if (!error.Success()) { + std::string error_string(error.GetCString()); + if (error_string.find("tracing not active") != std::string::npos) { + uint32_t unique_id = sbprocess.GetUniqueID(); + auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id); + if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) + return; + itr_process->second.erase(tid); + } + sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64, + error_string.c_str(), + sbprocess.GetProcessID()); + return; + } + if (traceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) { + sberror.SetErrorStringWithFormat("invalid TraceType received from LLDB " + "for this thread; thread id=%" PRIu64 + ", ProcessID = %" PRIu64, + tid, sbprocess.GetProcessID()); + return; + } + options.setType(traceoptions.getType()); + options.setTraceBufferSize(traceoptions.getTraceBufferSize()); + options.setMetaDataBufferSize(traceoptions.getMetaDataBufferSize()); + lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror); + if (!sberror.Success()) + return; + options.setTraceParams(sbstructdata); + options.setInstructionLogSize(threadTraceInfo->GetInstructionLog().size()); +} + +void Decoder::FetchAndDecode(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo **threadTraceInfo) { + // Return with error if 'sbprocess' is not registered in the class + uint32_t unique_id = sbprocess.GetUniqueID(); + auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id); + if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) { + sberror.SetErrorStringWithFormat( + "tracing not active for this process; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + if (tid == LLDB_INVALID_THREAD_ID) { + sberror.SetErrorStringWithFormat( + "invalid thread id provided; thread_id = %" PRIu64 + ", ProcessID = %" PRIu64, + tid, sbprocess.GetProcessID()); + return; + } + + // Check whether 'tid' thread is registered in the class. If it is then in + // case StopID didn't change then return without doing anything (no need to + // read and decode trace data then). Otherwise, save new StopID and proceed + // with reading and decoding trace. + if (threadTraceInfo == nullptr) { + sberror.SetErrorStringWithFormat("internal error"); + return; + } + + MapThreadID_TraceInfo &mapThreadID_TraceInfo = itr_process->second; + auto itr_thread = mapThreadID_TraceInfo.find(tid); + if (itr_thread != mapThreadID_TraceInfo.end()) { + if (itr_thread->second.GetStopID() == sbprocess.GetStopID()) { + *threadTraceInfo = &(itr_thread->second); + return; + } + itr_thread->second.SetStopID(sbprocess.GetStopID()); + } else { + // Implies 'tid' is not registered in the class. If tracing was never + // started on the entire process then return an error. Else try to register + // this thread and proceed with reading and decoding trace. + lldb::SBError error; + itr_thread = mapThreadID_TraceInfo.find(LLDB_INVALID_THREAD_ID); + if (itr_thread == mapThreadID_TraceInfo.end()) { + sberror.SetErrorStringWithFormat( + "tracing not active for this thread; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + lldb::SBTrace &trace = itr_thread->second.GetUniqueTraceInstance(); + ThreadTraceInfo &trace_info = mapThreadID_TraceInfo[tid]; + trace_info.SetUniqueTraceInstance(trace); + trace_info.SetStopID(sbprocess.GetStopID()); + itr_thread = mapThreadID_TraceInfo.find(tid); + } + + // Get raw trace data and inferior image from LLDB for the registered thread + ReadTraceDataAndImageInfo(sbprocess, tid, sberror, itr_thread->second); + if (!sberror.Success()) { + std::string error_string(sberror.GetCString()); + if (error_string.find("tracing not active") != std::string::npos) + mapThreadID_TraceInfo.erase(itr_thread); + return; + } + // Decode raw trace data + DecodeProcessorTrace(sbprocess, tid, sberror, itr_thread->second); + if (!sberror.Success()) { + return; + } + *threadTraceInfo = &(itr_thread->second); +} + +// This function checks whether the provided SBProcess instance belongs to same +// SBDebugger with which this tool instance is associated. +void Decoder::CheckDebuggerID(lldb::SBProcess &sbprocess, + lldb::SBError &sberror) { + if (!sbprocess.IsValid()) { + sberror.SetErrorStringWithFormat("invalid process instance"); + return; + } + + lldb::SBTarget sbtarget = sbprocess.GetTarget(); + if (!sbtarget.IsValid()) { + sberror.SetErrorStringWithFormat( + "process contains an invalid target; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + lldb::SBDebugger sbdebugger = sbtarget.GetDebugger(); + if (!sbdebugger.IsValid()) { + sberror.SetErrorStringWithFormat("process's target contains an invalid " + "debugger instance; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + if (sbdebugger.GetID() != m_debugger_user_id) { + sberror.SetErrorStringWithFormat( + "process belongs to a different SBDebugger instance than the one for " + "which the tool is instantiated; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } +} diff --git a/tools/intel-features/intel-pt/Decoder.h b/tools/intel-features/intel-pt/Decoder.h new file mode 100644 index 000000000000..dc2794ac3bc3 --- /dev/null +++ b/tools/intel-features/intel-pt/Decoder.h @@ -0,0 +1,327 @@ +//===-- Decoder.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef Decoder_h_ +#define Decoder_h_ + +// C/C++ Includes +#include <map> +#include <mutex> +#include <string> +#include <vector> + +// Project includes, Other libraries and framework includes +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStructuredData.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBTrace.h" +#include "lldb/API/SBTraceOptions.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-types.h" + +#include "intel-pt.h" + +namespace ptdecoder_private { +//---------------------------------------------------------------------- +/// @class Instruction +/// @brief Represents an assembly instruction containing raw +/// instruction bytes, instruction address along with information +/// regarding execution flow context and Intel(R) Processor Trace +/// context. +//---------------------------------------------------------------------- +class Instruction { +public: + Instruction() : ip(0), data(), error(), iclass(ptic_error), speculative(0) {} + + Instruction(const Instruction &insn) = default; + + Instruction(const struct pt_insn &insn) + : ip(insn.ip), data(), error(insn.size == 0 ? "invalid instruction" : ""), + iclass(insn.iclass), speculative(insn.speculative) { + if (insn.size != 0) + data.assign(insn.raw, insn.raw + insn.size); + } + + Instruction(const char *err) + : ip(0), data(), error(err ? err : "unknown error"), iclass(ptic_error), + speculative(0) {} + + ~Instruction() {} + + uint64_t GetInsnAddress() const { return ip; } + + size_t GetRawBytes(void *buf, size_t size) const { + if ((buf == nullptr) || (size == 0)) + return data.size(); + + size_t bytes_to_read = ((size <= data.size()) ? size : data.size()); + ::memcpy(buf, data.data(), bytes_to_read); + return bytes_to_read; + } + + const std::string &GetError() const { return error; } + + bool GetSpeculative() const { return speculative; } + +private: + uint64_t ip; // instruction address in inferior's memory image + std::vector<uint8_t> data; // raw bytes + std::string error; // Error string if instruction is invalid + enum pt_insn_class iclass; // classification of the instruction + // A collection of flags giving additional information about instruction + uint32_t speculative : 1; // Instruction was executed speculatively or not +}; + +//--------------------------------------------------------------------------- +/// @class InstructionList +/// @brief Represents a list of assembly instructions. Each instruction is of +/// type Instruction. +//--------------------------------------------------------------------------- +class InstructionList { +public: + InstructionList() : m_insn_vec() {} + + InstructionList(const InstructionList &insn_list) + : m_insn_vec(insn_list.m_insn_vec) {} + + ~InstructionList() {} + + // Get number of instructions in the list + size_t GetSize() const { return m_insn_vec.size(); } + + // Get instruction at index + Instruction GetInstructionAtIndex(uint32_t idx) { + return (idx < m_insn_vec.size() ? m_insn_vec[idx] + : Instruction("invalid instruction")); + } + + // Append intruction at the end of the list + void AppendInstruction(Instruction inst) { m_insn_vec.push_back(inst); } + +private: + std::vector<Instruction> m_insn_vec; +}; + +//---------------------------------------------------------------------- +/// @class TraceOptions +/// @brief Provides Intel(R) Processor Trace specific configuration options and +/// other information obtained by decoding and post-processing the trace +/// data. Currently, this information comprises of the total number of +/// assembly instructions executed for an inferior. +//---------------------------------------------------------------------- +class TraceOptions : public lldb::SBTraceOptions { +public: + TraceOptions() : lldb::SBTraceOptions(), m_insn_log_size(0) {} + + ~TraceOptions() {} + + //------------------------------------------------------------------ + /// Get total number of assembly instructions obtained after decoding the + /// complete Intel(R) Processor Trace data obtained from LLDB. + /// + /// @return + /// Total number of instructions. + //------------------------------------------------------------------ + uint32_t getInstructionLogSize() const { return m_insn_log_size; } + + //------------------------------------------------------------------ + /// Set total number of assembly instructions. + /// + /// @param[in] size + /// Value to be set. + //------------------------------------------------------------------ + void setInstructionLogSize(uint32_t size) { m_insn_log_size = size; } + +private: + uint32_t m_insn_log_size; +}; + +//---------------------------------------------------------------------- +/// @class Decoder +/// @brief This class makes use of Intel(R) Processor Trace hardware feature +/// (implememted inside LLDB) to gather trace data for an inferior (being +/// debugged with LLDB) to provide meaningful information out of it. +/// +/// Currently the meaningful information comprises of the execution flow +/// of the inferior (in terms of assembly instructions executed). The class +/// enables user to: +/// - start the trace with configuration options for a thread/process, +/// - stop the trace for a thread/process, +/// - get the execution flow (assembly instructions) for a thread and +/// - get trace specific information for a thread +//---------------------------------------------------------------------- +class Decoder { +public: + typedef std::vector<Instruction> Instructions; + + Decoder(lldb::SBDebugger &sbdebugger) + : m_mapProcessUID_mapThreadID_TraceInfo_mutex(), + m_mapProcessUID_mapThreadID_TraceInfo(), + m_debugger_user_id(sbdebugger.GetID()) {} + + ~Decoder() {} + + void StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror); + + void StopProcessorTrace(lldb::SBProcess &sbprocess, lldb::SBError &sberror, + lldb::tid_t tid = LLDB_INVALID_THREAD_ID); + + void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid, + uint32_t offset, uint32_t count, + InstructionList &result_list, + lldb::SBError &sberror); + + void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + TraceOptions &traceinfo, lldb::SBError &sberror); + +private: + class ThreadTraceInfo; + typedef std::vector<uint8_t> Buffer; + + // internal class to manage inferior's read-execute section information + class ReadExecuteSectionInfo { + public: + uint64_t load_address; + uint64_t file_offset; + uint64_t size; + std::string image_path; + + ReadExecuteSectionInfo(const uint64_t addr, const uint64_t offset, + const uint64_t sz, const std::string &path) + : load_address(addr), file_offset(offset), size(sz), image_path(path) {} + + ReadExecuteSectionInfo(const ReadExecuteSectionInfo &rxsection) = default; + }; + + typedef struct pt_cpu CPUInfo; + typedef std::vector<ReadExecuteSectionInfo> ReadExecuteSectionInfos; + + // Check whether the provided SBProcess belongs to the same SBDebugger with + // which Decoder class instance was constructed. + void CheckDebuggerID(lldb::SBProcess &sbprocess, lldb::SBError &sberror); + + // Function to remove entries of finished processes/threads in the class + void RemoveDeadProcessesAndThreads(lldb::SBProcess &sbprocess); + + // Parse cpu information from trace configuration received from LLDB + void ParseCPUInfo(CPUInfo &pt_cpu, lldb::SBStructuredData &s, + lldb::SBError &sberror); + + ///------------------------------------------------------------------------ + /// Function performs following tasks for a given process and thread: + /// - Checks if the given thread is registered in the class or not. If not + /// then tries to register it if trace was ever started on the entire + /// process. Else returns error. + /// - fetches trace and other necessary information from LLDB (using + /// ReadTraceDataAndImageInfo()) and decodes the trace (using + /// DecodeProcessorTrace()) + ///------------------------------------------------------------------------ + void FetchAndDecode(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo **threadTraceInfo); + + // Helper function of FetchAndDecode() to get raw trace data and memory image + // info of inferior from LLDB + void ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo &threadTraceInfo); + + // Helper function of FetchAndDecode() to initialize raw trace decoder and + // start trace decoding + void DecodeProcessorTrace(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo &threadTraceInfo); + + // Helper function of ReadTraceDataAndImageInfo() function for gathering + // inferior's memory image info along with all dynamic libraries linked with + // it + void GetTargetModulesInfo(lldb::SBTarget &sbtarget, + ReadExecuteSectionInfos &readExecuteSectionInfos, + lldb::SBError &sberror); + + ///------------------------------------------------------------------------ + /// Helper functions of DecodeProcessorTrace() function for: + /// - initializing raw trace decoder (provided by Intel(R) Processor Trace + /// Decoding library) + /// - start trace decoding + ///------------------------------------------------------------------------ + void InitializePTInstDecoder( + struct pt_insn_decoder **decoder, struct pt_config *config, + const CPUInfo &pt_cpu, Buffer &pt_buffer, + const ReadExecuteSectionInfos &readExecuteSectionInfos, + lldb::SBError &sberror) const; + void DecodeTrace(struct pt_insn_decoder *decoder, + Instructions &instruction_list, lldb::SBError &sberror); + + // Function to diagnose and indicate errors during raw trace decoding + void Diagnose(struct pt_insn_decoder *decoder, int errcode, + lldb::SBError &sberror, const struct pt_insn *insn = nullptr); + + class ThreadTraceInfo { + public: + ThreadTraceInfo() + : m_pt_buffer(), m_readExecuteSectionInfos(), m_thread_stop_id(0), + m_trace(), m_pt_cpu(), m_instruction_log() {} + + ThreadTraceInfo(const ThreadTraceInfo &trace_info) = default; + + ~ThreadTraceInfo() {} + + Buffer &GetPTBuffer() { return m_pt_buffer; } + + void AllocatePTBuffer(uint64_t size) { m_pt_buffer.assign(size, 0); } + + ReadExecuteSectionInfos &GetReadExecuteSectionInfos() { + return m_readExecuteSectionInfos; + } + + CPUInfo &GetCPUInfo() { return m_pt_cpu; } + + Instructions &GetInstructionLog() { return m_instruction_log; } + + uint32_t GetStopID() const { return m_thread_stop_id; } + + void SetStopID(uint32_t stop_id) { m_thread_stop_id = stop_id; } + + lldb::SBTrace &GetUniqueTraceInstance() { return m_trace; } + + void SetUniqueTraceInstance(lldb::SBTrace &trace) { m_trace = trace; } + + friend class Decoder; + + private: + Buffer m_pt_buffer; // raw trace buffer + ReadExecuteSectionInfos + m_readExecuteSectionInfos; // inferior's memory image info + uint32_t m_thread_stop_id; // stop id for thread + lldb::SBTrace m_trace; // unique tracing instance of a thread/process + CPUInfo m_pt_cpu; // cpu info of the target on which inferior is running + Instructions m_instruction_log; // complete instruction log + }; + + typedef std::map<lldb::user_id_t, ThreadTraceInfo> MapThreadID_TraceInfo; + typedef std::map<uint32_t, MapThreadID_TraceInfo> + MapProcessUID_MapThreadID_TraceInfo; + + std::mutex m_mapProcessUID_mapThreadID_TraceInfo_mutex; + MapProcessUID_MapThreadID_TraceInfo + m_mapProcessUID_mapThreadID_TraceInfo; // to store trace information for + // each process and its associated + // threads + lldb::user_id_t m_debugger_user_id; // SBDebugger instance which is associated + // to this Decoder instance +}; + +} // namespace ptdecoder_private +#endif // Decoder_h_ diff --git a/tools/intel-features/intel-pt/PTDecoder.cpp b/tools/intel-features/intel-pt/PTDecoder.cpp new file mode 100644 index 000000000000..4f3554e84d3c --- /dev/null +++ b/tools/intel-features/intel-pt/PTDecoder.cpp @@ -0,0 +1,175 @@ +//===-- PTDecoder.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Project includes +#include "PTDecoder.h" +#include "Decoder.h" + +using namespace ptdecoder; +using namespace ptdecoder_private; + +// PTInstruction class member functions definitions +PTInstruction::PTInstruction() : m_opaque_sp() {} + +PTInstruction::PTInstruction(const PTInstruction &insn) + : m_opaque_sp(insn.m_opaque_sp) {} + +PTInstruction::PTInstruction( + const std::shared_ptr<ptdecoder_private::Instruction> &ptr) + : m_opaque_sp(ptr) {} + +PTInstruction::~PTInstruction() {} + +uint64_t PTInstruction::GetInsnAddress() const { + return (m_opaque_sp ? m_opaque_sp->GetInsnAddress() : 0); +} + +size_t PTInstruction::GetRawBytes(void *buf, size_t size) const { + return (m_opaque_sp ? m_opaque_sp->GetRawBytes(buf, size) : 0); +} + +std::string PTInstruction::GetError() const { + return (m_opaque_sp ? m_opaque_sp->GetError() : "null pointer"); +} + +bool PTInstruction::GetSpeculative() const { + return (m_opaque_sp ? m_opaque_sp->GetSpeculative() : 0); +} + +// PTInstructionList class member functions definitions +PTInstructionList::PTInstructionList() : m_opaque_sp() {} + +PTInstructionList::PTInstructionList(const PTInstructionList &insn_list) + : m_opaque_sp(insn_list.m_opaque_sp) {} + +PTInstructionList::~PTInstructionList() {} + +size_t PTInstructionList::GetSize() const { + return (m_opaque_sp ? m_opaque_sp->GetSize() : 0); +} + +PTInstruction PTInstructionList::GetInstructionAtIndex(uint32_t idx) { + if (m_opaque_sp) + return PTInstruction(std::shared_ptr<ptdecoder_private::Instruction>( + new Instruction(m_opaque_sp->GetInstructionAtIndex(idx)))); + + return PTInstruction(std::shared_ptr<ptdecoder_private::Instruction>( + new Instruction("invalid instruction"))); +} + +void PTInstructionList::SetSP( + const std::shared_ptr<ptdecoder_private::InstructionList> &ptr) { + m_opaque_sp = ptr; +} +void PTInstructionList::Clear() { + if (!m_opaque_sp) + return; + m_opaque_sp.reset(); +} + +// PTTraceOptions class member functions definitions +PTTraceOptions::PTTraceOptions() : m_opaque_sp() {} + +PTTraceOptions::PTTraceOptions(const PTTraceOptions &options) + : m_opaque_sp(options.m_opaque_sp) {} + +PTTraceOptions::~PTTraceOptions() {} + +lldb::TraceType PTTraceOptions::GetType() const { + return (m_opaque_sp ? m_opaque_sp->getType() + : lldb::TraceType::eTraceTypeNone); +} + +uint64_t PTTraceOptions::GetTraceBufferSize() const { + return (m_opaque_sp ? m_opaque_sp->getTraceBufferSize() : 0); +} + +uint64_t PTTraceOptions::GetMetaDataBufferSize() const { + return (m_opaque_sp ? m_opaque_sp->getMetaDataBufferSize() : 0); +} + +lldb::SBStructuredData PTTraceOptions::GetTraceParams(lldb::SBError &error) { + if (!m_opaque_sp) + error.SetErrorString("null pointer"); + return (m_opaque_sp ? m_opaque_sp->getTraceParams(error) + : lldb::SBStructuredData()); +} + +void PTTraceOptions::SetSP( + const std::shared_ptr<ptdecoder_private::TraceOptions> &ptr) { + m_opaque_sp = ptr; +} + +// PTDecoder class member functions definitions +PTDecoder::PTDecoder(lldb::SBDebugger &sbdebugger) + : m_opaque_sp(new ptdecoder_private::Decoder(sbdebugger)) {} + +PTDecoder::PTDecoder(const PTDecoder &ptdecoder) + : m_opaque_sp(ptdecoder.m_opaque_sp) {} + +PTDecoder::~PTDecoder() {} + +void PTDecoder::StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + m_opaque_sp->StartProcessorTrace(sbprocess, sbtraceoptions, sberror); +} + +void PTDecoder::StopProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBError &sberror, lldb::tid_t tid) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + m_opaque_sp->StopProcessorTrace(sbprocess, sberror, tid); +} + +void PTDecoder::GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, + lldb::tid_t tid, uint32_t offset, + uint32_t count, + PTInstructionList &result_list, + lldb::SBError &sberror) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + std::shared_ptr<ptdecoder_private::InstructionList> insn_list_ptr( + new InstructionList()); + m_opaque_sp->GetInstructionLogAtOffset(sbprocess, tid, offset, count, + *insn_list_ptr, sberror); + if (!sberror.Success()) + return; + + result_list.SetSP(insn_list_ptr); +} + +void PTDecoder::GetProcessorTraceInfo(lldb::SBProcess &sbprocess, + lldb::tid_t tid, PTTraceOptions &options, + lldb::SBError &sberror) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + std::shared_ptr<ptdecoder_private::TraceOptions> trace_options_ptr( + new TraceOptions()); + m_opaque_sp->GetProcessorTraceInfo(sbprocess, tid, *trace_options_ptr, + sberror); + if (!sberror.Success()) + return; + + options.SetSP(trace_options_ptr); +} diff --git a/tools/intel-features/intel-pt/PTDecoder.h b/tools/intel-features/intel-pt/PTDecoder.h new file mode 100644 index 000000000000..9eb92745a863 --- /dev/null +++ b/tools/intel-features/intel-pt/PTDecoder.h @@ -0,0 +1,310 @@ +//===-- PTDecoder.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef PTDecoder_h_ +#define PTDecoder_h_ + +// C/C++ Includes +#include <vector> + +// Project includes, Other libraries and framework includes +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStructuredData.h" +#include "lldb/API/SBTraceOptions.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-types.h" + +namespace ptdecoder_private { +class Instruction; +class InstructionList; +class TraceOptions; +class Decoder; +} // namespace ptdecoder_private + +namespace ptdecoder { + +//---------------------------------------------------------------------- +/// @class PTInstruction +/// @brief Represents an assembly instruction containing raw +/// instruction bytes, instruction address along with information +/// regarding execution flow context and Intel(R) Processor Trace +/// context. +//---------------------------------------------------------------------- +class PTInstruction { +public: + PTInstruction(); + + PTInstruction(const PTInstruction &insn); + + PTInstruction(const std::shared_ptr<ptdecoder_private::Instruction> &ptr); + + ~PTInstruction(); + + // Get instruction address in inferior's memory image + uint64_t GetInsnAddress() const; + + //------------------------------------------------------------------ + /// Get raw bytes of the instruction in the buffer. + /// + /// @param[out] buf + /// The buffer where the raw bytes will be written. This buffer should be + /// allocated by the caller of this API. Providing an unallocated buffer + /// is an error. In case of errors, the content of the buffer is not + /// valid. + /// + /// @param[in] size + /// Number of raw bytes to be written to @buf. Atleast @size bytes of + /// memory should be allocated to @buf otherwise the behaviour of the API + /// is undefined. Providing 0 for this argument is an error. + /// + /// @return + /// Number of bytes of the instruction actually written to @buf if API + /// succeeds. In case of errors, total number of raw bytes of the + /// instruction is returned. + //------------------------------------------------------------------ + size_t GetRawBytes(void *buf, size_t size) const; + + // Get error string if it represents an invalid instruction. For a valid + // instruction, an empty string is returned + std::string GetError() const; + + // Instruction was executed speculatively or not + bool GetSpeculative() const; + +private: + std::shared_ptr<ptdecoder_private::Instruction> m_opaque_sp; +}; + +//--------------------------------------------------------------------------- +/// @class PTInstructionList +/// @brief Represents a list of assembly instructions. Each instruction is of +/// type PTInstruction. +//--------------------------------------------------------------------------- +class PTInstructionList { +public: + PTInstructionList(); + + PTInstructionList(const PTInstructionList &insn_list); + + ~PTInstructionList(); + + // Get number of instructions in the list + size_t GetSize() const; + + // Get instruction at index + PTInstruction GetInstructionAtIndex(uint32_t idx); + + void Clear(); + +private: + friend class PTDecoder; + + void SetSP(const std::shared_ptr<ptdecoder_private::InstructionList> &ptr); + + std::shared_ptr<ptdecoder_private::InstructionList> m_opaque_sp; +}; + +//---------------------------------------------------------------------- +/// @class PTTraceOptions +/// @brief Provides configuration options like trace type, trace buffer size, +/// meta data buffer size along with other Intel(R) Processor Trace +/// specific options. +//---------------------------------------------------------------------- +class PTTraceOptions { +public: + PTTraceOptions(); + + PTTraceOptions(const PTTraceOptions &options); + + ~PTTraceOptions(); + + lldb::TraceType GetType() const; + + uint64_t GetTraceBufferSize() const; + + uint64_t GetMetaDataBufferSize() const; + + //------------------------------------------------------------------ + /// Get Intel(R) Processor Trace specific configuration options (apart from + /// trace buffer size, meta data buffer size and TraceType) formatted as json + /// text i.e. {"Name":Value,"Name":Value} pairs, where "Value" is a 64-bit + /// unsigned integer in hex format. For "Name", please refer to + /// SBProcess::StartTrace API description for setting SBTraceOptions. + /// + /// @return + /// A string formatted as json text {"Name":Value,"Name":Value} + //------------------------------------------------------------------ + lldb::SBStructuredData GetTraceParams(lldb::SBError &error); + +private: + friend class PTDecoder; + + void SetSP(const std::shared_ptr<ptdecoder_private::TraceOptions> &ptr); + + std::shared_ptr<ptdecoder_private::TraceOptions> m_opaque_sp; +}; + +//---------------------------------------------------------------------- +/// @class PTDecoder +/// @brief This class makes use of Intel(R) Processor Trace hardware feature +/// (implememted inside LLDB) to gather trace data for an inferior (being +/// debugged with LLDB) to provide meaningful information out of it. +/// +/// Currently the meaningful information comprises of the execution flow +/// of the inferior (in terms of assembly instructions executed). The class +/// enables user to: +/// - start the trace with configuration options for a thread/process, +/// - stop the trace for a thread/process, +/// - get the execution flow (assembly instructions) for a thread and +/// - get trace specific information for a thread +//---------------------------------------------------------------------- +class PTDecoder { +public: + PTDecoder(lldb::SBDebugger &sbdebugger); + + PTDecoder(const PTDecoder &ptdecoder); + + ~PTDecoder(); + + //------------------------------------------------------------------ + /// Start Intel(R) Processor Trace on a thread or complete process with + /// Intel(R) Processor Trace specific configuration options + /// + /// @param[in] sbprocess + /// A valid process on which this operation will be performed. An error is + /// returned in case of an invalid process. + /// + /// @param[in] sbtraceoptions + /// Contains thread id information and configuration options: + /// + /// For tracing a single thread, provide a valid thread id. If sbprocess + /// doesn't contain this thread id, error will be returned. For tracing + /// complete process, set it to lldb::LLDB_INVALID_THREAD_ID + /// Configuration options comprises of: + /// a) trace buffer size, meta data buffer size, TraceType and + /// b) All other possible Intel(R) Processor Trace specific configuration + /// options (hereafter collectively referred as CUSTOM_OPTIONS), formatted + /// as json text i.e. {"Name":Value,"Name":Value,..} inside + /// sbtraceoptions, where "Value" should be a 64-bit unsigned integer in + /// hex format. For information regarding what all configuration options + /// are currently supported by LLDB and detailed information about + /// CUSTOM_OPTIONS usage, please refer to SBProcess::StartTrace() API + /// description. To know about all possible configuration options of + /// Intel(R) Processor Trace, please refer to Intel(R) 64 and IA-32 + /// Architectures Software Developer's Manual. + /// + /// TraceType should be set to lldb::TraceType::eTraceTypeProcessorTrace, + /// else error is returned. To find out any other requirement to start + /// tracing successfully, please refer to SBProcess::StartTrace() API + /// description. LLDB's current implementation of Intel(R) Processor Trace + /// feature may round off invalid values for configuration options. + /// Therefore, the configuration options with which the trace was actually + /// started, might be different to the ones with which trace was asked to + /// be started by user. The actual used configuration options can be + /// obtained from GetProcessorTraceInfo() API. + /// + /// @param[out] sberror + /// An error with the failure reason if API fails. Else success. + //------------------------------------------------------------------ + void StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror); + + //------------------------------------------------------------------ + /// Stop Intel(R) Processor Trace on a thread or complete process. + /// + /// @param[in] sbprocess + /// A valid process on which this operation will be performed. An error is + /// returned in case of an invalid process. + /// + /// @param[in] tid + /// Case 1: To stop tracing a single thread, provide a valid thread id. If + /// sbprocess doesn't contain the thread tid, error will be returned. + /// Case 2: To stop tracing complete process, use + /// lldb::LLDB_INVALID_THREAD_ID. + /// + /// @param[out] sberror + /// An error with the failure reason if API fails. Else success. + //------------------------------------------------------------------ + void StopProcessorTrace(lldb::SBProcess &sbprocess, lldb::SBError &sberror, + lldb::tid_t tid = LLDB_INVALID_THREAD_ID); + + //------------------------------------------------------------------ + /// Get instruction log containing the execution flow for a thread of a + /// process in terms of assembly instructions executed. + /// + /// @param[in] sbprocess + /// A valid process on which this operation will be performed. An error is + /// returned in case of an invalid process. + /// + /// @param[in] tid + /// A valid thread id of the thread for which instruction log is desired. + /// If sbprocess doesn't contain the thread tid, error will be returned. + /// + /// @param[in] count + /// The number of instructions requested by the user to be returned from + /// the complete instruction log. Complete instruction log refers to all + /// the assembly instructions obtained after decoding the complete raw + /// trace data obtained from LLDB. The length of the complete instruction + /// log is dependent on the trace buffer size with which processor tracing + /// was started for this thread. + /// The number of instructions actually returned are dependent on 'count' + /// and 'offset' parameters of this API. + /// + /// @param[in] offset + /// The offset in the complete instruction log from where 'count' number + /// of instructions are requested by the user. offset is counted from the + /// end of of this complete instruction log (which means the last executed + /// instruction is at offset 0 (zero)). + /// + /// @param[out] result_list + /// Depending upon 'count' and 'offset' values, list will be overwritten + /// with the new instructions. + /// + /// @param[out] sberror + /// An error with the failure reason if API fails. Else success. + //------------------------------------------------------------------ + void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid, + uint32_t offset, uint32_t count, + PTInstructionList &result_list, + lldb::SBError &sberror); + + //------------------------------------------------------------------ + /// Get Intel(R) Processor Trace specific information for a thread of a + /// process. The information contains the actual configuration options with + /// which the trace was started for this thread. + /// + /// @param[in] sbprocess + /// A valid process on which this operation will be performed. An error is + /// returned in case of an invalid process. + /// + /// @param[in] tid + /// A valid thread id of the thread for which the trace specific + /// information is required. If sbprocess doesn't contain the thread tid, + /// an error will be returned. + /// + /// @param[out] options + /// Contains actual configuration options (they may be different to the + /// ones with which tracing was asked to be started for this thread during + /// StartProcessorTrace() API call). + /// + /// @param[out] sberror + /// An error with the failure reason if API fails. Else success. + //------------------------------------------------------------------ + void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + PTTraceOptions &options, lldb::SBError &sberror); + +private: + std::shared_ptr<ptdecoder_private::Decoder> m_opaque_sp; +}; + +} // namespace ptdecoder +#endif // PTDecoder_h_ diff --git a/tools/intel-features/intel-pt/README_CLI.txt b/tools/intel-features/intel-pt/README_CLI.txt new file mode 100644 index 000000000000..2a497c4b3ef1 --- /dev/null +++ b/tools/intel-features/intel-pt/README_CLI.txt @@ -0,0 +1,123 @@ +**************************************************************************** +* README * +* * +* This file provides all the information regarding 4 new CLI commands that * +* enable using Intel(R) Processor Trace Tool from LLDB's CLI. * +**************************************************************************** + + +============ +Introduction +============ +A C++ based cli wrapper has been developed to use Intel(R) Processor Trace Tool +through LLDB's command line. This also provides an idea to all developers on how +to integrate the Tool into various IDEs providing LLDB as a debugger. + + + +============ +How to Build +============ +The wrapper cli-wrapper-pt.cpp needs to be compiled and linked with the shared +library of the Intel(R) Processor Trace Tool in order to be used through LLDB's +CLI. The procedure to build shared library of the Intel(R) Processor Trace Tool +is given in README_TOOL.txt file. + + + +============ +How to Use +============ +All these commands are available via shared library (lldbIntelFeatures) +obtained after building intel-features folder from top. Please refer to +cli-wrapper.cpp and README files of "intel-features" folder for this purpose. + + + +============ +Description +============ +4 CLI commands have been designed keeping the LLDB's existing CLI command syntax +in mind. + + 1) processor-trace start [-b <buffer-size>] [<thread-index>] + + Start Intel(R) Processor Trace on a specific thread or on the whole process + + Syntax: processor-trace start <cmd-options> + + cmd-options Usage: + processor-trace start [-b <buffer-size>] [<thread-index>] + + -b <buffer-size> + size of the trace buffer to store the trace data. If not specified + then a default value (=4KB) will be taken + + <thread-index> + thread index of the thread. If no threads are specified, currently + selected thread is taken. Use the thread-index 'all' to start + tracing the whole process + + + + 2) processor-trace stop [<thread-index>] + + Stop Intel(R) Processor Trace on a specific thread or on the whole process + + Syntax: processor-trace stop <cmd-options> + + cmd-options Usage: + processor-trace stop [<thread-index>] + + <thread-index> + thread index of the thread. If no threads are specified, currently + selected thread is taken. Use the thread-index 'all' to stop + tracing the whole process + + + + 3) processor-trace show-trace-options [<thread-index>] + + Display all the information regarding Intel(R) Processor Trace for a specific + thread or for the whole process. The information contains trace buffer + size and configuration options of Intel(R) Processor Trace. + + Syntax: processor-trace show-trace-options <cmd-options> + + cmd-options Usage: + processor-trace show-trace-options [<thread-index>] + + <thread-index> + thread index of the thread. If no threads are specified, currently + selected thread is taken. Use the thread-index 'all' to display + information for all threads of the process + + + + 4) processor-trace show-instr-log [-o <offset>] [-c <count>] [<thread-index>] + + Display a log of assembly instructions executed for a specific thread or + for the whole process. The length of the log to be displayed and the + offset in the whole instruction log from where the log needs to be + displayed can also be provided. The offset is counted from the end of this + whole instruction log which means the last executed instruction is at + offset 0 (zero). + + Syntax: processor-trace show-instr-log <cmd-options> + + cmd-options Usage: + processor-trace show-instr-log [-o <offset>] [-c <count>] [<thread-index>] + + -c <count> + number of instructions to be displayed. If not specified then a + default value (=10) will be taken + + -o <offset> + offset in the whole instruction log from where the log will be + displayed. If not specified then default value is calculated as + offset = count -1 + + <thread-index> + thread index of the thread. If no threads are specified, currently + selected thread is taken. Use the thread-index 'all' to show + instruction log for all the threads of the process diff --git a/tools/intel-features/intel-pt/README_TOOL.txt b/tools/intel-features/intel-pt/README_TOOL.txt new file mode 100644 index 000000000000..d1ec1caf73ca --- /dev/null +++ b/tools/intel-features/intel-pt/README_TOOL.txt @@ -0,0 +1,311 @@ +******************************************************************************* +* README * +* * +* This file provides all the information regarding Intel(R) Processor Trace * +* Tool. It consists explanation about how Tool internally works, its hardware * +* and software dependencies, build procedure and usage of the API. * +******************************************************************************* + + + +============ +Introduction +============ +The Intel(R) Processor Trace Tool is developed on top of LLDB and provides its +its users execution trace of the debugged applications. Tool makes use of +Intel(R) Processor Trace hardware feature implementation inside LLDB for this +purpose. This hardware feature generates a set of trace packets that +encapsulates program flow information. These trace packets along with the binary +of the application can be decoded with the help of a software decoder to +construct the execution trace of the application. + +More information about Intel(R) Processor Trace feature can be obtained from +website: https://software.intel.com/en-us/blogs/2013/09/18/processor-tracing + + + + +========= +Details +========= +The functionality of the Tool consists three parts: + +1. Raw Trace Collection from LLDB + With the help of API of this Tool (given below), Intel(R) Processor Trace + can be started on the application being debugged with LLDB. The generated + trace of the application is gathered inside LLDB and is collected by the + Tool from LLDB through LLDB's public API. + +2. Raw Trace Decoding + For decoding the raw trace data, the Tool makes use of "libipt", an + Intel(R) Processor Trace Decoder Library. The library needs binary of + the application and information about the cpu on which the application is + running in order to decode the raw trace. The Tool gathers this + information from LLDB public API and provide it to "libipt". More + information about "libipt" can be found at: + https://software.intel.com/en-us/blogs/2013/09/18/processor-tracing and + https://github.com/01org/processor-trace + +3. Decoded Trace Post-processing + The decoded trace is post-processed to reconstruct the execution flow of + the application. The execution flow contains the list of assembly + instructions (called instruction log hereafter). + + + + +============= +Dependencies +============= +The Tool has following hardware and software dependencies: + + - Hardware dependency: The Tool makes use of this hardware feature to capture + raw trace of an application from LLDB. This hardware feature may not be + present in all processors. The hardware feature is supported on Broadwell + and other succeeding CPUs such as Skylake etc. In order for Tool to provide + something meaningful, the target machine on which the application is running + should have this feature. + + - Software dependency: The Tool has an indirect dependency on the Operating + System level software support for Intel(R) Processor Trace on the target + machine where the application is running and being debugged by LLDB. This + support is required to enable raw trace generation on the target machine. + Currently, the Tool works for applications running on Linux OS as till now + the Operating System level support for the feature is present only in Linux + (more specifically starting from the 4.1 kernel). In Linux, this feature is + implemented in perf_events subsystem and is usable through perf_event_open + system call. In the User space level, the Tool has a direct dependency on + "libipt" to decode the captured raw trace. This library might be + pre-installed on host systems. If not then the library can be built from + its sources (available at): https://github.com/01org/processor-trace + + + + +============ +How to Build +============ +The Tool has a cmake based build and can be built by specifying some extra flags +while building LLDB with cmake. The following cmake flags need to be provided to +build the Tool: + + - LIBIPT_INCLUDE_PATH - The flag specifies the directory where the header + file of "libipt" resides. If the library is not pre-installed on the host + system and is built directly from "libipt" project sources then this file + may either come as a part of the sources itself or will be generated in + build folder while building library. + + - LIBIPT_LIBRARY_PATH - The flag points to the location of "libipt" shared + library. + +The Tool currently works successfully with following versions of this library: + - v1.4, v1.5, v1.6 + + + +============ +How to Use +============ +The Tool's API are exposed as a C++ object oriented interface (file PTDecoder.h) +in a shared library. The main class that implements the whole functionality is +PTDecoder. This class makes use of 3 other classes, + - PTInstruction to represent an assembly instruction + - PTInstructionList to return instruction log + - PTTraceOptions to return trace specific information +The users can use these API to develop their own products. All API are also +available as python functions through a script bridging interface, allowing +them to be used directly from python either interactively or to build python +apps. + +Currently, cli wrapper has been developed on top of the Tool to use it through +LLDB's command line. Please refer to README_CLI.txt file for command line usage. + + +A brief introduction about the classes and their API are given below. + + class PTDecoder + =============== + This class makes use of Intel(R) Processor Trace hardware feature + (implemented inside LLDB) to gather trace data for an inferior (being + debugged with LLDB) to provide meaningful information out of it. Currently + the meaningful information comprises of the execution flow of the inferior + (in terms of assembly instructions executed). The class enables user to: + + - start the trace with configuration options for a thread/process, + - stop the trace for a thread/process, + - get the execution flow (assembly instructions) for a thread and + - get trace specific information for a thread + + Corresponding API are explained below: + a) void StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror) + ------------------------------------------------------------------------ + This API allows the user to start trace on a particular thread or on + the whole process with Intel(R) Processor Trace specific + configuration options. + + @param[in] sbprocess : A valid process on which this operation + will be performed. An error is returned in case of an invalid + process. + + @param[out] sberror : An error with the failure reason if API + fails. Else success. + + @param[in] sbtraceoptions : Contains thread id information and + configuration options: + For tracing a single thread, provide a valid thread id. If + sbprocess doesn't contain this thread id, error will be returned. + For tracing complete process, set to lldb::LLDB_INVALID_THREAD_ID + Configuration options comprises of: + - trace buffer size, meta data buffer size, TraceType and + - All other possible Intel(R) Processor Trace specific + configuration options (hereafter collectively referred as + CUSTOM_OPTIONS) + + Trace buffer, meant to store the trace data read from target + machine, inside LLDB is configured as a cyclic buffer. Hence, + depending upon the trace buffer size provided here, buffer + overwrites may happen while LLDB writes trace data into it. + CUSTOM_OPTIONS are formatted as json text i.e. {"Name":Value, + "Name":Value,...} inside sbtraceoptions, where "Value" should be + a 64-bit unsigned integer in hex format. For information + regarding what all configuration options are currently supported + by LLDB and detailed information about CUSTOM_OPTIONS usage, + please refer to SBProcess::StartTrace() API description. An + overview of some of the various CUSTOM_OPTIONS are briefly given + below. Please refer to "Intel(R) 64 and IA-32 Architectures + Software Developer's Manual" for more details about them. + - CYCEn Enable/Disable Cycle Count Packet (CYC) Packet + - OS Packet generation enabled/disabled if + Current Privilege Level (CPL)=0 + - User Packet generation enabled/disabled if CPL>0 + - CR3Filter Enable/Disable CR3 Filtering + - MTCEn Enable/disable MTC packets + - TSCEn Enable/disable TSC packets + - DisRETC Enable/disable RET Compression + - BranchEn Enable/disable COFI-based packets + - MTCFreq Defines MTC Packet Frequency + - CycThresh CYC Packet threshold + - PSBFreq Frequency of PSB Packets + + TraceType should be set to + lldb::TraceType::eTraceTypeProcessorTrace, else error is + returned. To find out any other requirement to start tracing + successfully, refer to SBProcess::StartTrace() API description. + LLDB's current implementation of Intel(R) Processor Trace + feature may round off invalid values for configuration options. + Therefore, the configuration options with which the trace was + actually started, might be different to the ones with which + trace was asked to be started by user. The actual used + configuration options can be obtained from + GetProcessorTraceInfo() API. + + + + b) void StopProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBError &sberror, + lldb::tid_t tid = LLDB_INVALID_THREAD_ID) + ------------------------------------------------------------------------ + This API allows the user to Stop trace on a particular thread or on + the whole process. + + @param[in] sbprocess : A valid process on which this operation will + be performed. An error is returned in case of an invalid process. + + @param[in] tid : To stop tracing a single thread, provide a + valid thread id. If sbprocess doesn't contain the thread tid, + error will be returned. To stop tracing complete process, use + lldb::LLDB_INVALID_THREAD_ID + + @param[out] sberror : An error with the failure reason if API fails. + Else success + + + + c) void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid, + uint32_t offset, uint32_t count, + PTInstructionList &result_list, + lldb::SBError &sberror) + ------------------------------------------------------------------------ + This API provides instruction log that contains the execution flow + for a thread of a process in terms of assembly instruction executed. + The API works on only 1 thread at a time. To gather this information + for whole process, this API needs to be called for each thread. + + @param[in] sbprocess : A valid process on which this operation + will be performed. An error is returned in case of an invalid + process. + + @param[in] tid : A valid thread id of the thread for which + instruction log is desired. If sbprocess doesn't contain the + thread tid, error will be returned. + + @param[in] count : Number of instructions requested by the + user to be returned from the complete instruction log. Complete + instruction log refers to all the assembly instructions obtained + after decoding the complete raw trace data obtained from LLDB. + The length of the complete instruction log is dependent on the + trace buffer size with which processor tracing was started for + this thread. + The number of instructions actually returned are dependent on + 'count' and 'offset' parameters of this API. + + @param[in] offset : The offset in the complete instruction log + from where 'count' number of instructions are requested by the + user. offset is counted from the end of of this complete + instruction log (which means the last executed instruction + is at offset 0 (zero)). + + @param[out] result_list : Depending upon 'count' and 'offset' values, + list will be overwritten with the instructions. + + @param[out] sberror : An error with the failure reason if API + fails. Else success + + + + d) void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + PTTraceOptions &options, lldb::SBError &sberror) + ------------------------------------------------------------------------ + This API provides Intel(R) Processor Trace specific information for + a thread of a process. The API works on only 1 thread at a time. To + gather this information for whole process, this API needs to be + called for each thread. The information contains the actual + configuration options with which the trace was started for this + thread. + + @param[in] sbprocess : The valid process on which this operation + will be performed. An error is returned in case of an invalid + process. + + @param[in] tid : A valid thread id of the thread for which the + trace specific information is required. If sbprocess doesn't + contain the thread tid, an error will be returned. + + @param[out] options : Contains actual configuration options (they + may be different to the ones with which tracing was asked to be + started for this thread during StartProcessorTrace() API call). + + @param[out] sberror : An error with the failure reason if API + fails. Else success + + + class PTInstruction + =================== + This class represents an assembly instruction containing raw instruction + bytes, instruction address along with execution flow context and + Intel(R) Processor Trace context. For more details, please refer to + PTDecoder.h file. + + class PTInstructionList + ======================= + This class represents a list of assembly instructions. Each assembly + instruction is of type PTInstruction. + + class PTTraceOptions + ==================== + This class provides Intel(R) Processor Trace specific configuration + options like trace type, trace buffer size, meta data buffer size along + with other trace specific options. For more details, please refer to + PTDecoder.h file. diff --git a/tools/intel-features/intel-pt/cli-wrapper-pt.cpp b/tools/intel-features/intel-pt/cli-wrapper-pt.cpp new file mode 100644 index 000000000000..1fa236cb0bc7 --- /dev/null +++ b/tools/intel-features/intel-pt/cli-wrapper-pt.cpp @@ -0,0 +1,583 @@ +//===-- cli-wrapper-pt.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// CLI Wrapper of PTDecoder Tool to enable it to be used through LLDB's CLI. The +// wrapper provides a new command called processor-trace with 4 child +// subcommands as follows: +// processor-trace start +// processor-trace stop +// processor-trace show-trace-options +// processor-trace show-instr-log +// +//===----------------------------------------------------------------------===// + +#include <cerrno> +#include <cinttypes> +#include <cstring> +#include <string> +#include <vector> + +#include "PTDecoder.h" +#include "cli-wrapper-pt.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStructuredData.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" + +static bool GetProcess(lldb::SBDebugger &debugger, + lldb::SBCommandReturnObject &result, + lldb::SBProcess &process) { + if (!debugger.IsValid()) { + result.Printf("error: invalid debugger\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + lldb::SBTarget target = debugger.GetSelectedTarget(); + if (!target.IsValid()) { + result.Printf("error: invalid target inside debugger\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + process = target.GetProcess(); + if (!process.IsValid() || + (process.GetState() == lldb::StateType::eStateDetached) || + (process.GetState() == lldb::StateType::eStateExited) || + (process.GetState() == lldb::StateType::eStateInvalid)) { + result.Printf("error: invalid process inside debugger's target\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + return true; +} + +static bool ParseCommandOption(char **command, + lldb::SBCommandReturnObject &result, + uint32_t &index, const std::string &arg, + uint32_t &parsed_result) { + char *endptr; + if (!command[++index]) { + result.Printf("error: option \"%s\" requires an argument\n", arg.c_str()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + errno = 0; + unsigned long output = strtoul(command[index], &endptr, 0); + if ((errno != 0) || (*endptr != '\0')) { + result.Printf("error: invalid value \"%s\" provided for option \"%s\"\n", + command[index], arg.c_str()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + if (output > UINT32_MAX) { + result.Printf("error: value \"%s\" for option \"%s\" exceeds UINT32_MAX\n", + command[index], arg.c_str()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + parsed_result = (uint32_t)output; + return true; +} + +static bool ParseCommandArgThread(char **command, + lldb::SBCommandReturnObject &result, + lldb::SBProcess &process, uint32_t &index, + lldb::tid_t &thread_id) { + char *endptr; + if (!strcmp(command[index], "all")) + thread_id = LLDB_INVALID_THREAD_ID; + else { + uint32_t thread_index_id; + errno = 0; + unsigned long output = strtoul(command[index], &endptr, 0); + if ((errno != 0) || (*endptr != '\0') || (output > UINT32_MAX)) { + result.Printf("error: invalid thread specification: \"%s\"\n", + command[index]); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_index_id = (uint32_t)output; + + lldb::SBThread thread = process.GetThreadByIndexID(thread_index_id); + if (!thread.IsValid()) { + result.Printf( + "error: process has no thread with thread specification: \"%s\"\n", + command[index]); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + return true; +} + +class ProcessorTraceStart : public lldb::SBCommandPluginInterface { +public: + ProcessorTraceStart(std::shared_ptr<ptdecoder::PTDecoder> &pt_decoder) + : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {} + + ~ProcessorTraceStart() {} + + virtual bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + lldb::SBProcess process; + lldb::SBThread thread; + if (!GetProcess(debugger, result, process)) + return false; + + // Default initialize API's arguments + lldb::SBTraceOptions lldb_SBTraceOptions; + uint32_t trace_buffer_size = m_default_trace_buff_size; + lldb::tid_t thread_id; + + // Parse Command line options + bool thread_argument_provided = false; + if (command) { + for (uint32_t i = 0; command[i]; i++) { + if (!strcmp(command[i], "-b")) { + if (!ParseCommandOption(command, result, i, "-b", trace_buffer_size)) + return false; + } else { + thread_argument_provided = true; + if (!ParseCommandArgThread(command, result, process, i, thread_id)) + return false; + } + } + } + + if (!thread_argument_provided) { + thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + result.Printf("error: invalid current selected thread\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + + if (trace_buffer_size > m_max_trace_buff_size) + trace_buffer_size = m_max_trace_buff_size; + + // Set API's arguments with parsed values + lldb_SBTraceOptions.setType(lldb::TraceType::eTraceTypeProcessorTrace); + lldb_SBTraceOptions.setTraceBufferSize(trace_buffer_size); + lldb_SBTraceOptions.setMetaDataBufferSize(0); + lldb_SBTraceOptions.setThreadID(thread_id); + lldb::SBStream sb_stream; + sb_stream.Printf("{\"trace-tech\":\"intel-pt\"}"); + lldb::SBStructuredData custom_params; + lldb::SBError error = custom_params.SetFromJSON(sb_stream); + if (!error.Success()) { + result.Printf("error: %s\n", error.GetCString()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + lldb_SBTraceOptions.setTraceParams(custom_params); + + // Start trace + pt_decoder_sp->StartProcessorTrace(process, lldb_SBTraceOptions, error); + if (!error.Success()) { + result.Printf("error: %s\n", error.GetCString()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + return true; + } + +private: + std::shared_ptr<ptdecoder::PTDecoder> pt_decoder_sp; + const uint32_t m_max_trace_buff_size = 0x3fff; + const uint32_t m_default_trace_buff_size = 4096; +}; + +class ProcessorTraceInfo : public lldb::SBCommandPluginInterface { +public: + ProcessorTraceInfo(std::shared_ptr<ptdecoder::PTDecoder> &pt_decoder) + : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {} + + ~ProcessorTraceInfo() {} + + virtual bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + lldb::SBProcess process; + lldb::SBThread thread; + if (!GetProcess(debugger, result, process)) + return false; + + lldb::tid_t thread_id; + + // Parse command line options + bool thread_argument_provided = false; + if (command) { + for (uint32_t i = 0; command[i]; i++) { + thread_argument_provided = true; + if (!ParseCommandArgThread(command, result, process, i, thread_id)) + return false; + } + } + + if (!thread_argument_provided) { + thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + result.Printf("error: invalid current selected thread\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + + size_t loop_count = 1; + bool entire_process_tracing = false; + if (thread_id == LLDB_INVALID_THREAD_ID) { + entire_process_tracing = true; + loop_count = process.GetNumThreads(); + } + + // Get trace information + lldb::SBError error; + lldb::SBCommandReturnObject res; + for (size_t i = 0; i < loop_count; i++) { + error.Clear(); + res.Clear(); + + if (entire_process_tracing) + thread = process.GetThreadAtIndex(i); + else + thread = process.GetThreadByID(thread_id); + thread_id = thread.GetThreadID(); + + ptdecoder::PTTraceOptions options; + pt_decoder_sp->GetProcessorTraceInfo(process, thread_id, options, error); + if (!error.Success()) { + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s", + thread.GetIndexID(), thread_id, error.GetCString()); + result.AppendMessage(res.GetOutput()); + continue; + } + + lldb::SBStructuredData data = options.GetTraceParams(error); + if (!error.Success()) { + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s", + thread.GetIndexID(), thread_id, error.GetCString()); + result.AppendMessage(res.GetOutput()); + continue; + } + + lldb::SBStream s; + error = data.GetAsJSON(s); + if (!error.Success()) { + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s", + thread.GetIndexID(), thread_id, error.GetCString()); + result.AppendMessage(res.GetOutput()); + continue; + } + + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 + ", trace buffer size=%" PRIu64 ", meta buffer size=%" PRIu64 + ", trace type=%" PRIu32 ", custom trace params=%s", + thread.GetIndexID(), thread_id, options.GetTraceBufferSize(), + options.GetMetaDataBufferSize(), options.GetType(), + s.GetData()); + result.AppendMessage(res.GetOutput()); + } + return true; + } + +private: + std::shared_ptr<ptdecoder::PTDecoder> pt_decoder_sp; +}; + +class ProcessorTraceShowInstrLog : public lldb::SBCommandPluginInterface { +public: + ProcessorTraceShowInstrLog(std::shared_ptr<ptdecoder::PTDecoder> &pt_decoder) + : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {} + + ~ProcessorTraceShowInstrLog() {} + + virtual bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + lldb::SBProcess process; + lldb::SBThread thread; + if (!GetProcess(debugger, result, process)) + return false; + + // Default initialize API's arguments + uint32_t offset; + bool offset_provided = false; + uint32_t count = m_default_count; + lldb::tid_t thread_id; + + // Parse command line options + bool thread_argument_provided = false; + if (command) { + for (uint32_t i = 0; command[i]; i++) { + if (!strcmp(command[i], "-o")) { + if (!ParseCommandOption(command, result, i, "-o", offset)) + return false; + offset_provided = true; + } else if (!strcmp(command[i], "-c")) { + if (!ParseCommandOption(command, result, i, "-c", count)) + return false; + } else { + thread_argument_provided = true; + if (!ParseCommandArgThread(command, result, process, i, thread_id)) + return false; + } + } + } + + if (!thread_argument_provided) { + thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + result.Printf("error: invalid current selected thread\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + + size_t loop_count = 1; + bool entire_process_tracing = false; + if (thread_id == LLDB_INVALID_THREAD_ID) { + entire_process_tracing = true; + loop_count = process.GetNumThreads(); + } + + // Get instruction log and disassemble it + lldb::SBError error; + lldb::SBCommandReturnObject res; + for (size_t i = 0; i < loop_count; i++) { + error.Clear(); + res.Clear(); + + if (entire_process_tracing) + thread = process.GetThreadAtIndex(i); + else + thread = process.GetThreadByID(thread_id); + thread_id = thread.GetThreadID(); + + // If offset is not provided then calculate a default offset (to display + // last 'count' number of instructions) + if (!offset_provided) + offset = count - 1; + + // Get the instruction log + ptdecoder::PTInstructionList insn_list; + pt_decoder_sp->GetInstructionLogAtOffset(process, thread_id, offset, + count, insn_list, error); + if (!error.Success()) { + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s", + thread.GetIndexID(), thread_id, error.GetCString()); + result.AppendMessage(res.GetOutput()); + continue; + } + + // Disassemble the instruction log + std::string disassembler_command("dis -c 1 -s "); + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 "\n", thread.GetIndexID(), + thread_id); + lldb::SBCommandInterpreter sb_cmnd_interpreter( + debugger.GetCommandInterpreter()); + lldb::SBCommandReturnObject result_obj; + for (size_t i = 0; i < insn_list.GetSize(); i++) { + ptdecoder::PTInstruction insn = insn_list.GetInstructionAtIndex(i); + uint64_t addr = insn.GetInsnAddress(); + std::string error = insn.GetError(); + if (!error.empty()) { + res.AppendMessage(error.c_str()); + continue; + } + + result_obj.Clear(); + std::string complete_disassembler_command = + disassembler_command + std::to_string(addr); + sb_cmnd_interpreter.HandleCommand(complete_disassembler_command.c_str(), + result_obj, false); + std::string result_str(result_obj.GetOutput()); + if (result_str.empty()) { + lldb::SBCommandReturnObject output; + output.Printf(" Disassembly not found for address: %" PRIu64, addr); + res.AppendMessage(output.GetOutput()); + continue; + } + + // LLDB's disassemble command displays assembly instructions along with + // the names of the functions they belong to. Parse this result to + // display only the assembly instructions and not the function names + // in an instruction log + std::size_t first_new_line_index = result_str.find_first_of('\n'); + std::size_t last_new_line_index = result_str.find_last_of('\n'); + if (first_new_line_index != last_new_line_index) + res.AppendMessage((result_str.substr(first_new_line_index + 1, + last_new_line_index - + first_new_line_index - 1)) + .c_str()); + else + res.AppendMessage( + (result_str.substr(0, result_str.length() - 1)).c_str()); + } + result.AppendMessage(res.GetOutput()); + } + return true; + } + +private: + std::shared_ptr<ptdecoder::PTDecoder> pt_decoder_sp; + const uint32_t m_default_count = 10; +}; + +class ProcessorTraceStop : public lldb::SBCommandPluginInterface { +public: + ProcessorTraceStop(std::shared_ptr<ptdecoder::PTDecoder> &pt_decoder) + : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {} + + ~ProcessorTraceStop() {} + + virtual bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + lldb::SBProcess process; + lldb::SBThread thread; + if (!GetProcess(debugger, result, process)) + return false; + + lldb::tid_t thread_id; + + // Parse command line options + bool thread_argument_provided = false; + if (command) { + for (uint32_t i = 0; command[i]; i++) { + thread_argument_provided = true; + if (!ParseCommandArgThread(command, result, process, i, thread_id)) + return false; + } + } + + if (!thread_argument_provided) { + thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + result.Printf("error: invalid current selected thread\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + + // Stop trace + lldb::SBError error; + pt_decoder_sp->StopProcessorTrace(process, error, thread_id); + if (!error.Success()) { + result.Printf("error: %s\n", error.GetCString()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + return true; + } + +private: + std::shared_ptr<ptdecoder::PTDecoder> pt_decoder_sp; +}; + +bool PTPluginInitialize(lldb::SBDebugger &debugger) { + lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); + lldb::SBCommand proc_trace = interpreter.AddMultiwordCommand( + "processor-trace", "Intel(R) Processor Trace for thread/process"); + + std::shared_ptr<ptdecoder::PTDecoder> PTDecoderSP( + new ptdecoder::PTDecoder(debugger)); + + lldb::SBCommandPluginInterface *proc_trace_start = + new ProcessorTraceStart(PTDecoderSP); + const char *help_proc_trace_start = "start Intel(R) Processor Trace on a " + "specific thread or on the whole process"; + const char *syntax_proc_trace_start = + "processor-trace start <cmd-options>\n\n" + "\rcmd-options Usage:\n" + "\r processor-trace start [-b <buffer-size>] [<thread-index>]\n\n" + "\t\b-b <buffer-size>\n" + "\t size of the trace buffer to store the trace data. If not " + "specified then a default value will be taken\n\n" + "\t\b<thread-index>\n" + "\t thread index of the thread. If no threads are specified, " + "currently selected thread is taken.\n" + "\t Use the thread-index 'all' to start tracing the whole process\n"; + proc_trace.AddCommand("start", proc_trace_start, help_proc_trace_start, + syntax_proc_trace_start); + + lldb::SBCommandPluginInterface *proc_trace_stop = + new ProcessorTraceStop(PTDecoderSP); + const char *help_proc_trace_stop = + "stop Intel(R) Processor Trace on a specific thread or on whole process"; + const char *syntax_proc_trace_stop = + "processor-trace stop <cmd-options>\n\n" + "\rcmd-options Usage:\n" + "\r processor-trace stop [<thread-index>]\n\n" + "\t\b<thread-index>\n" + "\t thread index of the thread. If no threads are specified, " + "currently selected thread is taken.\n" + "\t Use the thread-index 'all' to stop tracing the whole process\n"; + proc_trace.AddCommand("stop", proc_trace_stop, help_proc_trace_stop, + syntax_proc_trace_stop); + + lldb::SBCommandPluginInterface *proc_trace_show_instr_log = + new ProcessorTraceShowInstrLog(PTDecoderSP); + const char *help_proc_trace_show_instr_log = + "display a log of assembly instructions executed for a specific thread " + "or for the whole process.\n" + "The length of the log to be displayed and the offset in the whole " + "instruction log from where the log needs to be displayed can also be " + "provided. The offset is counted from the end of this whole " + "instruction log which means the last executed instruction is at offset " + "0 (zero)"; + const char *syntax_proc_trace_show_instr_log = + "processor-trace show-instr-log <cmd-options>\n\n" + "\rcmd-options Usage:\n" + "\r processor-trace show-instr-log [-o <offset>] [-c <count>] " + "[<thread-index>]\n\n" + "\t\b-o <offset>\n" + "\t offset in the whole instruction log from where the log will be " + "displayed. If not specified then a default value will be taken\n\n" + "\t\b-c <count>\n" + "\t number of instructions to be displayed. If not specified then a " + "default value will be taken\n\n" + "\t\b<thread-index>\n" + "\t thread index of the thread. If no threads are specified, " + "currently selected thread is taken.\n" + "\t Use the thread-index 'all' to show instruction log for all the " + "threads of the process\n"; + proc_trace.AddCommand("show-instr-log", proc_trace_show_instr_log, + help_proc_trace_show_instr_log, + syntax_proc_trace_show_instr_log); + + lldb::SBCommandPluginInterface *proc_trace_options = + new ProcessorTraceInfo(PTDecoderSP); + const char *help_proc_trace_show_options = + "display all the information regarding Intel(R) Processor Trace for a " + "specific thread or for the whole process.\n" + "The information contains trace buffer size and configuration options" + " of Intel(R) Processor Trace."; + const char *syntax_proc_trace_show_options = + "processor-trace show-options <cmd-options>\n\n" + "\rcmd-options Usage:\n" + "\r processor-trace show-options [<thread-index>]\n\n" + "\t\b<thread-index>\n" + "\t thread index of the thread. If no threads are specified, " + "currently selected thread is taken.\n" + "\t Use the thread-index 'all' to display information for all threads " + "of the process\n"; + proc_trace.AddCommand("show-trace-options", proc_trace_options, + help_proc_trace_show_options, + syntax_proc_trace_show_options); + + return true; +} diff --git a/tools/intel-features/intel-pt/cli-wrapper-pt.h b/tools/intel-features/intel-pt/cli-wrapper-pt.h new file mode 100644 index 000000000000..e2a0ee47b579 --- /dev/null +++ b/tools/intel-features/intel-pt/cli-wrapper-pt.h @@ -0,0 +1,13 @@ +//===-- cli-wrapper-pt.h----------------------------------*- C++ -*-==========// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// CLI Wrapper of PTDecoder Tool to enable it to be used through LLDB's CLI. +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBDebugger.h" + +bool PTPluginInitialize(lldb::SBDebugger &debugger); diff --git a/tools/intel-features/intel-pt/interface/PTDecoder.i b/tools/intel-features/intel-pt/interface/PTDecoder.i new file mode 100644 index 000000000000..f662b8ff3599 --- /dev/null +++ b/tools/intel-features/intel-pt/interface/PTDecoder.i @@ -0,0 +1,10 @@ +%include "stdint.i" + +%include "lldb/lldb-defines.h" +%include "lldb/lldb-enumerations.h" +%include "lldb/lldb-forward.h" +%include "lldb/lldb-types.h" + +%include "lldb/API/SBDefines.h" + +%include "../PTDecoder.h" diff --git a/tools/intel-features/scripts/CMakeLists.txt b/tools/intel-features/scripts/CMakeLists.txt new file mode 100644 index 000000000000..6df97a4b006d --- /dev/null +++ b/tools/intel-features/scripts/CMakeLists.txt @@ -0,0 +1,37 @@ +file(GLOB_RECURSE SWIG_SOURCES *.swig) + +set(FLAGS + -c++ + -shadow + -python + -D__STDC_LIMIT_MACROS + -D__STDC_CONSTANT_MACROS + ) + +set(INCLUDES + -I${LLDB_SOURCE_DIR}/include + -I${LLDB_SOURCE_DIR}/tools/intel-features/intel-pt + ) + +set(OUTPUT_PYTHON_WRAPPER + ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp + ) + +set(OUTPUT_PYTHON_SCRIPT_DIR + ${CMAKE_CURRENT_BINARY_DIR} + ) + +find_package(SWIG REQUIRED) +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lldbIntelFeatures.py + DEPENDS ${SWIG_SOURCES} + COMMAND ${SWIG_EXECUTABLE} ${FLAGS} ${INCLUDES} -o ${OUTPUT_PYTHON_WRAPPER} -outdir ${OUTPUT_PYTHON_SCRIPT_DIR} ${SWIG_SOURCES} + COMMENT "Generating python wrapper for features library") + +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp PROPERTIES GENERATED 1) +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/lldbIntelFeatures.py PROPERTIES GENERATED 1) + +add_custom_target(intel-features-swig_wrapper ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp + ) diff --git a/tools/intel-features/scripts/lldb-intel-features.swig b/tools/intel-features/scripts/lldb-intel-features.swig new file mode 100644 index 000000000000..c035fb6132dd --- /dev/null +++ b/tools/intel-features/scripts/lldb-intel-features.swig @@ -0,0 +1,16 @@ +%module lldbIntelFeatures + +%{ +#include "lldb/lldb-public.h" +#include "intel-pt/PTDecoder.h" +using namespace ptdecoder; +%} + +/* Undefine GCC keyword to make Swig happy when processing glibc's stdint.h */ +#define __extension__ + +/* Combined python typemap for all features */ +%include "python-typemaps.txt" + +/* Feature specific python interface files*/ +%include "../intel-pt/interface/PTDecoder.i" diff --git a/tools/intel-features/scripts/python-typemaps.txt b/tools/intel-features/scripts/python-typemaps.txt new file mode 100644 index 000000000000..888d5321393f --- /dev/null +++ b/tools/intel-features/scripts/python-typemaps.txt @@ -0,0 +1,31 @@ +/* Typemap definitions to allow SWIG to properly handle some data types */ + +// typemap for an incoming buffer +%typemap(in) (void *buf, size_t size) { + if (PyInt_Check($input)) { + $2 = PyInt_AsLong($input); + } else if (PyLong_Check($input)) { + $2 = PyLong_AsLong($input); + } else { + PyErr_SetString(PyExc_ValueError, "Expecting an integer or long object"); + return NULL; + } + if ($2 <= 0) { + PyErr_SetString(PyExc_ValueError, "Positive integer expected"); + return NULL; + } + $1 = (void *) malloc($2); +} + +// Return the buffer. Discarding any previous return result +%typemap(argout) (void *buf, size_t size) { + Py_XDECREF($result); /* Blow away any previous result */ + if (result == 0) { + $result = Py_None; + Py_INCREF($result); + } else { + PyObject *py_bytes = PyBytes_FromStringAndSize(reinterpret_cast<const char*>($1), result); + $result = py_bytes; + } + free($1); +} diff --git a/tools/intel-mpx/CMakeLists.txt b/tools/intel-mpx/CMakeLists.txt deleted file mode 100644 index 29ba9a1cacec..000000000000 --- a/tools/intel-mpx/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -if (NOT CMAKE_SYSTEM_NAME MATCHES "Linux") - return () -endif () - -include(${LLDB_PROJECT_ROOT}/cmake/LLDBDependencies.cmake) - -add_library(lldb-intel-mpxtable SHARED - IntelMPXTablePlugin.cpp - ) - -target_link_libraries(lldb-intel-mpxtable - PUBLIC liblldb LLVMSupport) - -install(TARGETS lldb-intel-mpxtable - LIBRARY DESTINATION bin) diff --git a/tools/lldb-mi/MICmdCmdVar.cpp b/tools/lldb-mi/MICmdCmdVar.cpp index 66b392be6bed..85f14ec29b93 100644 --- a/tools/lldb-mi/MICmdCmdVar.cpp +++ b/tools/lldb-mi/MICmdCmdVar.cpp @@ -509,19 +509,19 @@ bool CMICmdCmdVarUpdate::ExamineSBValueForChange(lldb::SBValue &vrwValue, return MIstatus::success; } - lldb::SBType valueType = vrwValue.GetType(); - const MIuint nChildren = vrwValue.GetNumChildren(); for (MIuint i = 0; i < nChildren; ++i) { lldb::SBValue member = vrwValue.GetChildAtIndex(i); if (!member.IsValid()) continue; - if (member.GetValueDidChange()) { - vrwbChanged = true; - return MIstatus::success; - } else if (ExamineSBValueForChange(member, vrwbChanged) && vrwbChanged) - // Handle composite types (i.e. struct or arrays) + // skip pointers and references to avoid infinite loop + if (member.GetType().GetTypeFlags() & + (lldb::eTypeIsPointer | lldb::eTypeIsReference)) + continue; + + // Handle composite types (i.e. struct or arrays) + if (ExamineSBValueForChange(member, vrwbChanged) && vrwbChanged) return MIstatus::success; } vrwbChanged = false; diff --git a/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp b/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp index a61244f92fc2..561fa91a0ee3 100644 --- a/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp +++ b/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp @@ -262,6 +262,10 @@ bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBBreakPoint( pEventType = "eBreakpointEventTypeIgnoreChanged"; bOk = HandleEventSBBreakpointCmn(vEvent); break; + case lldb::eBreakpointEventTypeAutoContinueChanged: + pEventType = "eBreakpointEventTypeAutoContinueChanged"; + bOk = HandleEventSBBreakpointCmn(vEvent); + break; } m_pLog->WriteLog(CMIUtilString::Format( "##### An SB Breakpoint event occurred: %s", pEventType)); diff --git a/tools/lldb-mi/MIDriverMain.cpp b/tools/lldb-mi/MIDriverMain.cpp index be01f1d97790..eda67782ad67 100644 --- a/tools/lldb-mi/MIDriverMain.cpp +++ b/tools/lldb-mi/MIDriverMain.cpp @@ -33,6 +33,7 @@ // Third party headers: #include "lldb/API/SBHostOS.h" +#include <atomic> #include <csignal> #include <stdio.h> @@ -72,14 +73,13 @@ void sigint_handler(int vSigno) { #ifdef _WIN32 // Restore handler as it is not persistent on Windows signal(SIGINT, sigint_handler); #endif - static bool g_interrupt_sent = false; + static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT; CMIDriverMgr &rDriverMgr = CMIDriverMgr::Instance(); lldb::SBDebugger *pDebugger = rDriverMgr.DriverGetTheDebugger(); if (pDebugger != nullptr) { - if (!g_interrupt_sent) { - g_interrupt_sent = true; + if (!g_interrupt_sent.test_and_set()) { pDebugger->DispatchInputInterrupt(); - g_interrupt_sent = false; + g_interrupt_sent.clear(); } } diff --git a/tools/lldb-server/CMakeLists.txt b/tools/lldb-server/CMakeLists.txt index f8c57cb9488f..b226adb2b570 100644 --- a/tools/lldb-server/CMakeLists.txt +++ b/tools/lldb-server/CMakeLists.txt @@ -24,41 +24,6 @@ endif () include_directories(../../source) -set(LLDB_SYSTEM_LIBS) -if (NOT LLDB_DISABLE_LIBEDIT) - list(APPEND LLDB_SYSTEM_LIBS edit) -endif() -if (NOT LLDB_DISABLE_CURSES) - list(APPEND LLDB_SYSTEM_LIBS ${CURSES_LIBRARIES}) - if(LLVM_ENABLE_TERMINFO AND HAVE_TERMINFO) - list(APPEND LLDB_SYSTEM_LIBS ${TERMINFO_LIBS}) - endif() -endif() - -if (NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB ) - list(APPEND LLDB_SYSTEM_LIBS atomic) -endif() - -# On FreeBSD/NetBSD backtrace() is provided by libexecinfo, not libc. -if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "NetBSD") - list(APPEND LLDB_SYSTEM_LIBS execinfo) -endif() - -if (NOT LLDB_DISABLE_PYTHON AND NOT LLVM_BUILD_STATIC) - list(APPEND LLDB_SYSTEM_LIBS ${PYTHON_LIBRARIES}) -endif() - -list(APPEND LLDB_SYSTEM_LIBS ${system_libs}) - -if (LLVM_BUILD_STATIC) - if (NOT LLDB_DISABLE_PYTHON) - list(APPEND LLDB_SYSTEM_LIBS python2.7 util) - endif() - if (NOT LLDB_DISABLE_CURSES) - list(APPEND LLDB_SYSTEM_LIBS gpm) - endif() -endif() - set(LLDB_PLUGINS) if(CMAKE_SYSTEM_NAME MATCHES "Linux|Android") @@ -89,4 +54,4 @@ add_lldb_tool(lldb-server INCLUDE_IN_FRAMEWORK Support ) -target_link_libraries(lldb-server ${LLDB_SYSTEM_LIBS}) +target_link_libraries(lldb-server PRIVATE ${LLDB_SYSTEM_LIBS}) diff --git a/tools/lldb-server/lldb-gdbserver.cpp b/tools/lldb-server/lldb-gdbserver.cpp index f1a9b113c8ee..810cb92e4ffc 100644 --- a/tools/lldb-server/lldb-gdbserver.cpp +++ b/tools/lldb-server/lldb-gdbserver.cpp @@ -106,6 +106,7 @@ static struct option g_long_options[] = { // than llgs listening for a connection from address on port. {"setsid", no_argument, NULL, 'S'}, // Call setsid() to make llgs run in its own session. + {"fd", required_argument, NULL, 'F'}, {NULL, 0, NULL, 0}}; //---------------------------------------------------------------------- @@ -132,13 +133,13 @@ static void display_usage(const char *progname, const char *subcommand) { "[--log-file log-file-name] " "[--log-channels log-channel-list] " "[--setsid] " + "[--fd file-descriptor]" "[--named-pipe named-pipe-path] " "[--native-regs] " "[--attach pid] " "[[HOST]:PORT] " "[-- PROGRAM ARG1 ARG2 ...]\n", progname, subcommand); - exit(0); } void handle_attach_to_pid(GDBRemoteCommunicationServerLLGS &gdb_server, @@ -176,27 +177,28 @@ void handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server, void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server, int argc, const char *const argv[]) { - Status error; - error = gdb_server.SetLaunchArguments(argv, argc); - if (error.Fail()) { - fprintf(stderr, "error: failed to set launch args for '%s': %s\n", argv[0], - error.AsCString()); + ProcessLaunchInfo info; + info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug | + eLaunchFlagDisableASLR); + info.SetArguments(const_cast<const char **>(argv), true); + + llvm::SmallString<64> cwd; + if (std::error_code ec = llvm::sys::fs::current_path(cwd)) { + llvm::errs() << "Error getting current directory: " << ec.message() << "\n"; exit(1); } + info.SetWorkingDirectory(FileSpec(cwd, true)); - unsigned int launch_flags = eLaunchFlagStopAtEntry | eLaunchFlagDebug; + StringList env; + Host::GetEnvironment(env); + info.GetEnvironmentEntries() = Args(env); - error = gdb_server.SetLaunchFlags(launch_flags); - if (error.Fail()) { - fprintf(stderr, "error: failed to set launch flags for '%s': %s\n", argv[0], - error.AsCString()); - exit(1); - } + gdb_server.SetLaunchInfo(info); - error = gdb_server.LaunchProcess(); + Status error = gdb_server.LaunchProcess(); if (error.Fail()) { - fprintf(stderr, "error: failed to launch '%s': %s\n", argv[0], - error.AsCString()); + llvm::errs() << llvm::formatv("error: failed to launch '{0}': {1}\n", + argv[0], error); exit(1); } } @@ -232,10 +234,34 @@ void ConnectToRemote(MainLoop &mainloop, GDBRemoteCommunicationServerLLGS &gdb_server, bool reverse_connect, const char *const host_and_port, const char *const progname, const char *const subcommand, - const char *const named_pipe_path, int unnamed_pipe_fd) { + const char *const named_pipe_path, int unnamed_pipe_fd, + int connection_fd) { Status error; - if (host_and_port && host_and_port[0]) { + std::unique_ptr<Connection> connection_up; + if (connection_fd != -1) { + // Build the connection string. + char connection_url[512]; + snprintf(connection_url, sizeof(connection_url), "fd://%d", connection_fd); + + // Create the connection. +#if !defined LLDB_DISABLE_POSIX && !defined _WIN32 + ::fcntl(connection_fd, F_SETFD, FD_CLOEXEC); +#endif + connection_up.reset(new ConnectionFileDescriptor); + auto connection_result = connection_up->Connect(connection_url, &error); + if (connection_result != eConnectionStatusSuccess) { + fprintf(stderr, "error: failed to connect to client at '%s' " + "(connection status: %d)\n", + connection_url, static_cast<int>(connection_result)); + exit(-1); + } + if (error.Fail()) { + fprintf(stderr, "error: failed to connect to client at '%s': %s\n", + connection_url, error.AsCString()); + exit(-1); + } + } else if (host_and_port && host_and_port[0]) { // Parse out host and port. std::string final_host_and_port; std::string connection_host; @@ -255,7 +281,6 @@ void ConnectToRemote(MainLoop &mainloop, connection_portno = StringConvert::ToUInt32(connection_port.c_str(), 0); } - std::unique_ptr<Connection> connection_up; if (reverse_connect) { // llgs will connect to the gdb-remote client. @@ -263,7 +288,7 @@ void ConnectToRemote(MainLoop &mainloop, // Ensure we have a port number for the connection. if (connection_portno == 0) { fprintf(stderr, "error: port number must be specified on when using " - "reverse connect"); + "reverse connect\n"); exit(1); } @@ -277,12 +302,12 @@ void ConnectToRemote(MainLoop &mainloop, auto connection_result = connection_up->Connect(connection_url, &error); if (connection_result != eConnectionStatusSuccess) { fprintf(stderr, "error: failed to connect to client at '%s' " - "(connection status: %d)", + "(connection status: %d)\n", connection_url, static_cast<int>(connection_result)); exit(-1); } if (error.Fail()) { - fprintf(stderr, "error: failed to connect to client at '%s': %s", + fprintf(stderr, "error: failed to connect to client at '%s': %s\n", connection_url, error.AsCString()); exit(-1); } @@ -290,7 +315,7 @@ void ConnectToRemote(MainLoop &mainloop, std::unique_ptr<Acceptor> acceptor_up( Acceptor::Create(final_host_and_port, false, error)); if (error.Fail()) { - fprintf(stderr, "failed to create acceptor: %s", error.AsCString()); + fprintf(stderr, "failed to create acceptor: %s\n", error.AsCString()); exit(1); } error = acceptor_up->Listen(1); @@ -304,7 +329,7 @@ void ConnectToRemote(MainLoop &mainloop, if (named_pipe_path && named_pipe_path[0]) { error = writeSocketIdToPipe(named_pipe_path, socket_id); if (error.Fail()) - fprintf(stderr, "failed to write to the named pipe \'%s\': %s", + fprintf(stderr, "failed to write to the named pipe \'%s\': %s\n", named_pipe_path, error.AsCString()); } // If we have an unnamed pipe to write the socket id back to, do that @@ -312,7 +337,7 @@ void ConnectToRemote(MainLoop &mainloop, else if (unnamed_pipe_fd >= 0) { error = writeSocketIdToPipe(unnamed_pipe_fd, socket_id); if (error.Fail()) - fprintf(stderr, "failed to write to the unnamed pipe: %s", + fprintf(stderr, "failed to write to the unnamed pipe: %s\n", error.AsCString()); } } else { @@ -328,14 +353,14 @@ void ConnectToRemote(MainLoop &mainloop, } connection_up.reset(conn); } - error = gdb_server.InitializeConnection(std::move(connection_up)); - if (error.Fail()) { - fprintf(stderr, "Failed to initialize connection: %s\n", - error.AsCString()); - exit(-1); - } - printf("Connection established.\n"); } + error = gdb_server.InitializeConnection(std::move(connection_up)); + if (error.Fail()) { + fprintf(stderr, "Failed to initialize connection: %s\n", + error.AsCString()); + exit(-1); + } + printf("Connection established.\n"); } //---------------------------------------------------------------------- @@ -364,6 +389,7 @@ int main_gdbserver(int argc, char *argv[]) { log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" int unnamed_pipe_fd = -1; bool reverse_connect = false; + int connection_fd = -1; // ProcessLaunchInfo launch_info; ProcessAttachInfo attach_info; @@ -413,6 +439,10 @@ int main_gdbserver(int argc, char *argv[]) { reverse_connect = true; break; + case 'F': + connection_fd = StringConvert::ToUInt32(optarg, -1); + break; + #ifndef _WIN32 case 'S': // Put llgs into a new session. Terminals group processes @@ -472,7 +502,8 @@ int main_gdbserver(int argc, char *argv[]) { argc -= optind; argv += optind; - if (argc == 0) { + if (argc == 0 && connection_fd == -1) { + fputs("No arguments\n", stderr); display_usage(progname, subcommand); exit(255); } @@ -501,7 +532,7 @@ int main_gdbserver(int argc, char *argv[]) { ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port, progname, subcommand, named_pipe_path.c_str(), - unnamed_pipe_fd); + unnamed_pipe_fd, connection_fd); if (!gdb_server.IsConnected()) { fprintf(stderr, "no connection information provided, unable to run\n"); diff --git a/tools/lldb-test/CMakeLists.txt b/tools/lldb-test/CMakeLists.txt new file mode 100644 index 000000000000..2ab1ceacdcd1 --- /dev/null +++ b/tools/lldb-test/CMakeLists.txt @@ -0,0 +1,27 @@ +get_property(LLDB_ALL_PLUGINS GLOBAL PROPERTY LLDB_PLUGINS) + +add_lldb_tool(lldb-test + FormatUtil.cpp + lldb-test.cpp + SystemInitializerTest.cpp + + LINK_LIBS + lldbBase + lldbBreakpoint + lldbCore + lldbDataFormatters + lldbExpression + lldbHost + lldbInitialization + lldbInterpreter + lldbSymbol + lldbTarget + lldbUtility + ${LLDB_ALL_PLUGINS} + ${host_lib} + + LINK_COMPONENTS + Support + ) + +include_directories(${LLDB_SOURCE_DIR}/source) diff --git a/tools/lldb-test/FormatUtil.cpp b/tools/lldb-test/FormatUtil.cpp new file mode 100644 index 000000000000..381cbd6e25b8 --- /dev/null +++ b/tools/lldb-test/FormatUtil.cpp @@ -0,0 +1,69 @@ +//===- FormatUtil.cpp ----------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FormatUtil.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace lldb_private; +using namespace llvm; + +LinePrinter::LinePrinter(int Indent, llvm::raw_ostream &Stream) + : OS(Stream), IndentSpaces(Indent), CurrentIndent(0) {} + +void LinePrinter::Indent(uint32_t Amount) { + if (Amount == 0) + Amount = IndentSpaces; + CurrentIndent += Amount; +} + +void LinePrinter::Unindent(uint32_t Amount) { + if (Amount == 0) + Amount = IndentSpaces; + CurrentIndent = std::max<int>(0, CurrentIndent - Amount); +} + +void LinePrinter::NewLine() { + OS << "\n"; + OS.indent(CurrentIndent); +} + +void LinePrinter::print(const Twine &T) { OS << T; } + +void LinePrinter::printLine(const Twine &T) { + NewLine(); + OS << T; +} + +void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, + uint32_t StartOffset) { + NewLine(); + OS << Label << " ("; + if (!Data.empty()) { + OS << "\n"; + OS << format_bytes_with_ascii(Data, StartOffset, 32, 4, + CurrentIndent + IndentSpaces, true); + NewLine(); + } + OS << ")"; +} + +void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, + uint64_t Base, uint32_t StartOffset) { + NewLine(); + OS << Label << " ("; + if (!Data.empty()) { + OS << "\n"; + Base += StartOffset; + OS << format_bytes_with_ascii(Data, Base, 32, 4, + CurrentIndent + IndentSpaces, true); + NewLine(); + } + OS << ")"; +} diff --git a/tools/lldb-test/FormatUtil.h b/tools/lldb-test/FormatUtil.h new file mode 100644 index 000000000000..f22ee41662ee --- /dev/null +++ b/tools/lldb-test/FormatUtil.h @@ -0,0 +1,75 @@ +//===- FormatUtil.h ------------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLDBTEST_FORMATUTIL_H +#define LLVM_TOOLS_LLDBTEST_FORMATUTIL_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +#include <list> + +namespace lldb_private { + +class LinePrinter { + llvm::raw_ostream &OS; + int IndentSpaces; + int CurrentIndent; + +public: + LinePrinter(int Indent, llvm::raw_ostream &Stream); + + void Indent(uint32_t Amount = 0); + void Unindent(uint32_t Amount = 0); + void NewLine(); + + void printLine(const llvm::Twine &T); + void print(const llvm::Twine &T); + template <typename... Ts> void formatLine(const char *Fmt, Ts &&... Items) { + printLine(llvm::formatv(Fmt, std::forward<Ts>(Items)...)); + } + template <typename... Ts> void format(const char *Fmt, Ts &&... Items) { + print(llvm::formatv(Fmt, std::forward<Ts>(Items)...)); + } + + void formatBinary(llvm::StringRef Label, llvm::ArrayRef<uint8_t> Data, + uint32_t StartOffset); + void formatBinary(llvm::StringRef Label, llvm::ArrayRef<uint8_t> Data, + uint64_t BaseAddr, uint32_t StartOffset); + + llvm::raw_ostream &getStream() { return OS; } + int getIndentLevel() const { return CurrentIndent; } +}; + +struct AutoIndent { + explicit AutoIndent(LinePrinter &L, uint32_t Amount = 0) + : L(&L), Amount(Amount) { + L.Indent(Amount); + } + ~AutoIndent() { + if (L) + L->Unindent(Amount); + } + + LinePrinter *L = nullptr; + uint32_t Amount = 0; +}; + +template <class T> +inline llvm::raw_ostream &operator<<(LinePrinter &Printer, const T &Item) { + Printer.getStream() << Item; + return Printer.getStream(); +} + +} // namespace lldb_private + +#endif diff --git a/tools/lldb-test/SystemInitializerTest.cpp b/tools/lldb-test/SystemInitializerTest.cpp new file mode 100644 index 000000000000..cae395e133ee --- /dev/null +++ b/tools/lldb-test/SystemInitializerTest.cpp @@ -0,0 +1,345 @@ +//===-- SystemInitializerTest.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SystemInitializerTest.h" + +#include "lldb/Core/Debugger.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 "lldb/Symbol/JavaASTContext.h" +#include "lldb/Symbol/OCamlASTContext.h" +#include "lldb/Utility/Timer.h" + +#include "Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h" +#include "Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h" +#include "Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h" +#include "Plugins/ABI/SysV-arm/ABISysV_arm.h" +#include "Plugins/ABI/SysV-arm64/ABISysV_arm64.h" +#include "Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h" +#include "Plugins/ABI/SysV-i386/ABISysV_i386.h" +#include "Plugins/ABI/SysV-mips/ABISysV_mips.h" +#include "Plugins/ABI/SysV-mips64/ABISysV_mips64.h" +#include "Plugins/ABI/SysV-ppc/ABISysV_ppc.h" +#include "Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h" +#include "Plugins/ABI/SysV-s390x/ABISysV_s390x.h" +#include "Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h" +#include "Plugins/Architecture/Arm/ArchitectureArm.h" +#include "Plugins/Disassembler/llvm/DisassemblerLLVMC.h" +#include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h" +#include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h" +#include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h" +#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" +#include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h" +#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h" +#include "Plugins/InstrumentationRuntime/ASan/ASanRuntime.h" +#include "Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h" +#include "Plugins/InstrumentationRuntime/TSan/TSanRuntime.h" +#include "Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h" +#include "Plugins/JITLoader/GDB/JITLoaderGDB.h" +#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" +#include "Plugins/Language/Go/GoLanguage.h" +#include "Plugins/Language/Java/JavaLanguage.h" +#include "Plugins/Language/OCaml/OCamlLanguage.h" +#include "Plugins/Language/ObjC/ObjCLanguage.h" +#include "Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h" +#include "Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h" +#include "Plugins/LanguageRuntime/Go/GoLanguageRuntime.h" +#include "Plugins/LanguageRuntime/Java/JavaLanguageRuntime.h" +#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h" +#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h" +#include "Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h" +#include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h" +#include "Plugins/OperatingSystem/Go/OperatingSystemGo.h" +#include "Plugins/Platform/Android/PlatformAndroid.h" +#include "Plugins/Platform/FreeBSD/PlatformFreeBSD.h" +#include "Plugins/Platform/Kalimba/PlatformKalimba.h" +#include "Plugins/Platform/Linux/PlatformLinux.h" +#include "Plugins/Platform/MacOSX/PlatformMacOSX.h" +#include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h" +#include "Plugins/Platform/NetBSD/PlatformNetBSD.h" +#include "Plugins/Platform/OpenBSD/PlatformOpenBSD.h" +#include "Plugins/Platform/Windows/PlatformWindows.h" +#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" +#include "Plugins/Process/elf-core/ProcessElfCore.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" +#include "Plugins/Process/minidump/ProcessMinidump.h" +#include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h" +#include "Plugins/SymbolFile/PDB/SymbolFilePDB.h" +#include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h" +#include "Plugins/SymbolVendor/ELF/SymbolVendorELF.h" +#include "Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h" +#include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h" +#include "Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h" + +#if defined(__APPLE__) +#include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" +#include "Plugins/Platform/MacOSX/PlatformAppleTVSimulator.h" +#include "Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.h" +#include "Plugins/Platform/MacOSX/PlatformDarwinKernel.h" +#include "Plugins/Platform/MacOSX/PlatformRemoteAppleTV.h" +#include "Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.h" +#include "Plugins/Platform/MacOSX/PlatformiOSSimulator.h" +#include "Plugins/Process/MacOSX-Kernel/ProcessKDP.h" +#include "Plugins/Process/mach-core/ProcessMachCore.h" +#include "Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h" +#endif +#include "Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h" + +#if defined(__FreeBSD__) +#include "Plugins/Process/FreeBSD/ProcessFreeBSD.h" +#endif + +#if defined(_WIN32) +#include "Plugins/Process/Windows/Common/ProcessWindows.h" +#include "lldb/Host/windows/windows.h" +#endif + +#include "llvm/Support/TargetSelect.h" + +#include <string> + +using namespace lldb_private; + +SystemInitializerTest::SystemInitializerTest() {} + +SystemInitializerTest::~SystemInitializerTest() {} + +void SystemInitializerTest::Initialize() { + SystemInitializerCommon::Initialize(); + ScriptInterpreterNone::Initialize(); + + OperatingSystemGo::Initialize(); + + platform_freebsd::PlatformFreeBSD::Initialize(); + platform_linux::PlatformLinux::Initialize(); + platform_netbsd::PlatformNetBSD::Initialize(); + platform_openbsd::PlatformOpenBSD::Initialize(); + PlatformWindows::Initialize(); + PlatformKalimba::Initialize(); + platform_android::PlatformAndroid::Initialize(); + PlatformRemoteiOS::Initialize(); + PlatformMacOSX::Initialize(); +#if defined(__APPLE__) + PlatformiOSSimulator::Initialize(); + PlatformDarwinKernel::Initialize(); +#endif + + // Initialize LLVM and Clang + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + + ClangASTContext::Initialize(); + GoASTContext::Initialize(); + JavaASTContext::Initialize(); + OCamlASTContext::Initialize(); + + ABIMacOSX_i386::Initialize(); + ABIMacOSX_arm::Initialize(); + ABIMacOSX_arm64::Initialize(); + ABISysV_arm::Initialize(); + ABISysV_arm64::Initialize(); + ABISysV_hexagon::Initialize(); + ABISysV_i386::Initialize(); + ABISysV_x86_64::Initialize(); + ABISysV_ppc::Initialize(); + ABISysV_ppc64::Initialize(); + ABISysV_mips::Initialize(); + ABISysV_mips64::Initialize(); + ABISysV_s390x::Initialize(); + + ArchitectureArm::Initialize(); + + DisassemblerLLVMC::Initialize(); + + JITLoaderGDB::Initialize(); + ProcessElfCore::Initialize(); + minidump::ProcessMinidump::Initialize(); + MemoryHistoryASan::Initialize(); + AddressSanitizerRuntime::Initialize(); + ThreadSanitizerRuntime::Initialize(); + UndefinedBehaviorSanitizerRuntime::Initialize(); + MainThreadCheckerRuntime::Initialize(); + + SymbolVendorELF::Initialize(); + SymbolFileDWARF::Initialize(); + SymbolFilePDB::Initialize(); + SymbolFileSymtab::Initialize(); + UnwindAssemblyInstEmulation::Initialize(); + UnwindAssembly_x86::Initialize(); + EmulateInstructionARM64::Initialize(); + SymbolFileDWARFDebugMap::Initialize(); + ItaniumABILanguageRuntime::Initialize(); + AppleObjCRuntimeV2::Initialize(); + AppleObjCRuntimeV1::Initialize(); + SystemRuntimeMacOSX::Initialize(); + RenderScriptRuntime::Initialize(); + GoLanguageRuntime::Initialize(); + JavaLanguageRuntime::Initialize(); + + CPlusPlusLanguage::Initialize(); + GoLanguage::Initialize(); + JavaLanguage::Initialize(); + ObjCLanguage::Initialize(); + ObjCPlusPlusLanguage::Initialize(); + OCamlLanguage::Initialize(); + +#if defined(_WIN32) + ProcessWindows::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(); + DynamicLoaderDarwinKernel::Initialize(); +#endif + + // This plugin is valid on any host that talks to a Darwin remote. + // It shouldn't be limited to __APPLE__. + StructuredDataDarwinLog::Initialize(); + + //---------------------------------------------------------------------- + // Platform agnostic plugins + //---------------------------------------------------------------------- + platform_gdb_server::PlatformRemoteGDBServer::Initialize(); + + process_gdb_remote::ProcessGDBRemote::Initialize(); + DynamicLoaderMacOSXDYLD::Initialize(); + DynamicLoaderMacOS::Initialize(); + DynamicLoaderPOSIXDYLD::Initialize(); + DynamicLoaderStatic::Initialize(); + DynamicLoaderWindowsDYLD::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 SystemInitializerTest::Terminate() { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); + + Debugger::SettingsTerminate(); + + // Terminate and unload and loaded system or user LLDB plug-ins + PluginManager::Terminate(); + + ClangASTContext::Terminate(); + GoASTContext::Terminate(); + JavaASTContext::Terminate(); + OCamlASTContext::Terminate(); + + ABIMacOSX_i386::Terminate(); + ABIMacOSX_arm::Terminate(); + ABIMacOSX_arm64::Terminate(); + ABISysV_arm::Terminate(); + ABISysV_arm64::Terminate(); + ABISysV_hexagon::Terminate(); + ABISysV_i386::Terminate(); + ABISysV_x86_64::Terminate(); + ABISysV_ppc::Terminate(); + ABISysV_ppc64::Terminate(); + ABISysV_mips::Terminate(); + ABISysV_mips64::Terminate(); + ABISysV_s390x::Terminate(); + DisassemblerLLVMC::Terminate(); + + JITLoaderGDB::Terminate(); + ProcessElfCore::Terminate(); + minidump::ProcessMinidump::Terminate(); + MemoryHistoryASan::Terminate(); + AddressSanitizerRuntime::Terminate(); + ThreadSanitizerRuntime::Terminate(); + UndefinedBehaviorSanitizerRuntime::Terminate(); + MainThreadCheckerRuntime::Terminate(); + SymbolVendorELF::Terminate(); + SymbolFileDWARF::Terminate(); + SymbolFilePDB::Terminate(); + SymbolFileSymtab::Terminate(); + UnwindAssembly_x86::Terminate(); + UnwindAssemblyInstEmulation::Terminate(); + EmulateInstructionARM64::Terminate(); + SymbolFileDWARFDebugMap::Terminate(); + ItaniumABILanguageRuntime::Terminate(); + AppleObjCRuntimeV2::Terminate(); + AppleObjCRuntimeV1::Terminate(); + SystemRuntimeMacOSX::Terminate(); + RenderScriptRuntime::Terminate(); + JavaLanguageRuntime::Terminate(); + + CPlusPlusLanguage::Terminate(); + GoLanguage::Terminate(); + JavaLanguage::Terminate(); + ObjCLanguage::Terminate(); + ObjCPlusPlusLanguage::Terminate(); + OCamlLanguage::Terminate(); + +#if defined(__APPLE__) + DynamicLoaderDarwinKernel::Terminate(); + 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(); + StructuredDataDarwinLog::Terminate(); + + DynamicLoaderMacOSXDYLD::Terminate(); + DynamicLoaderMacOS::Terminate(); + DynamicLoaderPOSIXDYLD::Terminate(); + DynamicLoaderStatic::Terminate(); + DynamicLoaderWindowsDYLD::Terminate(); + + OperatingSystemGo::Terminate(); + + platform_freebsd::PlatformFreeBSD::Terminate(); + platform_linux::PlatformLinux::Terminate(); + platform_netbsd::PlatformNetBSD::Terminate(); + platform_openbsd::PlatformOpenBSD::Terminate(); + PlatformWindows::Terminate(); + PlatformKalimba::Terminate(); + platform_android::PlatformAndroid::Terminate(); + PlatformMacOSX::Terminate(); + PlatformRemoteiOS::Terminate(); +#if defined(__APPLE__) + PlatformiOSSimulator::Terminate(); + PlatformDarwinKernel::Terminate(); +#endif + + // Now shutdown the common parts, in reverse order. + SystemInitializerCommon::Terminate(); +} diff --git a/tools/lldb-test/SystemInitializerTest.h b/tools/lldb-test/SystemInitializerTest.h new file mode 100644 index 000000000000..887d6243765d --- /dev/null +++ b/tools/lldb-test/SystemInitializerTest.h @@ -0,0 +1,35 @@ +//===-- SystemInitializerTest.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_API_SYSTEM_INITIALIZER_TEST_H +#define LLDB_API_SYSTEM_INITIALIZER_TEST_H + +#include "lldb/Initialization/SystemInitializerCommon.h" + +namespace lldb_private { +//------------------------------------------------------------------ +/// Initializes lldb. +/// +/// This class is responsible for initializing all of lldb system +/// services needed to use the full LLDB application. This class is +/// not intended to be used externally, but is instead used +/// internally by SBDebugger to initialize the system. +//------------------------------------------------------------------ +class SystemInitializerTest : public SystemInitializerCommon { +public: + SystemInitializerTest(); + ~SystemInitializerTest() override; + + void Initialize() override; + void Terminate() override; +}; + +} // namespace lldb_private + +#endif // LLDB_API_SYSTEM_INITIALIZER_FULL_H diff --git a/tools/lldb-test/lldb-test.cpp b/tools/lldb-test/lldb-test.cpp new file mode 100644 index 000000000000..1fe2f5b63859 --- /dev/null +++ b/tools/lldb-test/lldb-test.cpp @@ -0,0 +1,126 @@ +//===- lldb-test.cpp ------------------------------------------ *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FormatUtil.h" +#include "SystemInitializerTest.h" + +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Initialization/SystemLifetimeManager.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/StreamString.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include <thread> + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +namespace opts { +cl::SubCommand ModuleSubcommand("module-sections", + "Display LLDB Module Information"); +cl::SubCommand SymbolsSubcommand("symbols", "Dump symbols for an object file"); + +namespace module { +cl::opt<bool> SectionContents("contents", + cl::desc("Dump each section's contents"), + cl::sub(ModuleSubcommand)); +cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"), + cl::OneOrMore, cl::sub(ModuleSubcommand)); +} // namespace module + +namespace symbols { +cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"), + cl::OneOrMore, cl::sub(SymbolsSubcommand)); +} +} // namespace opts + +static llvm::ManagedStatic<SystemLifetimeManager> DebuggerLifetime; + +static void dumpSymbols(Debugger &Dbg) { + for (const auto &File : opts::symbols::InputFilenames) { + ModuleSpec Spec{FileSpec(File, false)}; + Spec.GetSymbolFileSpec().SetFile(File, false); + + auto ModulePtr = std::make_shared<lldb_private::Module>(Spec); + + StreamString Stream; + ModulePtr->ParseAllDebugSymbols(); + ModulePtr->Dump(&Stream); + llvm::outs() << Stream.GetData() << "\n"; + llvm::outs().flush(); + } +} + +static void dumpModules(Debugger &Dbg) { + LinePrinter Printer(4, llvm::outs()); + + for (const auto &File : opts::module::InputFilenames) { + ModuleSpec Spec{FileSpec(File, false)}; + Spec.GetSymbolFileSpec().SetFile(File, false); + + auto ModulePtr = std::make_shared<lldb_private::Module>(Spec); + SectionList *Sections = ModulePtr->GetSectionList(); + if (!Sections) { + llvm::errs() << "Could not load sections for module " << File << "\n"; + continue; + } + + size_t Count = Sections->GetNumSections(0); + Printer.formatLine("Showing {0} sections", Count); + for (size_t I = 0; I < Count; ++I) { + AutoIndent Indent(Printer, 2); + auto S = Sections->GetSectionAtIndex(I); + assert(S); + Printer.formatLine("Index: {0}", I); + Printer.formatLine("Name: {0}", S->GetName().GetStringRef()); + Printer.formatLine("VM size: {0}", S->GetByteSize()); + Printer.formatLine("File size: {0}", S->GetFileSize()); + + if (opts::module::SectionContents) { + DataExtractor Data; + S->GetSectionData(Data); + ArrayRef<uint8_t> Bytes = {Data.GetDataStart(), Data.GetDataEnd()}; + Printer.formatBinary("Data: ", Bytes, 0); + } + Printer.NewLine(); + } + } +} + +int main(int argc, const char *argv[]) { + StringRef ToolName = argv[0]; + sys::PrintStackTraceOnErrorSignal(ToolName); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; + + cl::ParseCommandLineOptions(argc, argv, "LLDB Testing Utility\n"); + + DebuggerLifetime->Initialize(llvm::make_unique<SystemInitializerTest>(), + nullptr); + + auto Dbg = lldb_private::Debugger::CreateInstance(); + + if (opts::ModuleSubcommand) + dumpModules(*Dbg); + else if (opts::SymbolsSubcommand) + dumpSymbols(*Dbg); + + DebuggerLifetime->Terminate(); + return 0; +} |