Chad Smith bcb48aa5b2
[lldb] refactor PlatformAndroid and make threadsafe (#145382)
## Problem

When the new setting

```
set target.parallel-module-load true
```
was added, lldb began fetching modules from the devices from multiple
threads simultaneously. This caused crashes of lldb when debugging on
android devices.

The top of the stack in the crash look something like this:
```
#0 0x0000555aaf2b27fe llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/llvm/bin/lldb-dap+0xb87fe)
 #1 0x0000555aaf2b0a99 llvm::sys::RunSignalHandlers() (/opt/llvm/bin/lldb-dap+0xb6a99)
 #2 0x0000555aaf2b2fda SignalHandler(int, siginfo_t*, void*) (/opt/llvm/bin/lldb-dap+0xb8fda)
 #3 0x00007f9c02444560 __restore_rt /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/signal/../sysdeps/unix/sysv/linux/libc_sigaction.c:13:0
 #4 0x00007f9c04ea7707 lldb_private::ConnectionFileDescriptor::Disconnect(lldb_private::Status*) (usr/bin/../lib/liblldb.so.15+0x22a7707)
 #5 0x00007f9c04ea5b41 lldb_private::ConnectionFileDescriptor::~ConnectionFileDescriptor() (usr/bin/../lib/liblldb.so.15+0x22a5b41)
 #6 0x00007f9c04ea5c1e lldb_private::ConnectionFileDescriptor::~ConnectionFileDescriptor() (usr/bin/../lib/liblldb.so.15+0x22a5c1e)
 #7 0x00007f9c052916ff lldb_private::platform_android::AdbClient::SyncService::Stat(lldb_private::FileSpec const&, unsigned int&, unsigned int&, unsigned int&) (usr/bin/../lib/liblldb.so.15+0x26916ff)
 #8 0x00007f9c0528b9dc lldb_private::platform_android::PlatformAndroid::GetFile(lldb_private::FileSpec const&, lldb_private::FileSpec const&) (usr/bin/../lib/liblldb.so.15+0x268b9dc)
```
Our workaround was to set `set target.parallel-module-load ` to `false`
to avoid the crash.

## Background

PlatformAndroid creates two different classes with one stateful adb
connection shared between the two -- one through AdbClient and another
through AdbClient::SyncService. The connection management and state is
complex, and seems to be responsible for the segfault we are seeing. The
AdbClient code resets these connections at times, and re-establishes
connections if they are not active. Similarly, PlatformAndroid caches
its SyncService, which uses an AdbClient class, but the SyncService puts
its connection into a different 'sync' state that is incompatible with a
standard connection.

## Changes in this diff

* This diff refactors the code to (hopefully) have clearer ownership of
the connection, clearer separation of AdbClient and SyncService by
making a new class for clearer separations of concerns, called
AdbSyncService.
* New unit tests are added
* Additional logs were added (see
https://github.com/llvm/llvm-project/pull/145382#issuecomment-3055535017
for details)
2025-08-13 22:43:45 +00:00

118 lines
3.8 KiB
C++

//===-- AdbClient.h ---------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_ANDROID_ADBCLIENT_H
#define LLDB_SOURCE_PLUGINS_PLATFORM_ANDROID_ADBCLIENT_H
#include "lldb/Utility/Status.h"
#include "llvm/Support/Error.h"
#include <chrono>
#include <functional>
#include <list>
#include <memory>
#include <string>
#include <vector>
namespace lldb_private {
class FileSpec;
namespace platform_android {
class AdbClient {
public:
enum UnixSocketNamespace {
UnixSocketNamespaceAbstract,
UnixSocketNamespaceFileSystem,
};
using DeviceIDList = std::list<std::string>;
/// Resolves a device identifier to its canonical form.
///
/// \param device_id the device identifier to resolve (may be empty).
///
/// \returns Expected<std::string> containing the resolved device ID on
/// success, or an Error if the device ID cannot be resolved or
/// is ambiguous.
static llvm::Expected<std::string> ResolveDeviceID(llvm::StringRef device_id);
AdbClient();
explicit AdbClient(llvm::StringRef device_id);
virtual ~AdbClient();
llvm::StringRef GetDeviceID() const;
Status SetPortForwarding(const uint16_t local_port,
const uint16_t remote_port);
Status SetPortForwarding(const uint16_t local_port,
llvm::StringRef remote_socket_name,
const UnixSocketNamespace socket_namespace);
Status DeletePortForwarding(const uint16_t local_port);
Status Shell(const char *command, std::chrono::milliseconds timeout,
std::string *output);
virtual Status ShellToFile(const char *command,
std::chrono::milliseconds timeout,
const FileSpec &output_file_spec);
Status Connect();
private:
Status SendDeviceMessage(llvm::StringRef packet);
Status ReadMessageStream(std::vector<char> &message,
std::chrono::milliseconds timeout);
Status internalShell(const char *command, std::chrono::milliseconds timeout,
std::vector<char> &output_buf);
std::string m_device_id;
std::unique_ptr<Connection> m_conn;
};
class AdbSyncService {
public:
explicit AdbSyncService(const std::string device_id);
virtual ~AdbSyncService();
Status SetupSyncConnection();
virtual Status PullFile(const FileSpec &remote_file,
const FileSpec &local_file);
virtual Status PushFile(const FileSpec &local_file,
const FileSpec &remote_file);
virtual Status Stat(const FileSpec &remote_file, uint32_t &mode,
uint32_t &size, uint32_t &mtime);
virtual bool IsConnected() const;
llvm::StringRef GetDeviceId() const { return m_device_id; }
private:
Status SendSyncRequest(const char *request_id, const uint32_t data_len,
const void *data);
Status ReadSyncHeader(std::string &response_id, uint32_t &data_len);
Status PullFileChunk(std::vector<char> &buffer, bool &eof);
Status PullFileImpl(const FileSpec &remote_file, const FileSpec &local_file);
Status PushFileImpl(const FileSpec &local_file, const FileSpec &remote_file);
Status StatImpl(const FileSpec &remote_file, uint32_t &mode, uint32_t &size,
uint32_t &mtime);
Status ExecuteCommand(const std::function<Status()> &cmd);
std::unique_ptr<Connection> m_conn;
std::string m_device_id;
};
} // namespace platform_android
} // namespace lldb_private
#endif // LLDB_SOURCE_PLUGINS_PLATFORM_ANDROID_ADBCLIENT_H