Error MachException::PortInfo::Save(task_t task) { Error error; Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); if (log) log->Printf("MachException::PortInfo::%s(task = 0x%4.4x)", __FUNCTION__, task); // Be careful to be able to have debugserver built on a newer OS than what // it is currently running on by being able to start with all exceptions // and back off to just what is supported on the current system mask = LLDB_EXC_MASK; count = (sizeof(ports) / sizeof(ports[0])); auto mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports, behaviors, flavors); if (mach_err) error.SetError(mach_err, eErrorTypeMachKernel); if (log) { if (error.Success()) { log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = " "0x%x, maskCnt => %u, ports, behaviors, flavors)", task, mask, count); } else { log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = 0x%x, " "maskCnt => %u, ports, behaviors, flavors) error: %u (%s)", task, mask, count, error.GetError(), error.AsCString()); } } if ((error.GetError() == KERN_INVALID_ARGUMENT) && (mask != PREV_EXC_MASK_ALL)) { mask = PREV_EXC_MASK_ALL; count = (sizeof(ports) / sizeof(ports[0])); mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports, behaviors, flavors); error.SetError(mach_err, eErrorTypeMachKernel); if (log) { if (error.Success()) { log->Printf("::task_get_exception_ports(task = 0x%4.4x, " "mask = 0x%x, maskCnt => %u, ports, behaviors, " "flavors)", task, mask, count); } else { log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = " "0x%x, maskCnt => %u, ports, behaviors, flavors) " "error: %u (%s)", task, mask, count, error.GetError(), error.AsCString()); } } } if (error.Fail()) { mask = 0; count = 0; } return error; }
GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServer::Handle_vFile_unlink (StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen("vFile:unlink:")); std::string path; packet.GetHexByteString(path); Error error = Host::Unlink(path.c_str()); StreamString response; response.Printf("F%u,%u", error.GetError(), error.GetError()); return SendPacketNoLock(response.GetData(), response.GetSize()); }
GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerCommon::Handle_vFile_unlink( StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen("vFile:unlink:")); std::string path; packet.GetHexByteString(path); Error error = FileSystem::Unlink(FileSpec{path, true}); StreamString response; response.Printf("F%u,%u", error.GetError(), error.GetError()); return SendPacketNoLock(response.GetString()); }
GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerCommon::Handle_vFile_symlink( StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen("vFile:symlink:")); std::string dst, src; packet.GetHexByteStringTerminatedBy(dst, ','); packet.GetChar(); // Skip ',' char packet.GetHexByteString(src); Error error = FileSystem::Symlink(FileSpec{src, true}, FileSpec{dst, false}); StreamString response; response.Printf("F%u,%u", error.GetError(), error.GetError()); return SendPacketNoLock(response.GetString()); }
GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServer::Handle_vFile_symlink (StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen("vFile:symlink:")); std::string dst, src; packet.GetHexByteStringTerminatedBy(dst, ','); packet.GetChar(); // Skip ',' char packet.GetHexByteString(src); Error error = Host::Symlink(src.c_str(), dst.c_str()); StreamString response; response.Printf("F%u,%u", error.GetError(), error.GetError()); return SendPacketNoLock(response.GetData(), response.GetSize()); }
size_t ProcessWindows::DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, Error &error) { llvm::sys::ScopedLock lock(m_mutex); WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory attempting to write %u bytes into address 0x%I64x", size, vm_addr); if (!m_session_data) { WINERR_IFANY(WINDOWS_LOG_MEMORY, "DoWriteMemory cannot write, there is no active debugger connection."); return 0; } HostProcess process = m_session_data->m_debugger->GetProcess(); void *addr = reinterpret_cast<void *>(vm_addr); SIZE_T bytes_written = 0; lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); if (WriteProcessMemory(handle, addr, buf, size, &bytes_written)) FlushInstructionCache(handle, addr, bytes_written); else { error.SetError(GetLastError(), eErrorTypeWin32); WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory failed with error code %u", error.GetError()); } return bytes_written; }
Error PipePosix::CreateWithUniqueName(llvm::StringRef prefix, bool child_process_inherit, llvm::SmallVectorImpl<char> &name) { llvm::SmallString<PATH_MAX> named_pipe_path; llvm::SmallString<PATH_MAX> pipe_spec((prefix + ".%%%%%%").str()); FileSpec tmpdir_file_spec; tmpdir_file_spec.Clear(); if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) { tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str()); } else { tmpdir_file_spec.AppendPathComponent("/tmp"); tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str()); } // It's possible that another process creates the target path after we've // verified it's available but before we create it, in which case we // should try again. Error error; do { llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(), named_pipe_path); error = CreateNew(named_pipe_path, child_process_inherit); } while (error.GetError() == EEXIST); if (error.Success()) name = named_pipe_path; return error; }
Error PlatformRemoteGDBServer::Unlink(const FileSpec &file_spec) { Error error = m_gdb_client.Unlink(file_spec); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf("PlatformRemoteGDBServer::Unlink(path='%s') error = %u (%s)", file_spec.GetCString(), error.GetError(), error.AsCString()); return error; }
size_t ConnectionFileDescriptor::Write(const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")", static_cast<void *>(this), static_cast<const void *>(src), static_cast<uint64_t>(src_len)); if (!IsConnected()) { if (error_ptr) error_ptr->SetErrorString("not connected"); status = eConnectionStatusNoConnection; return 0; } Error error; size_t bytes_sent = src_len; error = m_write_sp->Write(src, bytes_sent); if (log) { log->Printf("%p ConnectionFileDescriptor::Write(fd = %" PRIu64 ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)", static_cast<void *>(this), static_cast<uint64_t>(m_write_sp->GetWaitableHandle()), static_cast<const void *>(src), static_cast<uint64_t>(src_len), static_cast<uint64_t>(bytes_sent), error.AsCString()); } if (error_ptr) *error_ptr = error; if (error.Fail()) { switch (error.GetError()) { case EAGAIN: case EINTR: status = eConnectionStatusSuccess; return 0; case ECONNRESET: // The connection is closed by the peer during a read attempt on a socket. case ENOTCONN: // A read is attempted on an unconnected socket. status = eConnectionStatusLostConnection; break; // Break to close.... default: status = eConnectionStatusError; break; // Break to close.... } return 0; } status = eConnectionStatusSuccess; return bytes_sent; }
Error PlatformRemoteGDBServer::MakeDirectory(const FileSpec &file_spec, uint32_t mode) { Error error = m_gdb_client.MakeDirectory(file_spec, mode); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf ("PlatformRemoteGDBServer::MakeDirectory(path='%s', mode=%o) error = %u (%s)", file_spec.GetCString(), mode, error.GetError(), error.AsCString()); return error; }
Error PlatformRemoteGDBServer::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) { Error error = m_gdb_client.SetFilePermissions(file_spec, file_permissions); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf ("PlatformRemoteGDBServer::SetFilePermissions(path='%s', file_permissions=%o) error = %u (%s)", file_spec.GetCString(), file_permissions, error.GetError(), error.AsCString()); return error; }
Error PlatformRemoteGDBServer::CreateSymlink(const FileSpec &src, // The name of the link is in src const FileSpec &dst) // The symlink points to dst { Error error = m_gdb_client.CreateSymlink(src, dst); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf ("PlatformRemoteGDBServer::CreateSymlink(src='%s', dst='%s') error = %u (%s)", src.GetCString(), dst.GetCString(), error.GetError(), error.AsCString()); return error; }
void ProcessWindows::OnDebuggerError(const Error &error, uint32_t type) { llvm::sys::ScopedLock lock(m_mutex); if (m_session_data->m_initial_stop_received) { // This happened while debugging. Do we shutdown the debugging session, try to continue, // or do something else? WINERR_IFALL(WINDOWS_LOG_PROCESS, "Error %u occurred during debugging. Unexpected behavior may result. %s", error.GetError(), error.AsCString()); } else { // If we haven't actually launched the process yet, this was an error launching the // process. Set the internal error and signal the initial stop event so that the DoLaunch // method wakes up and returns a failure. m_session_data->m_launch_error = error; ::SetEvent(m_session_data->m_initial_stop_event); WINERR_IFALL(WINDOWS_LOG_PROCESS, "Error %u occurred launching the process before the initial stop. %s", error.GetError(), error.AsCString()); return; } }
Error ProcessWindowsLive::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo &info) { Error error; llvm::sys::ScopedLock lock(m_mutex); if (!m_session_data) { error.SetErrorString("GetMemoryRegionInfo called with no debugging session."); WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString()); return error; } HostProcess process = m_session_data->m_debugger->GetProcess(); lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); if (handle == nullptr || handle == LLDB_INVALID_PROCESS) { error.SetErrorString("GetMemoryRegionInfo called with an invalid target process."); WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString()); return error; } WINLOG_IFALL(WINDOWS_LOG_MEMORY, "GetMemoryRegionInfo getting info for address 0x%I64x", vm_addr); void *addr = reinterpret_cast<void *>(vm_addr); MEMORY_BASIC_INFORMATION mem_info = {0}; SIZE_T result = ::VirtualQueryEx(handle, addr, &mem_info, sizeof(mem_info)); if (result == 0) { error.SetError(::GetLastError(), eErrorTypeWin32); WINERR_IFALL(WINDOWS_LOG_MEMORY, "VirtualQueryEx returned error %u while getting memory region info for address 0x%I64x", error.GetError(), vm_addr); return error; } const bool readable = IsPageReadable(mem_info.Protect); const bool executable = IsPageExecutable(mem_info.Protect); const bool writable = IsPageWritable(mem_info.Protect); info.SetReadable(readable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); info.SetExecutable(executable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); info.SetWritable(writable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); error.SetError(::GetLastError(), eErrorTypeWin32); WINLOGV_IFALL(WINDOWS_LOG_MEMORY, "Memory region info for address 0x%I64u: readable=%s, executable=%s, writable=%s", BOOL_STR(readable), BOOL_STR(executable), BOOL_STR(writable)); return error; }
GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerCommon::Handle_vFile_Mode( StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen("vFile:mode:")); std::string path; packet.GetHexByteString(path); if (!path.empty()) { Error error; const uint32_t mode = File::GetPermissions(FileSpec{path, true}, error); StreamString response; response.Printf("F%u", mode); if (mode == 0 || error.Fail()) response.Printf(",%i", (int)error.GetError()); return SendPacketNoLock(response.GetString()); } return SendErrorResponse(23); }
GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServer::Handle_qPlatform_mkdir (StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen("qPlatform_mkdir:")); mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); if (packet.GetChar() == ',') { std::string path; packet.GetHexByteString(path); Error error = Host::MakeDirectory(path.c_str(),mode); if (error.Success()) return SendPacketNoLock ("OK", 2); else return SendErrorResponse(error.GetError()); } return SendErrorResponse(20); }
GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerCommon::Handle_qPlatform_mkdir( StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen("qPlatform_mkdir:")); mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); if (packet.GetChar() == ',') { std::string path; packet.GetHexByteString(path); Error error = FileSystem::MakeDirectory(FileSpec{path, false}, mode); StreamGDBRemote response; response.Printf("F%u", error.GetError()); return SendPacketNoLock(response.GetString()); } return SendErrorResponse(20); }
GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod (StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen("qPlatform_chmod:")); mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); if (packet.GetChar() == ',') { std::string path; packet.GetHexByteString(path); Error error = FileSystem::SetFilePermissions(FileSpec{path, true}, mode); StreamGDBRemote response; response.Printf("F%u", error.GetError()); return SendPacketNoLock(response.GetData(), response.GetSize()); } return SendErrorResponse(19); }
Error ProcessWindows::DoHalt(bool &caused_stop) { Error error; StateType state = GetPrivateState(); if (state == eStateStopped) caused_stop = false; else { llvm::sys::ScopedLock lock(m_mutex); caused_stop = ::DebugBreakProcess(m_session_data->m_debugger->GetProcess().GetNativeProcess().GetSystemHandle()); if (!caused_stop) { error.SetError(::GetLastError(), eErrorTypeWin32); WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoHalt called DebugBreakProcess, but it failed with error %u", error.GetError()); } } return error; }
Error MachException::PortInfo::Restore(task_t task) { Error error; Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); if (log) log->Printf("MachException::PortInfo::Restore(task = 0x%4.4x)", task); uint32_t i = 0; if (count > 0) { for (i = 0; i < count; i++) { auto mach_err = ::task_set_exception_ports(task, masks[i], ports[i], behaviors[i], flavors[i]); if (mach_err) error.SetError(mach_err, eErrorTypeMachKernel); if (log) { if (error.Success()) { log->Printf("::task_set_exception_ports(task = 0x%4.4x, " "exception_mask = 0x%8.8x, new_port = 0x%4.4x, " "behavior = 0x%8.8x, new_flavor = 0x%8.8x)", task, masks[i], ports[i], behaviors[i], flavors[i]); } else { log->Printf("::task_set_exception_ports(task = 0x%4.4x, " "exception_mask = 0x%8.8x, new_port = 0x%4.4x, " "behavior = 0x%8.8x, new_flavor = 0x%8.8x): " "error %u (%s)", task, masks[i], ports[i], behaviors[i], flavors[i], error.GetError(), error.AsCString()); } } // Bail if we encounter any errors if (error.Fail()) break; } } count = 0; return error; }
size_t ProcessWindows::DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Error &error) { llvm::sys::ScopedLock lock(m_mutex); if (!m_session_data) return 0; WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoReadMemory attempting to read %u bytes from address 0x%I64x", size, vm_addr); HostProcess process = m_session_data->m_debugger->GetProcess(); void *addr = reinterpret_cast<void *>(vm_addr); SIZE_T bytes_read = 0; if (!ReadProcessMemory(process.GetNativeProcess().GetSystemHandle(), addr, buf, size, &bytes_read)) { error.SetError(GetLastError(), eErrorTypeWin32); WINERR_IFALL(WINDOWS_LOG_MEMORY, "DoReadMemory failed with error code %u", error.GetError()); } return bytes_read; }
void SendError( int priority, const char* module_name, const Error& error ) { SendErrorText( priority, module_name, error.GetError()); }
//---------------------------------------------------------------------- // The file descriptor FD is assumed to already be opened as read only // and the STAT structure is assumed to a valid pointer and already // containing valid data from a call to stat(). // // Memory map FILE_LENGTH bytes in FILE starting FILE_OFFSET bytes into // the file. If FILE_LENGTH is set to SIZE_MAX, then map as many bytes // as possible. // // RETURNS // Number of bytes mapped starting from the requested offset. //---------------------------------------------------------------------- size_t DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd, off_t offset, size_t length, bool writeable, bool fd_is_file) { Clear(); if (fd >= 0) { struct stat stat; if (::fstat(fd, &stat) == 0) { if (S_ISREG(stat.st_mode) && (stat.st_size > offset)) { const size_t max_bytes_available = stat.st_size - offset; if (length == SIZE_MAX) { length = max_bytes_available; } else if (length > max_bytes_available) { // Cap the length if too much data was requested length = max_bytes_available; } if (length > 0) { int prot = PROT_READ; if (writeable) prot |= PROT_WRITE; int flags = MAP_PRIVATE; if (fd_is_file) flags |= MAP_FILE; m_mmap_addr = (uint8_t *)::mmap(NULL, length, prot, flags, fd, offset); if (m_mmap_addr == (void*)-1) { Error error; error.SetErrorToErrno (); if (error.GetError() == EINVAL) { // We may still have a shot at memory mapping if we align things correctly size_t page_offset = offset % Host::GetPageSize(); if (page_offset != 0) { m_mmap_addr = (uint8_t *)::mmap(NULL, length + page_offset, prot, flags, fd, offset - page_offset); if (m_mmap_addr == (void*)-1) { // Failed to map file m_mmap_addr = NULL; } else if (m_mmap_addr != NULL) { // We recovered and were able to memory map // after we aligned things to page boundaries // Save the actual mmap'ed size m_mmap_size = length + page_offset; // Our data is at an offset into the the mapped data m_data = m_mmap_addr + page_offset; // Our pretend size is the size that was requestd m_size = length; } } } if (error.GetError() == ENOMEM) { error.SetErrorStringWithFormat("could not allocate %lld bytes of memory to mmap in file", (uint64_t) length); } } else { // We were able to map the requested data in one chunk // where our mmap and actual data are the same. m_mmap_size = length; m_data = m_mmap_addr; m_size = length; } } } } } return GetByteSize (); }
ConnectionStatus ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) { // Don't need to take the mutex here separately since we are only called from Read. If we // ever get used more generally we will need to lock here as well. LogSP log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", this, timeout_usec); struct timeval *tv_ptr; struct timeval tv; if (timeout_usec == UINT32_MAX) { // Infinite wait... tv_ptr = NULL; } else { TimeValue time_value; time_value.OffsetWithMicroSeconds (timeout_usec); tv = time_value.GetAsTimeVal(); tv_ptr = &tv; } while (m_fd_recv >= 0) { fd_set read_fds; FD_ZERO (&read_fds); FD_SET (m_fd_recv, &read_fds); if (m_pipe_read != -1) FD_SET (m_pipe_read, &read_fds); int nfds = std::max<int>(m_fd_recv, m_pipe_read) + 1; Error error; if (log) log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds = %i, fd = %i, NULL, NULL, timeout = %p)...", this, nfds, m_fd_recv, tv_ptr); const int num_set_fds = ::select (nfds, &read_fds, NULL, NULL, tv_ptr); if (num_set_fds < 0) error.SetErrorToErrno(); else error.Clear(); if (log) log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds = %i, fd = %i, NULL, NULL, timeout = %p) => %d, error = %s", this, nfds, m_fd_recv, tv_ptr, num_set_fds, error.AsCString()); if (error_ptr) *error_ptr = error; if (error.Fail()) { switch (error.GetError()) { case EBADF: // One of the descriptor sets specified an invalid descriptor. return eConnectionStatusLostConnection; case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. default: // Other unknown error return eConnectionStatusError; case EAGAIN: // The kernel was (perhaps temporarily) unable to // allocate the requested number of file descriptors, // or we have non-blocking IO case EINTR: // A signal was delivered before the time limit // expired and before any of the selected events // occurred. break; // Lets keep reading to until we timeout } } else if (num_set_fds == 0) { return eConnectionStatusTimedOut; } else if (num_set_fds > 0) { if (m_pipe_read != -1 && FD_ISSET(m_pipe_read, &read_fds)) { // We got a command to exit. Read the data from that pipe: char buffer[16]; ssize_t bytes_read; do { bytes_read = ::read (m_pipe_read, buffer, sizeof(buffer)); } while (bytes_read < 0 && errno == EINTR); assert (bytes_read == 1 && buffer[0] == 'q'); if (log) log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", this, (int) bytes_read, buffer); return eConnectionStatusEndOfFile; } else return eConnectionStatusSuccess; } } if (error_ptr) error_ptr->SetErrorString("not connected"); return eConnectionStatusLostConnection; }
bool DoExecute (Args& command, CommandReturnObject &result) { const size_t argc = command.GetArgumentCount(); if (argc == 0) { if (!m_command_byte.GetOptionValue().OptionWasSet()) { result.AppendError ("the --command option must be set to a valid command byte"); result.SetStatus (eReturnStatusFailed); } else { const uint64_t command_byte = m_command_byte.GetOptionValue().GetUInt64Value(0); if (command_byte > 0 && command_byte <= UINT8_MAX) { ProcessKDP *process = (ProcessKDP *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { const StateType state = process->GetState(); if (StateIsStoppedState (state, true)) { std::vector<uint8_t> payload_bytes; const char *ascii_hex_bytes_cstr = m_packet_data.GetOptionValue().GetCurrentValue(); if (ascii_hex_bytes_cstr && ascii_hex_bytes_cstr[0]) { StringExtractor extractor(ascii_hex_bytes_cstr); const size_t ascii_hex_bytes_cstr_len = extractor.GetStringRef().size(); if (ascii_hex_bytes_cstr_len & 1) { result.AppendErrorWithFormat ("payload data must contain an even number of ASCII hex characters: '%s'", ascii_hex_bytes_cstr); result.SetStatus (eReturnStatusFailed); return false; } payload_bytes.resize(ascii_hex_bytes_cstr_len/2); if (extractor.GetHexBytes(&payload_bytes[0], payload_bytes.size(), '\xdd') != payload_bytes.size()) { result.AppendErrorWithFormat ("payload data must only contain ASCII hex characters (no spaces or hex prefixes): '%s'", ascii_hex_bytes_cstr); result.SetStatus (eReturnStatusFailed); return false; } } Error error; DataExtractor reply; process->GetCommunication().SendRawRequest (command_byte, payload_bytes.empty() ? NULL : payload_bytes.data(), payload_bytes.size(), reply, error); if (error.Success()) { // Copy the binary bytes into a hex ASCII string for the result StreamString packet; packet.PutBytesAsRawHex8(reply.GetDataStart(), reply.GetByteSize(), lldb::endian::InlHostByteOrder(), lldb::endian::InlHostByteOrder()); result.AppendMessage(packet.GetString().c_str()); result.SetStatus (eReturnStatusSuccessFinishResult); return true; } else { const char *error_cstr = error.AsCString(); if (error_cstr && error_cstr[0]) result.AppendError (error_cstr); else result.AppendErrorWithFormat ("unknown error 0x%8.8x", error.GetError()); result.SetStatus (eReturnStatusFailed); return false; } } else { result.AppendErrorWithFormat ("process must be stopped in order to send KDP packets, state is %s", StateAsCString (state)); result.SetStatus (eReturnStatusFailed); } } else { result.AppendError ("invalid process"); result.SetStatus (eReturnStatusFailed); } } else { result.AppendErrorWithFormat ("invalid command byte 0x%" PRIx64 ", valid values are 1 - 255", command_byte); result.SetStatus (eReturnStatusFailed); } } } else { result.AppendErrorWithFormat ("'%s' takes no arguments, only options.", m_cmd_name.c_str()); result.SetStatus (eReturnStatusFailed); } return false; }
size_t ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) { LogSP log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); if (log) log->Printf ("%p ConnectionFileDescriptor::Write (src = %p, src_len = %llu)", this, src, (uint64_t)src_len); if (!IsConnected ()) { if (error_ptr) error_ptr->SetErrorString("not connected"); status = eConnectionStatusNoConnection; return 0; } Error error; ssize_t bytes_sent = 0; switch (m_fd_send_type) { case eFDTypeFile: // Other FD requireing read/write do { bytes_sent = ::write (m_fd_send, src, src_len); } while (bytes_sent < 0 && errno == EINTR); break; case eFDTypeSocket: // Socket requiring send/recv do { bytes_sent = ::send (m_fd_send, src, src_len, 0); } while (bytes_sent < 0 && errno == EINTR); break; case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom assert (m_udp_send_sockaddr.GetFamily() != 0); do { bytes_sent = ::sendto (m_fd_send, src, src_len, 0, m_udp_send_sockaddr, m_udp_send_sockaddr.GetLength()); } while (bytes_sent < 0 && errno == EINTR); break; } if (bytes_sent < 0) error.SetErrorToErrno (); else error.Clear (); if (log) { switch (m_fd_send_type) { case eFDTypeFile: // Other FD requireing read/write log->Printf ("%p ConnectionFileDescriptor::Write() ::write (fd = %i, src = %p, src_len = %llu) => %lli (error = %s)", this, m_fd_send, src, (uint64_t)src_len, (int64_t)bytes_sent, error.AsCString()); break; case eFDTypeSocket: // Socket requiring send/recv log->Printf ("%p ConnectionFileDescriptor::Write() ::send (socket = %i, src = %p, src_len = %llu, flags = 0) => %lli (error = %s)", this, m_fd_send, src, (uint64_t)src_len, (int64_t)bytes_sent, error.AsCString()); break; case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom log->Printf ("%p ConnectionFileDescriptor::Write() ::sendto (socket = %i, src = %p, src_len = %llu, flags = 0) => %lli (error = %s)", this, m_fd_send, src, (uint64_t)src_len, (int64_t)bytes_sent, error.AsCString()); break; } } if (error_ptr) *error_ptr = error; if (error.Fail()) { switch (error.GetError()) { case EAGAIN: case EINTR: status = eConnectionStatusSuccess; return 0; case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. case ENOTCONN: // A read is attempted on an unconnected socket. status = eConnectionStatusLostConnection; break; // Break to close.... default: status = eConnectionStatusError; break; // Break to close.... } return 0; } status = eConnectionStatusSuccess; return bytes_sent; }
size_t ConnectionFileDescriptor::Read (void *dst, size_t dst_len, uint32_t timeout_usec, ConnectionStatus &status, Error *error_ptr) { LogSP log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); if (log) log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %llu)...", this, m_fd_recv, dst, (uint64_t)dst_len); Mutex::Locker locker; bool got_lock = locker.TryLock (m_mutex); if (!got_lock) { if (log) log->Printf ("%p ConnectionFileDescriptor::Read () failed to get the connection lock.", this); if (error_ptr) error_ptr->SetErrorString ("failed to get the connection lock for read."); status = eConnectionStatusTimedOut; return 0; } else if (m_shutting_down) return eConnectionStatusError; ssize_t bytes_read = 0; status = BytesAvailable (timeout_usec, error_ptr); if (status == eConnectionStatusSuccess) { do { bytes_read = ::read (m_fd_recv, dst, dst_len); } while (bytes_read < 0 && errno == EINTR); } if (status != eConnectionStatusSuccess) return 0; Error error; if (bytes_read == 0) { error.Clear(); // End-of-file. Do not automatically close; pass along for the end-of-file handlers. status = eConnectionStatusEndOfFile; } else if (bytes_read < 0) { error.SetErrorToErrno(); } else { error.Clear(); } if (log) log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %llu) => %lli, error = %s", this, m_fd_recv, dst, (uint64_t)dst_len, (int64_t)bytes_read, error.AsCString()); if (error_ptr) *error_ptr = error; if (error.Fail()) { uint32_t error_value = error.GetError(); switch (error_value) { case EAGAIN: // The file was marked for non-blocking I/O, and no data were ready to be read. if (m_fd_recv_type == eFDTypeSocket || m_fd_recv_type == eFDTypeSocketUDP) status = eConnectionStatusTimedOut; else status = eConnectionStatusSuccess; return 0; case EFAULT: // Buf points outside the allocated address space. case EINTR: // A read from a slow device was interrupted before any data arrived by the delivery of a signal. case EINVAL: // The pointer associated with fildes was negative. case EIO: // An I/O error occurred while reading from the file system. // The process group is orphaned. // The file is a regular file, nbyte is greater than 0, // the starting position is before the end-of-file, and // the starting position is greater than or equal to the // offset maximum established for the open file // descriptor associated with fildes. case EISDIR: // An attempt is made to read a directory. case ENOBUFS: // An attempt to allocate a memory buffer fails. case ENOMEM: // Insufficient memory is available. status = eConnectionStatusError; break; // Break to close.... case ENOENT: // no such file or directory case EBADF: // fildes is not a valid file or socket descriptor open for reading. case ENXIO: // An action is requested of a device that does not exist.. // A requested action cannot be performed by the device. case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. case ENOTCONN: // A read is attempted on an unconnected socket. status = eConnectionStatusLostConnection; break; // Break to close.... case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a socket. status = eConnectionStatusTimedOut; return 0; } //Disconnect (NULL); return 0; } return bytes_read; }
lldb::thread_result_t Communication::ReadThread (lldb::thread_arg_t p) { Communication *comm = (Communication *)p; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMUNICATION)); if (log) log->Printf ("%p Communication::ReadThread () thread starting...", p); uint8_t buf[1024]; Error error; ConnectionStatus status = eConnectionStatusSuccess; bool done = false; while (!done && comm->m_read_thread_enabled) { size_t bytes_read = comm->ReadFromConnection (buf, sizeof(buf), 5 * TimeValue::MicroSecPerSec, status, &error); if (bytes_read > 0) comm->AppendBytesToCache (buf, bytes_read, true, status); else if ((bytes_read == 0) && status == eConnectionStatusEndOfFile) { if (comm->GetCloseOnEOF ()) comm->Disconnect (); comm->AppendBytesToCache (buf, bytes_read, true, status); } switch (status) { case eConnectionStatusSuccess: break; case eConnectionStatusEndOfFile: done = true; break; case eConnectionStatusError: // Check GetError() for details if (error.GetType() == eErrorTypePOSIX && error.GetError() == EIO) { // EIO on a pipe is usually caused by remote shutdown comm->Disconnect (); done = true; } if (log) error.LogIfError (log, "%p Communication::ReadFromConnection () => status = %s", p, Communication::ConnectionStatusAsCString (status)); break; case eConnectionStatusInterrupted: // Synchronization signal from SynchronizeWithReadThread() // The connection returns eConnectionStatusInterrupted only when there is no // input pending to be read, so we can signal that. comm->BroadcastEvent (eBroadcastBitNoMorePendingInput); break; case eConnectionStatusNoConnection: // No connection case eConnectionStatusLostConnection: // Lost connection while connected to a valid connection done = true; LLVM_FALLTHROUGH; case eConnectionStatusTimedOut: // Request timed out if (log) error.LogIfError (log, "%p Communication::ReadFromConnection () => status = %s", p, Communication::ConnectionStatusAsCString (status)); break; } } log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMUNICATION); if (log) log->Printf ("%p Communication::ReadThread () thread exiting...", p); comm->m_read_thread_did_exit = true; // Let clients know that this thread is exiting comm->BroadcastEvent (eBroadcastBitNoMorePendingInput); comm->BroadcastEvent (eBroadcastBitReadThreadDidExit); return NULL; }
ConnectionStatus ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) { // Don't need to take the mutex here separately since we are only called from Read. If we // ever get used more generally we will need to lock here as well. Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", static_cast<void*>(this), timeout_usec); struct timeval *tv_ptr; struct timeval tv; if (timeout_usec == UINT32_MAX) { // Inifinite wait... tv_ptr = nullptr; } else { TimeValue time_value; time_value.OffsetWithMicroSeconds (timeout_usec); tv.tv_sec = time_value.seconds(); tv.tv_usec = time_value.microseconds(); tv_ptr = &tv; } // Make a copy of the file descriptors to make sure we don't // have another thread change these values out from under us // and cause problems in the loop below where like in FS_SET() const IOObject::WaitableHandle handle = m_read_sp->GetWaitableHandle(); const int pipe_fd = m_pipe.GetReadFileDescriptor(); if (handle != IOObject::kInvalidHandleValue) { #if defined(_MSC_VER) // select() won't accept pipes on Windows. The entire Windows codepath needs to be // converted over to using WaitForMultipleObjects and event HANDLEs, but for now at least // this will allow ::select() to not return an error. const bool have_pipe_fd = false; #else const bool have_pipe_fd = pipe_fd >= 0; #if !defined(__APPLE__) assert (handle < FD_SETSIZE); if (have_pipe_fd) assert (pipe_fd < FD_SETSIZE); #endif #endif while (handle == m_read_sp->GetWaitableHandle()) { const int nfds = std::max<int>(handle, pipe_fd) + 1; #if defined(__APPLE__) llvm::SmallVector<fd_set, 1> read_fds; read_fds.resize((nfds/FD_SETSIZE) + 1); for (size_t i=0; i<read_fds.size(); ++i) FD_ZERO (&read_fds[i]); // FD_SET doesn't bounds check, it just happily walks off the end // but we have taken care of making the extra storage with our // SmallVector of fd_set objects #else fd_set read_fds; FD_ZERO (&read_fds); #endif FD_SET (handle, FD_SET_DATA(read_fds)); if (have_pipe_fd) FD_SET (pipe_fd, FD_SET_DATA(read_fds)); Error error; if (log) { if (have_pipe_fd) log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", static_cast<void*>(this), nfds, handle, pipe_fd, static_cast<void*>(tv_ptr)); else log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", static_cast<void*>(this), nfds, handle, static_cast<void*>(tv_ptr)); } const int num_set_fds = ::select (nfds, FD_SET_DATA(read_fds), NULL, NULL, tv_ptr); if (num_set_fds < 0) error.SetErrorToErrno(); else error.Clear(); if (log) { if (have_pipe_fd) log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) => %d, error = %s", static_cast<void*>(this), nfds, handle, pipe_fd, static_cast<void*>(tv_ptr), num_set_fds, error.AsCString()); else log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => %d, error = %s", static_cast<void*>(this), nfds, handle, static_cast<void*>(tv_ptr), num_set_fds, error.AsCString()); } if (error_ptr) *error_ptr = error; if (error.Fail()) { switch (error.GetError()) { case EBADF: // One of the descriptor sets specified an invalid descriptor. return eConnectionStatusLostConnection; case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. default: // Other unknown error return eConnectionStatusError; case EAGAIN: // The kernel was (perhaps temporarily) unable to // allocate the requested number of file descriptors, // or we have non-blocking IO case EINTR: // A signal was delivered before the time limit // expired and before any of the selected events // occurred. break; // Lets keep reading to until we timeout } } else if (num_set_fds == 0) { return eConnectionStatusTimedOut; } else if (num_set_fds > 0) { if (FD_ISSET(handle, FD_SET_DATA(read_fds))) return eConnectionStatusSuccess; if (have_pipe_fd && FD_ISSET(pipe_fd, FD_SET_DATA(read_fds))) { // We got a command to exit. Read the data from that pipe: char buffer[16]; ssize_t bytes_read; do { bytes_read = ::read (pipe_fd, buffer, sizeof(buffer)); } while (bytes_read < 0 && errno == EINTR); switch (buffer[0]) { case 'q': if (log) log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", static_cast<void*>(this), static_cast<int>(bytes_read), buffer); return eConnectionStatusEndOfFile; case 'i': // Interrupt the current read return eConnectionStatusInterrupted; } } } } } if (error_ptr) error_ptr->SetErrorString("not connected"); return eConnectionStatusLostConnection; }
//---------------------------------------------------------------------- // The file descriptor FD is assumed to already be opened as read only // and the STAT structure is assumed to a valid pointer and already // containing valid data from a call to stat(). // // Memory map FILE_LENGTH bytes in FILE starting FILE_OFFSET bytes into // the file. If FILE_LENGTH is set to SIZE_MAX, then map as many bytes // as possible. // // RETURNS // Number of bytes mapped starting from the requested offset. //---------------------------------------------------------------------- size_t DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd, lldb::offset_t offset, size_t length, bool writeable, bool fd_is_file) { Clear(); if (fd >= 0) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP|LIBLLDB_LOG_VERBOSE)); if (log) { log->Printf("DataBufferMemoryMap::MemoryMapFromFileDescriptor(fd=%i, offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i, fd_is_file=%i)", fd, offset, (uint64_t)length, writeable, fd_is_file); } #ifdef _WIN32 HANDLE handle = (HANDLE)_get_osfhandle(fd); DWORD file_size_low, file_size_high; file_size_low = GetFileSize(handle, &file_size_high); const lldb::offset_t file_size = llvm::Make_64(file_size_high, file_size_low); const lldb::offset_t max_bytes_available = file_size - offset; const size_t max_bytes_mappable = (size_t)std::min<lldb::offset_t>(SIZE_MAX, max_bytes_available); if (length == SIZE_MAX || length > max_bytes_mappable) { // Cap the length if too much data was requested length = max_bytes_mappable; } if (length > 0) { HANDLE fileMapping = CreateFileMapping(handle, NULL, writeable ? PAGE_READWRITE : PAGE_READONLY, file_size_high, file_size_low, NULL); if (fileMapping != NULL) { if (win32memmapalignment == 0) LoadWin32MemMapAlignment(); lldb::offset_t realoffset = offset; lldb::offset_t delta = 0; if (realoffset % win32memmapalignment != 0) { realoffset = realoffset / win32memmapalignment * win32memmapalignment; delta = offset - realoffset; } LPVOID data = MapViewOfFile(fileMapping, writeable ? FILE_MAP_WRITE : FILE_MAP_READ, 0, realoffset, length + delta); m_mmap_addr = (uint8_t *)data; if (!data) { Error error; error.SetErrorToErrno (); } else { m_data = m_mmap_addr + delta; m_size = length; } CloseHandle(fileMapping); } } #else struct stat stat; if (::fstat(fd, &stat) == 0) { if (S_ISREG(stat.st_mode) && (stat.st_size > static_cast<off_t>(offset))) { const size_t max_bytes_available = stat.st_size - offset; if (length == SIZE_MAX) { length = max_bytes_available; } else if (length > max_bytes_available) { // Cap the length if too much data was requested length = max_bytes_available; } if (length > 0) { int prot = PROT_READ; if (writeable) prot |= PROT_WRITE; int flags = MAP_PRIVATE; if (fd_is_file) flags |= MAP_FILE; m_mmap_addr = (uint8_t *)::mmap(NULL, length, prot, flags, fd, offset); Error error; if (m_mmap_addr == (void*)-1) { error.SetErrorToErrno (); if (error.GetError() == EINVAL) { // We may still have a shot at memory mapping if we align things correctly size_t page_offset = offset % Host::GetPageSize(); if (page_offset != 0) { m_mmap_addr = (uint8_t *)::mmap(NULL, length + page_offset, prot, flags, fd, offset - page_offset); if (m_mmap_addr == (void*)-1) { // Failed to map file m_mmap_addr = NULL; } else if (m_mmap_addr != NULL) { // We recovered and were able to memory map // after we aligned things to page boundaries // Save the actual mmap'ed size m_mmap_size = length + page_offset; // Our data is at an offset into the the mapped data m_data = m_mmap_addr + page_offset; // Our pretend size is the size that was requestd m_size = length; } } } if (error.GetError() == ENOMEM) { error.SetErrorStringWithFormat("could not allocate %" PRId64 " bytes of memory to mmap in file", (uint64_t) length); } } else { // We were able to map the requested data in one chunk // where our mmap and actual data are the same. m_mmap_size = length; m_data = m_mmap_addr; m_size = length; } if (log) { log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec() m_mmap_addr = %p, m_mmap_size = %" PRIu64 ", error = %s", m_mmap_addr, (uint64_t)m_mmap_size, error.AsCString()); } } } } #endif } return GetByteSize (); }