diff --git a/lldb/README_zh.md b/lldb/README_zh.md index 23be9aec884f9c5f73600ca11d760a0328f9bd78..4b0db70195a7ecbc5a6ae4d72cf73c129f49528e 100644 --- a/lldb/README_zh.md +++ b/lldb/README_zh.md @@ -1752,3 +1752,66 @@ WorkingDir: /data/local/tmp 多个服务调试同两个服务调试步骤,只需要开启多个LLDB命令行窗口即可。 +# 5 LLDB 命令行调试辅助脚本 + +## 5.1 运行 Python 代码 + +LLDB 支持运行 Python 代码。目前,该功能已在 OpenHarmony 发布的 Linux、Windows 和 macOS 版本 NDK 支持。 + +1. 执行单行 Python 代码 + +``` +script print("Hello OpenHarmony!") +``` + +2. 执行 `script` 命令进入 Python shell,然后执行 Python 代码 + +```python +import os +env_path_value = os.environ.get("PATH", "") +print(f"PATH='{env_path_value}'") +``` + +## 5.2 使用辅助脚本连接和启动 `lldb-server` + +使用 OpenHarmony NDK 的 LLDB 命令行远程调试时,通常需要以下步骤: + +1. 将 `lldb-server` 推送到 OpenHarmony 设备上。 + +2. 在 OpenHarmony 设备上启动 `lldb-server`。 + +3. 开发者在电脑上运行 `lldb` 和连接 `lldb-server`。 + +4. attach 正在运行的进程或者启动命令行程序,然后执行调试命令。 + +OpenHarmony NDK 提供了辅助脚本,简化了调试流程,输入以下指令即可完成 `lldb-server` 的启动和连接: + +``` +script import lldb_python +script lldb_python.main() +``` + +### 5.2.1 查看参数 + +查看脚本使用的 `lldb-server` 启动和连接参数: + +``` +script lldb_python.show_server_configs() +``` + +### 5.2.2 修改参数 + +修改启动和连接参数: + +``` +script lldb_python.set_server_config("tcp-listen-port", 8080) +``` + +目前支持下列参数: + +| 参数名 | 默认值 | 说明 | +| --- | --- | --- | +| `arch` | `unknown` | 可选值:`arm` 表示 ARM 32位架构;`aarch64` 表示 ARM 64位架构;`x86_64` 表示 x86 64位架构。根据硬件架构,选择响应的 `lldb-server`。参数值为 `unknown` 时,会尝试判断设备的架构;如果自动判断架构出错,用户可以手动设置成其他值,关闭架构发现功能(使用用户指定的架构)。 | +| `install-path` | `/data/local/tmp/lldb/lldb-server` | `lldb-server` 在 OpenHarmony 设备上的安装路径。 | +| `tcp-listen-port` | `1234` | `lldb-server` 监听的 tcp 端口号 | +| `platform` | `remote-ohos` | `lldb-server` 所在设备平台。 | diff --git a/lldb/scripts/lldb.cmd b/lldb/scripts/lldb.cmd new file mode 100644 index 0000000000000000000000000000000000000000..f840d4fde19ce8b4a85d56bb0c17d3daeaf882fa --- /dev/null +++ b/lldb/scripts/lldb.cmd @@ -0,0 +1,4 @@ +@ECHO OFF +SET PYTHONPATH=%~dp0..\script +%~dp0lldb.exe %* +EXIT /B%ERRORLEVEL% diff --git a/lldb/scripts/lldb.sh b/lldb/scripts/lldb.sh new file mode 100755 index 0000000000000000000000000000000000000000..f4514e659622ddf87d25a7be3e443774d764c7c2 --- /dev/null +++ b/lldb/scripts/lldb.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +CURDIR=$(cd $(dirname $0) && pwd) +export PYTHONPATH="$CURDIR/../script" +"$CURDIR/lldb" "$@" diff --git a/lldb/scripts/lldb_python.py b/lldb/scripts/lldb_python.py new file mode 100644 index 0000000000000000000000000000000000000000..bd593813d80e20d3cdd81193bd211205d8aa6ad0 --- /dev/null +++ b/lldb/scripts/lldb_python.py @@ -0,0 +1,150 @@ +# Copyright (C) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from enum import Enum +import lldb +import os +from pathlib import Path, PurePosixPath +import re +import shutil +import subprocess +import time +from typing import Mapping, Optional, Tuple + +class Arch(Enum): + ARM = "arm" + AARCH64 = "aarch64" + X86_64 = "x86_64" + UNKNOWN = "unknown" + +class LLDBException(Exception): + _err_msg: str + + def __init__(self, err_msg: str) -> None: + super().__init__(err_msg) + self._err_msg = err_msg + + @property + def err_msg(self) -> str: + return self._err_msg + +class _ServerConfig: + arch: Arch = Arch.UNKNOWN + install_path: PurePosixPath = PurePosixPath("/data/local/tmp/lldb/lldb-server") + tcp_listen_port: int = 1234 + platform: str = "remote-ohos" + +_server_config_item_mapping: Mapping[str, Tuple[str, type]] = { + "arch": ("arch", Arch), + "install-path": ("install_path", PurePosixPath), + "tcp-listen-port": ("tcp_listen_port", int), + "platform": ("platform", str) +} + +_config = _ServerConfig() + +def show_server_configs() -> None: + for config_name, mapping in _server_config_item_mapping.items(): + attr_name = mapping[0] + attr_value = getattr(_config, attr_name) + value = attr_value.value if isinstance(attr_value, Enum) else attr_value + print(f"{config_name}='{value}'") + +def set_server_config(name: str, value: str) -> None: + mapping = _server_config_item_mapping.get(name) + if mapping is None: + print(f"Error: unknown config name '{name}'") + return + attr_name, attr_type = mapping + setattr(_config, attr_name, attr_type(value)) + +def _check_hdc() -> None: + if shutil.which("hdc") is None: + raise LLDBException("hdc is not found. Make sure 'PATH' environment variable is properly set.") + +def _guess_arch() -> Arch: + ret = subprocess.check_output('hdc shell file /system/bin/appspawn', shell = True) + str_arch = ret.decode(encoding = "utf-8", errors = "strict") + match_res = re.search("musl-(.*?)(?:\.so)", str_arch) + + if match_res is None: + return Arch.UNKNOWN + try: + arch = Arch(match_res.group(1)) + except ValueError: + arch = Arch.UNKNOWN + return arch + +def _parse_lldb_version(version_string: str) -> Optional[str]: + # An example of LLDB's version string: + # + # lldb version 15.0.4 (https://gitee.com/openharmony/third_party_llvm-project revision edacc9e94de5f6d415b1b1ad13f420194739b85a) + # clang revision edacc9e94de5f6d415b1b1ad13f420194739b85a + # llvm revision edacc9e94de5f6d415b1b1ad13f420194739b85a + parts = version_string.split() + if len(parts) < 3: + return None + return parts[2] + +def _find_lldb_server(arch: Arch) -> Path: + if arch is Arch.UNKNOWN: + arch = _guess_arch() + if arch is Arch.UNKNOWN: + raise LLDBException("failed to get hardware archtecture for your device") + + curr_script_dir = Path(os.path.dirname(os.path.abspath(__file__))) + version = _parse_lldb_version(lldb.debugger.GetVersionString()) + if version is None: + raise LLDBException("failed to get version of LLDB") + + lldb_server_path = curr_script_dir / ".." / "lib" / "clang" / version / "bin" / f"{arch.value}-linux-ohos" / "lldb-server" + return lldb_server_path + +def _install_lldb_server(lldb_server_path: Path, install_path: PurePosixPath) -> None: + if not lldb_server_path.exists(): + raise LLDBException(f"file '{lldb_server_path}' does not exists") + if not lldb_server_path.is_file(): + raise LLDBException(f"file 'f{lldb_server_path}' is not a regular file") + + subprocess.run(['hdc', 'shell', 'mkdir', '-p', str(install_path.parent)]) + subprocess.run(['hdc', 'file', 'send', str(lldb_server_path), str(install_path)]) + subprocess.run(['hdc', 'shell', 'chmod', 'u+x', str(install_path)]) + +def _start_lldb_server(lldb_server_path: PurePosixPath, tcp_listen_port: int) -> None: + subprocess.Popen(['hdc', 'shell', str(lldb_server_path), 'platform', '--listen', f'*:{tcp_listen_port}'], stdout=subprocess.PIPE) + time.sleep(3) + +# connect to lldb-server +def _connect_server(platform: str, server_port: int) -> None: + remote_platform = lldb.SBPlatform(platform) + server_url = f"connect://localhost:{server_port}" + platform_connect_options = lldb.SBPlatformConnectOptions(server_url) + err = remote_platform.ConnectRemote(platform_connect_options) + if not err.Success(): + raise LLDBException(f"failed to connect to platform '{platform}' using URL '{server_url}': {err}") + + lldb.debugger.SetSelectedPlatform(remote_platform) + print("platform connected.") + +def main() -> None: + try: + _check_hdc() + lldb_server_path = _find_lldb_server(_config.arch) + _install_lldb_server(lldb_server_path, _config.install_path) + _start_lldb_server(_config.install_path, _config.tcp_listen_port) + _connect_server(_config.platform, _config.tcp_listen_port) + except LLDBException as e: + print(f"Error: {e.err_msg}") + +if __name__ == '__main__': + main() diff --git a/llvm-build/build.py b/llvm-build/build.py index 795f88257e04352eb555bd9277b2e1d027221f3f..4b657dd8b83c3aeccd828fddf03d5869c888313d 100755 --- a/llvm-build/build.py +++ b/llvm-build/build.py @@ -1608,6 +1608,16 @@ class LlvmPackage(BuildUtils): # Strip lldb-server self.strip_lldb_server(host, install_dir) + + # Copy lldb script + lldb_script_file = 'lldb.cmd' if host.startswith('windows') else 'lldb.sh' + self.check_copy_file(os.path.join(self.build_config.LLVM_PROJECT_DIR, 'lldb', 'scripts', lldb_script_file), os.path.join(install_dir, 'bin')) + + lldb_script_python_path = os.path.join(self.build_config.LLVM_PROJECT_DIR, 'lldb', 'scripts', 'lldb_python.py') + + self.check_create_dir(os.path.join(install_dir, 'script')) + + self.check_copy_file(lldb_script_python_path, os.path.join(install_dir, 'script')) for necessary_bin_file in necessary_bin_files: if not os.path.isfile(os.path.join(bin_dir, necessary_bin_file)):