From 23923806db75ce049439fbcadfc508772063102e Mon Sep 17 00:00:00 2001 From: weli-l <1289113577@qq.com> Date: Sun, 8 Jun 2025 23:05:37 +0800 Subject: [PATCH] init systrace Signed-off-by: weli-l <1289113577@qq.com> --- systrace/.clang-format | 5 + systrace/CMakeLists.txt | 70 + systrace/build.sh | 13 + systrace/convert/convert_json2csv.py | 55 + systrace/convert/convert_mem_to_flamegraph.py | 275 ++++ .../convert_mem_to_flamegraph_for_cur.py | 305 ++++ systrace/convert/convert_mspti_timeline.py | 129 ++ .../convert/convert_pytorch_to_timeline.py | 50 + systrace/hack/format.sh | 45 + systrace/include/common/constant.h | 19 + systrace/include/common/logging.cc | 6 + systrace/include/common/logging.h | 28 + systrace/include/common/macro.h | 17 + systrace/include/common/shared_constants.h | 12 + systrace/include/common/util.cc | 253 +++ systrace/include/common/util.h | 290 ++++ systrace/protos/CMakeLists.txt | 17 + systrace/protos/systrace.proto | 62 + systrace/src/ascend/hook.cc | 74 + systrace/src/ascend/hook.h | 41 + systrace/src/cann/cann_hook.c | 532 +++++++ systrace/src/mspti/json_file_writer.h | 189 +++ systrace/src/mspti/mspti_tracker.cpp | 96 ++ systrace/src/mspti/mspti_tracker.hpp | 33 + systrace/src/trace/CMakeLists.txt | 13 + systrace/src/trace/library_loader.cc | 46 + systrace/src/trace/library_loader.h | 32 + systrace/src/trace/python/pytorch_tracing.c | 618 +++++++ systrace/src/trace/python/pytorch_tracing.h | 69 + .../src/trace/python/pytorch_tracing_data.h | 54 + .../trace/python/pytorch_tracing_loader.cc | 121 ++ .../src/trace/python/pytorch_tracing_loader.h | 45 + .../trace/python/pytorch_tracing_manager.cc | 58 + .../trace/python/pytorch_tracing_manager.h | 73 + systrace/src/trace/systrace_manager.cc | 243 +++ systrace/src/trace/systrace_manager.h | 76 + .../aarch64/libunwind/libunwind-aarch64.h | 291 ++++ .../aarch64/libunwind/libunwind-common.h | 335 ++++ .../aarch64/libunwind/libunwind-dynamic.h | 201 +++ .../thirdparty/aarch64/libunwind/libunwind.h | 40 + .../thirdparty/aarch64/libunwind/unwind.h | 158 ++ .../thirdparty/aarch64/mspti/include/mspti.h | 19 + .../aarch64/mspti/include/mspti_activity.h | 424 +++++ .../aarch64/mspti/include/mspti_callback.h | 258 +++ .../aarch64/mspti/include/mspti_cbid.h | 83 + .../aarch64/mspti/include/mspti_result.h | 30 + .../aarch64/mspti/lib64/libmspti.so | Bin 0 -> 534512 bytes systrace/thirdparty/uthash.h | 1417 +++++++++++++++++ .../x86_64/libunwind/libunwind-common.h | 335 ++++ .../x86_64/libunwind/libunwind-dynamic.h | 201 +++ .../x86_64/libunwind/libunwind-x86_64.h | 146 ++ .../thirdparty/x86_64/libunwind/libunwind.h | 40 + systrace/thirdparty/x86_64/libunwind/unwind.h | 158 ++ .../thirdparty/x86_64/mspti/include/mspti.h | 19 + .../x86_64/mspti/include/mspti_activity.h | 424 +++++ .../x86_64/mspti/include/mspti_callback.h | 258 +++ .../x86_64/mspti/include/mspti_cbid.h | 83 + .../x86_64/mspti/include/mspti_result.h | 30 + .../thirdparty/x86_64/mspti/lib64/libmspti.so | Bin 0 -> 522288 bytes systrace/watchdog/watchdog.py | 147 ++ 60 files changed, 9161 insertions(+) create mode 100644 systrace/.clang-format create mode 100644 systrace/CMakeLists.txt create mode 100644 systrace/build.sh create mode 100644 systrace/convert/convert_json2csv.py create mode 100644 systrace/convert/convert_mem_to_flamegraph.py create mode 100644 systrace/convert/convert_mem_to_flamegraph_for_cur.py create mode 100644 systrace/convert/convert_mspti_timeline.py create mode 100644 systrace/convert/convert_pytorch_to_timeline.py create mode 100644 systrace/hack/format.sh create mode 100644 systrace/include/common/constant.h create mode 100644 systrace/include/common/logging.cc create mode 100644 systrace/include/common/logging.h create mode 100644 systrace/include/common/macro.h create mode 100644 systrace/include/common/shared_constants.h create mode 100644 systrace/include/common/util.cc create mode 100644 systrace/include/common/util.h create mode 100644 systrace/protos/CMakeLists.txt create mode 100644 systrace/protos/systrace.proto create mode 100644 systrace/src/ascend/hook.cc create mode 100644 systrace/src/ascend/hook.h create mode 100644 systrace/src/cann/cann_hook.c create mode 100644 systrace/src/mspti/json_file_writer.h create mode 100644 systrace/src/mspti/mspti_tracker.cpp create mode 100644 systrace/src/mspti/mspti_tracker.hpp create mode 100644 systrace/src/trace/CMakeLists.txt create mode 100644 systrace/src/trace/library_loader.cc create mode 100644 systrace/src/trace/library_loader.h create mode 100644 systrace/src/trace/python/pytorch_tracing.c create mode 100644 systrace/src/trace/python/pytorch_tracing.h create mode 100644 systrace/src/trace/python/pytorch_tracing_data.h create mode 100644 systrace/src/trace/python/pytorch_tracing_loader.cc create mode 100644 systrace/src/trace/python/pytorch_tracing_loader.h create mode 100644 systrace/src/trace/python/pytorch_tracing_manager.cc create mode 100644 systrace/src/trace/python/pytorch_tracing_manager.h create mode 100644 systrace/src/trace/systrace_manager.cc create mode 100644 systrace/src/trace/systrace_manager.h create mode 100644 systrace/thirdparty/aarch64/libunwind/libunwind-aarch64.h create mode 100644 systrace/thirdparty/aarch64/libunwind/libunwind-common.h create mode 100644 systrace/thirdparty/aarch64/libunwind/libunwind-dynamic.h create mode 100644 systrace/thirdparty/aarch64/libunwind/libunwind.h create mode 100644 systrace/thirdparty/aarch64/libunwind/unwind.h create mode 100644 systrace/thirdparty/aarch64/mspti/include/mspti.h create mode 100644 systrace/thirdparty/aarch64/mspti/include/mspti_activity.h create mode 100644 systrace/thirdparty/aarch64/mspti/include/mspti_callback.h create mode 100644 systrace/thirdparty/aarch64/mspti/include/mspti_cbid.h create mode 100644 systrace/thirdparty/aarch64/mspti/include/mspti_result.h create mode 100644 systrace/thirdparty/aarch64/mspti/lib64/libmspti.so create mode 100644 systrace/thirdparty/uthash.h create mode 100644 systrace/thirdparty/x86_64/libunwind/libunwind-common.h create mode 100644 systrace/thirdparty/x86_64/libunwind/libunwind-dynamic.h create mode 100644 systrace/thirdparty/x86_64/libunwind/libunwind-x86_64.h create mode 100644 systrace/thirdparty/x86_64/libunwind/libunwind.h create mode 100644 systrace/thirdparty/x86_64/libunwind/unwind.h create mode 100644 systrace/thirdparty/x86_64/mspti/include/mspti.h create mode 100644 systrace/thirdparty/x86_64/mspti/include/mspti_activity.h create mode 100644 systrace/thirdparty/x86_64/mspti/include/mspti_callback.h create mode 100644 systrace/thirdparty/x86_64/mspti/include/mspti_cbid.h create mode 100644 systrace/thirdparty/x86_64/mspti/include/mspti_result.h create mode 100644 systrace/thirdparty/x86_64/mspti/lib64/libmspti.so create mode 100644 systrace/watchdog/watchdog.py diff --git a/systrace/.clang-format b/systrace/.clang-format new file mode 100644 index 0000000..466d8df --- /dev/null +++ b/systrace/.clang-format @@ -0,0 +1,5 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +BreakBeforeBraces: Allman +UseTab: Never +TabWidth: 4 diff --git a/systrace/CMakeLists.txt b/systrace/CMakeLists.txt new file mode 100644 index 0000000..4856e45 --- /dev/null +++ b/systrace/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required(VERSION 3.10) +project(sysTrace) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_SKIP_RPATH TRUE) +set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +set(CMAKE_INSTALL_RPATH "") +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) +if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|ARM64") + set(UNWIND_LIB "unwind-aarch64") + set(MSPTI_INCLUDE "${PROJECT_SOURCE_DIR}/thirdparty/aarch64/mspti/include") + set(MSPTI_LIB "${PROJECT_SOURCE_DIR}/thirdparty/aarch64/mspti/lib64") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64") + set(UNWIND_LIB "unwind") + set(MSPTI_INCLUDE "${PROJECT_SOURCE_DIR}/thirdparty/x86_64/mspti/include") + set(MSPTI_LIB "${PROJECT_SOURCE_DIR}/thirdparty/x86_64/mspti/lib64") +else() + message(WARNING "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}") + set(UNWIND_LIB "unwind") +endif() +include_directories( + ${MSPTI_INCLUDE} +) + +find_package(Python3 REQUIRED COMPONENTS Development) +find_package(Protobuf REQUIRED) +find_package(Threads REQUIRED) + +find_library(ASCEND_MSPTI + NAMES mspti + HINTS ${MSPTI_LIB} +) +if(NOT ASCEND_MSPTI) + message(FATAL_ERROR "Ascend mspti library not found!") +endif() + +add_library(common STATIC + ${PROJECT_SOURCE_DIR}/include/common/logging.cc + ${PROJECT_SOURCE_DIR}/include/common/util.cc +) +target_include_directories(common PUBLIC ${PROJECT_SOURCE_DIR}/include ${Python3_INCLUDE_DIRS}) + + +add_subdirectory(protos) + +add_library(sysTrace_hook SHARED + ${PROJECT_SOURCE_DIR}/src/trace/systrace_manager.cc + ${PROJECT_SOURCE_DIR}/src/trace/library_loader.cc + ${PROJECT_SOURCE_DIR}/src/trace/python/pytorch_tracing_loader.cc + ${PROJECT_SOURCE_DIR}/src/trace/python/pytorch_tracing_manager.cc + ${PROJECT_SOURCE_DIR}/src/trace/python/pytorch_tracing.c + ${PROJECT_SOURCE_DIR}/src/ascend/hook.cc + ${PROJECT_SOURCE_DIR}/src/mspti/mspti_tracker.cpp + ${PROJECT_SOURCE_DIR}/src/cann/cann_hook.c +) + +set_target_properties(sysTrace_hook PROPERTIES OUTPUT_NAME "sysTrace") + +target_link_libraries(sysTrace_hook + common + general_pb2 + ${Python3_LIBRARIES} + protobuf::libprotobuf + ${CMAKE_THREAD_LIBS} + pthread + jsoncpp + -ldl +) diff --git a/systrace/build.sh b/systrace/build.sh new file mode 100644 index 0000000..3040c97 --- /dev/null +++ b/systrace/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +sudo dnf remove -y libunwind libunwind-devel 2>/dev/null || true +mkdir -p build + +cd protos +protoc --c_out=. systrace.proto +protoc --cpp_out=. systrace.proto +protoc --python_out=. systrace.proto +cd .. +cd build +cmake .. +make -j $(nproc) diff --git a/systrace/convert/convert_json2csv.py b/systrace/convert/convert_json2csv.py new file mode 100644 index 0000000..55b8ea2 --- /dev/null +++ b/systrace/convert/convert_json2csv.py @@ -0,0 +1,55 @@ +# coding=utf-8 +""" +Copyright (c) Huawei Technologies Co., Ltd. 2020-2028. All rights reserved. +Description: +FileName:convert_json2_csv.py +Author: h00568282/huangbin +Create Date: 2025/3/28 16:17 +Notes: + +""" +import os +import json +import pandas as pd +from util.logging_utils import get_default_logger + +logger = get_default_logger(__name__) + + +def convert_json2csv(json_path): + csv_path = f"{json_path[:-5]}.csv" + if os.path.exists(csv_path): + return + + try: + with open(json_path, 'r', encoding='utf-8') as file: + content = file.read() + content = content.replace(']\n[', ',').strip() + json_data = json.loads(content) + except: + logger.error("json data read error") + json_data = None + + if not json_data: + return + df = pd.json_normalize(json_data, sep='_') + + logger.info(f"save path: {csv_path}") + df.to_csv(csv_path, index=False) + + +def convert_jsons2csv(root_path): + json_files = [file for file in os.listdir(root_path) if file.endswith("json")] + + for json_file in json_files: + logger.info(f"{json_file}") + json_path = os.path.join(root_path, json_file) + convert_json2csv(json_path) + + +if __name__ == "__main__": + # json_path = "./data/json_data/hccl_activity.3.json" + # convert_json2csv(json_path) + + root_path = "./data/json_tp4dp1" + convert_jsons2csv(root_path) \ No newline at end of file diff --git a/systrace/convert/convert_mem_to_flamegraph.py b/systrace/convert/convert_mem_to_flamegraph.py new file mode 100644 index 0000000..fd80b78 --- /dev/null +++ b/systrace/convert/convert_mem_to_flamegraph.py @@ -0,0 +1,275 @@ +#!/usr/bin/env python3 +import sys +import json +import os +import subprocess +from collections import defaultdict, deque +from concurrent.futures import ThreadPoolExecutor +from systrace_pb2 import ProcMem, StageType + +class FixedFlameGraphConverter: + def __init__(self): + self.stage_names = { + StageType.STAGE_UNKNOWN: "UNKNOWN", + StageType.STAGE_DATALOADER: "DATALOADER", + StageType.STAGE_FORWARD: "FORWARD", + StageType.STAGE_BACKWARD: "BACKWARD", + StageType.STAGE_SYNCHRONIZATION: "SYNCHRONIZATION", + getattr(StageType, "STAGE_GC", 5): "GC" + } + self.symbol_cache = {} + self.so_path_cache = {} + self.executor = ThreadPoolExecutor(max_workers=os.cpu_count() or 4) + + def convert(self, input_pb, output_json): + proc_mem = self._load_proc_mem(input_pb) + alloc_groups = self._analyze_allocations(proc_mem) + self._precache_symbols(alloc_groups) + + trace_events = [] + global_timestamp = 0 + + # 按stage_name分组处理 + stage_data = defaultdict(list) + for (stage_type, stage_id), allocs in alloc_groups.items(): + stage_name = f"{stage_id}_{self.stage_names.get(stage_type, 'UNKNOWN')}" + stage_data[stage_name].extend(allocs) + + for stage_name, allocs in stage_data.items(): + # if any(s in stage_name for s in ["0_", "1_", "2_"]): + # continue + + # 生成该stage的所有事件 + stage_events = [] + min_ts = global_timestamp + max_ts = global_timestamp + sum(a.mem_size for a in allocs) + + # 先添加容器事件(强制置顶) + container_event = { + "name": stage_name, + "ph": "X", + "ts": min_ts, + "dur": max_ts - min_ts, + "pid": proc_mem.pid, + "tid": proc_mem.pid, + "args": { + "stage_type": self.stage_names.get(next(iter(alloc_groups.keys()))[0], "UNKNOWN"), + "stage_id": next(iter(alloc_groups.keys()))[1], + "is_container": True + } + } + stage_events.append(container_event) + + # 处理每个分配 + current_ts = global_timestamp + for alloc in allocs: + alloc_events, _ = self._process_allocation(alloc, proc_mem.pid, current_ts) + stage_events.extend(alloc_events) + current_ts += alloc.mem_size + + # 合并同名调用 + merged_events = self._merge_calls(stage_events, stage_name) + trace_events.extend(merged_events) + global_timestamp = max_ts + + self._save_json(output_json, trace_events) + self.executor.shutdown() + + def _merge_calls(self, events, stage_name): + """合并相同stage下的同名调用""" + # 分离容器事件和调用事件 + container = [e for e in events if e.get("args", {}).get("is_container")][0] + calls = [e for e in events if not e.get("args", {}).get("is_container")] + + # 按深度和名称分组 + call_groups = defaultdict(list) + for e in calls: + key = (e["args"]["depth"], e["name"]) + call_groups[key].append(e) + + # 合并每组调用 + merged_calls = [] + for (depth, name), group in call_groups.items(): + if len(group) == 1: + merged_calls.extend(group) + continue + + group.sort(key=lambda x: x["ts"]) + current = dict(group[0]) + + for e in group[1:]: + if e["ts"] == current["ts"] + current["dur"]: + current["dur"] += e["dur"] + current["args"]["bytes"] += e["args"]["bytes"] + if "merged_ptrs" not in current["args"]: + current["args"]["merged_ptrs"] = [current["args"]["alloc_ptr"]] + current["args"]["merged_ptrs"].append(e["args"]["alloc_ptr"]) + else: + if "merged_ptrs" in current["args"]: + current["args"]["alloc_ptr"] = ",".join(current["args"]["merged_ptrs"]) + del current["args"]["merged_ptrs"] + merged_calls.append(current) + current = dict(e) + + if "merged_ptrs" in current["args"]: + current["args"]["alloc_ptr"] = ",".join(current["args"]["merged_ptrs"]) + del current["args"]["merged_ptrs"] + merged_calls.append(current) + + # 确保容器事件在最前 + return [container] + sorted(merged_calls, key=lambda x: x["ts"]) + + def _process_allocation(self, alloc, pid, base_ts): + """处理单个分配事件""" + events = [] + alloc_duration = alloc.mem_size + + # 构建调用栈树 + call_tree = { + "name": "[root]", + "duration": alloc_duration, + "children": [] + } + current_parent = call_tree + + for frame in alloc.stack_frames: + so_name = os.path.basename(frame.so_name) + symbol = self._resolve_symbol(so_name, frame.address) + node = { + "name": symbol, + "duration": alloc_duration, + "children": [] + } + current_parent["children"].append(node) + current_parent = node + + # 调整duration + def adjust_durations(node): + if node["children"]: + node["duration"] = sum(adjust_durations(child) for child in node["children"]) + return node["duration"] + adjust_durations(call_tree) + + # 生成事件(BFS遍历) + stack = deque([(call_tree, base_ts, 0)]) + call_events = [] + while stack: + node, ts, depth = stack.popleft() + call_events.append({ + "name": node["name"], + "ph": "X", + "ts": ts, + "dur": node["duration"], + "pid": pid, + "tid": pid, + "args": { + "depth": depth, + "bytes": alloc.mem_size, + "alloc_ptr": f"0x{alloc.alloc_ptr:x}" + } + }) + for child in reversed(node["children"]): + stack.appendleft((child, ts, depth + 1)) + + return call_events, alloc_duration + + # 保留其他基础方法 + def _load_proc_mem(self, path): + with open(path, "rb") as f: + proc_mem = ProcMem() + proc_mem.ParseFromString(f.read()) + return proc_mem + + def _analyze_allocations(self, proc_mem): + freed_ptrs = {free.alloc_ptr for free in proc_mem.mem_free_stacks} + active_allocs = defaultdict(list) + for alloc in proc_mem.mem_alloc_stacks: + #if alloc.alloc_ptr not in freed_ptrs: + active_allocs[(alloc.stage_type, alloc.stage_id)].append(alloc) + return active_allocs + + def _precache_symbols(self, alloc_groups): + unique_frames = set() + for allocs in alloc_groups.values(): + for alloc in allocs: + for frame in alloc.stack_frames: + so_name = os.path.basename(frame.so_name) + unique_frames.add((so_name, frame.address)) + list(self.executor.map(lambda args: self._resolve_symbol(*args), unique_frames)) + + def _resolve_symbol(self, so_name, address): + cache_key = f"{so_name}:{address:x}" + if cache_key in self.symbol_cache: + return self.symbol_cache[cache_key] + + so_path = self._find_so_path(so_name) + if not so_path: + symbol = f"{so_name}@0x{address:x}" + self.symbol_cache[cache_key] = symbol + return symbol + + try: + result = subprocess.run( + ["addr2line", "-e", so_path, "-f", "-C", "-p", f"0x{address:x}"], + capture_output=True, text=True, timeout=0.05 + ) + func_name = result.stdout.split(" at ")[0].split("(")[0].strip() if result.returncode == 0 else "" + symbol = f"{so_name}@{func_name}" if func_name else f"{so_name}@0x{address:x}" + except: + symbol = f"{so_name}@0x{address:x}" + + self.symbol_cache[cache_key] = symbol + return symbol + + def _find_so_path(self, so_name): + if so_name in self.so_path_cache: + return self.so_path_cache[so_name] + + if os.path.isabs(so_name) and os.path.exists(so_name): + self.so_path_cache[so_name] = so_name + return so_name + + base_name = os.path.basename(so_name) + search_paths = [ + "/usr/lib", "/usr/local/lib", "/lib", + *os.getenv("LD_LIBRARY_PATH", "").split(":"), + *os.getenv("PATH", "").split(":") + ] + + for path in filter(os.path.isdir, search_paths): + test_path = os.path.join(path, base_name) + if os.path.exists(test_path): + self.so_path_cache[so_name] = test_path + return test_path + + if base_name.startswith("lib") and ".so" in base_name: + lib_prefix = base_name.split(".so")[0] + for ext in ["", ".1", ".2", ".3", ".4", ".5"]: + test_path = os.path.join(path, f"{lib_prefix}.so{ext}") + if os.path.exists(test_path): + self.so_path_cache[so_name] = test_path + return test_path + + self.so_path_cache[so_name] = None + return None + + def _save_json(self, path, trace_events): + if os.path.isdir(path): + input_name = os.path.splitext(os.path.basename(sys.argv[1]))[0] + path = os.path.join(path, f"{input_name}_fixed_flamegraph.json") + + with open(path, "w") as f: + json.dump({ + "traceEvents": sorted(trace_events, key=lambda x: x["ts"]), + "displayTimeUnit": "ns", + "metadata": { + "format": "FixedFlameGraph", + "stage_order": list(self.stage_names.values()) + } + }, f, indent=2) + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: python proc_mem_converter.py input.pb output.json") + sys.exit(1) + FixedFlameGraphConverter().convert(sys.argv[1], sys.argv[2]) \ No newline at end of file diff --git a/systrace/convert/convert_mem_to_flamegraph_for_cur.py b/systrace/convert/convert_mem_to_flamegraph_for_cur.py new file mode 100644 index 0000000..3880026 --- /dev/null +++ b/systrace/convert/convert_mem_to_flamegraph_for_cur.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python3 +import sys +import json +import os +import subprocess +from collections import defaultdict, deque +from concurrent.futures import ThreadPoolExecutor +from systrace_pb2 import ProcMem, StageType + +class FixedFlameGraphConverter: + def __init__(self): + self.stage_names = { + StageType.STAGE_UNKNOWN: "UNKNOWN", + StageType.STAGE_DATALOADER: "DATALOADER", + StageType.STAGE_FORWARD: "FORWARD", + StageType.STAGE_BACKWARD: "BACKWARD", + StageType.STAGE_SYNCHRONIZATION: "SYNCHRONIZATION", + getattr(StageType, "STAGE_GC", 5): "GC" + } + self.symbol_cache = {} + self.so_path_cache = {} + self.executor = ThreadPoolExecutor(max_workers=os.cpu_count() or 4) + + def convert(self, input_pb, output_json): + proc_mem = self._load_proc_mem(input_pb) + alloc_groups = self._analyze_allocations(proc_mem) + self._precache_symbols(alloc_groups) + + trace_events = [] + current_ts = 0 + alloc_records = {alloc.alloc_ptr: alloc for alloc in proc_mem.mem_alloc_stacks} + stage_stats = defaultdict(lambda: {'allocated': 0, 'freed': 0}) + + # 统计分配和释放 + for alloc in proc_mem.mem_alloc_stacks: + stage_key = (alloc.stage_type, alloc.stage_id) + stage_stats[stage_key]['allocated'] += alloc.mem_size + for free in proc_mem.mem_free_stacks: + if free.alloc_ptr in alloc_records: + alloc = alloc_records[free.alloc_ptr] + stage_key = (free.stage_type, free.stage_id) + stage_stats[stage_key]['freed'] += alloc.mem_size + + # 按stage_name分组(仅一次) + stage_data = defaultdict(list) + for (stage_type, stage_id), allocs in alloc_groups.items(): + stage_name = f"{stage_id}_{self.stage_names.get(stage_type, 'UNKNOWN')}" + stage_data[stage_name].extend(allocs) + + # 计算累计分配和持有内存 + cumulative_alloc = 0 + stage_alloc_info = {} + for stage_name, allocs in stage_data.items(): + stage_key = next(k for k in alloc_groups.keys() + if f"{k[1]}_{self.stage_names.get(k[0], 'UNKNOWN')}" == stage_name) + current_alloc = sum(a.mem_size for a in allocs) + current_free = stage_stats[stage_key]['freed'] + cumulative_alloc += (current_alloc - current_free) + held_memory = max(cumulative_alloc, 0) + stage_alloc_info[stage_name] = { + 'allocated': current_alloc, + 'freed': current_free, + 'held': held_memory # 避免负数 + } + cumulative_alloc += current_alloc + + # 生成时间轴 + for stage_name, allocs in stage_data.items(): + if stage_name.startswith(("0_", "1_", "2_")): + continue + + stage_events = [] + min_ts = current_ts # 使用严格连续的时间戳 + allocated_size = sum(a.mem_size for a in allocs) + max_ts = min_ts + allocated_size # 时间范围 = 新分配的内存 + + # 容器事件(时间范围反映新分配的内存) + container_event = { + "name": stage_name, + "ph": "X", + "ts": min_ts, + "dur": stage_alloc_info[stage_name]['held'] / 10000000, # 等于allocated_size + "pid": proc_mem.pid, + "tid": 1, + "args": { + "stage_type": self.stage_names.get(next(iter(alloc_groups.keys()))[0], "UNKNOWN"), + "stage_id": next(iter(alloc_groups.keys()))[1], + "is_container": True, + "allocated": stage_alloc_info[stage_name]['allocated'], + "freed": stage_alloc_info[stage_name]['freed'], + "held": stage_alloc_info[stage_name]['held'] # 持有的内存量(元数据) + } + } + stage_events.append(container_event) + + alloc_start_ts = min_ts + for alloc in allocs: + alloc_events, _ = self._process_allocation(alloc, proc_mem.pid, alloc_start_ts) + stage_events.extend(alloc_events) + alloc_start_ts += alloc.mem_size + + trace_events.extend(self._merge_calls(stage_events, stage_name)) + current_ts = max_ts + + self._save_json(output_json, trace_events) + self.executor.shutdown() + + def _merge_calls(self, events, stage_name): + """合并相同stage下的同名调用""" + # 分离容器事件和调用事件 + container = [e for e in events if e.get("args", {}).get("is_container")][0] + calls = [e for e in events if not e.get("args", {}).get("is_container")] + + # 按深度和名称分组 + call_groups = defaultdict(list) + for e in calls: + key = (e["args"]["depth"], e["name"]) + call_groups[key].append(e) + + # 合并每组调用 + merged_calls = [] + for (depth, name), group in call_groups.items(): + if len(group) == 1: + merged_calls.extend(group) + continue + + group.sort(key=lambda x: x["ts"]) + current = dict(group[0]) + + for e in group[1:]: + if e["ts"] == current["ts"] + current["dur"]: + current["dur"] += e["dur"] + current["args"]["bytes"] += e["args"]["bytes"] + if "merged_ptrs" not in current["args"]: + current["args"]["merged_ptrs"] = [current["args"]["alloc_ptr"]] + current["args"]["merged_ptrs"].append(e["args"]["alloc_ptr"]) + else: + if "merged_ptrs" in current["args"]: + current["args"]["alloc_ptr"] = ",".join(current["args"]["merged_ptrs"]) + del current["args"]["merged_ptrs"] + merged_calls.append(current) + current = dict(e) + + if "merged_ptrs" in current["args"]: + current["args"]["alloc_ptr"] = ",".join(current["args"]["merged_ptrs"]) + del current["args"]["merged_ptrs"] + merged_calls.append(current) + + # 确保容器事件在最前 + return [container] + sorted(merged_calls, key=lambda x: x["ts"]) + + def _process_allocation(self, alloc, pid, base_ts): + """处理单个分配事件""" + events = [] + alloc_duration = alloc.mem_size + + # 构建调用栈树 + call_tree = { + "name": "[root]", + "duration": alloc_duration, + "children": [] + } + current_parent = call_tree + + for frame in alloc.stack_frames: + so_name = os.path.basename(frame.so_name) + symbol = self._resolve_symbol(so_name, frame.address) + node = { + "name": symbol, + "duration": alloc_duration, + "children": [] + } + current_parent["children"].append(node) + current_parent = node + + # 调整duration + def adjust_durations(node): + if node["children"]: + node["duration"] = sum(adjust_durations(child) for child in node["children"]) + return node["duration"] + adjust_durations(call_tree) + + # 生成事件(BFS遍历) + stack = deque([(call_tree, base_ts, 0)]) + call_events = [] + while stack: + node, ts, depth = stack.popleft() + call_events.append({ + "name": node["name"], + "ph": "X", + "ts": ts, + "dur": node["duration"], + "pid": pid, + "tid": 2, + "args": { + "depth": depth, + "bytes": alloc.mem_size, + "alloc_ptr": f"0x{alloc.alloc_ptr:x}" + } + }) + for child in reversed(node["children"]): + stack.appendleft((child, ts, depth + 1)) + + return call_events, alloc_duration + + # 保留其他基础方法 + def _load_proc_mem(self, path): + with open(path, "rb") as f: + proc_mem = ProcMem() + proc_mem.ParseFromString(f.read()) + return proc_mem + + def _analyze_allocations(self, proc_mem): + freed_ptrs = {free.alloc_ptr for free in proc_mem.mem_free_stacks} + active_allocs = defaultdict(list) + for alloc in proc_mem.mem_alloc_stacks: + #if alloc.alloc_ptr not in freed_ptrs: + active_allocs[(alloc.stage_type, alloc.stage_id)].append(alloc) + return active_allocs + + def _precache_symbols(self, alloc_groups): + unique_frames = set() + for allocs in alloc_groups.values(): + for alloc in allocs: + for frame in alloc.stack_frames: + so_name = os.path.basename(frame.so_name) + unique_frames.add((so_name, frame.address)) + list(self.executor.map(lambda args: self._resolve_symbol(*args), unique_frames)) + + def _resolve_symbol(self, so_name, address): + cache_key = f"{so_name}:{address:x}" + if cache_key in self.symbol_cache: + return self.symbol_cache[cache_key] + + so_path = self._find_so_path(so_name) + if not so_path: + symbol = f"{so_name}@0x{address:x}" + self.symbol_cache[cache_key] = symbol + return symbol + + try: + result = subprocess.run( + ["addr2line", "-e", so_path, "-f", "-C", "-p", f"0x{address:x}"], + capture_output=True, text=True, timeout=0.05 + ) + func_name = result.stdout.split(" at ")[0].split("(")[0].strip() if result.returncode == 0 else "" + symbol = f"{so_name}@{func_name}" if func_name else f"{so_name}@0x{address:x}" + except: + symbol = f"{so_name}@0x{address:x}" + + self.symbol_cache[cache_key] = symbol + return symbol + + def _find_so_path(self, so_name): + if so_name in self.so_path_cache: + return self.so_path_cache[so_name] + + if os.path.isabs(so_name) and os.path.exists(so_name): + self.so_path_cache[so_name] = so_name + return so_name + + base_name = os.path.basename(so_name) + search_paths = [ + "/usr/lib", "/usr/local/lib", "/lib", + *os.getenv("LD_LIBRARY_PATH", "").split(":"), + *os.getenv("PATH", "").split(":") + ] + + for path in filter(os.path.isdir, search_paths): + test_path = os.path.join(path, base_name) + if os.path.exists(test_path): + self.so_path_cache[so_name] = test_path + return test_path + + if base_name.startswith("lib") and ".so" in base_name: + lib_prefix = base_name.split(".so")[0] + for ext in ["", ".1", ".2", ".3", ".4", ".5"]: + test_path = os.path.join(path, f"{lib_prefix}.so{ext}") + if os.path.exists(test_path): + self.so_path_cache[so_name] = test_path + return test_path + + self.so_path_cache[so_name] = None + return None + + def _save_json(self, path, trace_events): + if os.path.isdir(path): + input_name = os.path.splitext(os.path.basename(sys.argv[1]))[0] + path = os.path.join(path, f"{input_name}_fixed_flamegraph.json") + + with open(path, "w") as f: + json.dump({ + "traceEvents": sorted(trace_events, key=lambda x: x["ts"]), + "displayTimeUnit": "ns", + "metadata": { + "format": "FixedFlameGraph", + "stage_order": list(self.stage_names.values()) + } + }, f, indent=2) + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: python proc_mem_converter.py input.pb output.json") + sys.exit(1) + FixedFlameGraphConverter().convert(sys.argv[1], sys.argv[2]) \ No newline at end of file diff --git a/systrace/convert/convert_mspti_timeline.py b/systrace/convert/convert_mspti_timeline.py new file mode 100644 index 0000000..d8bbcf8 --- /dev/null +++ b/systrace/convert/convert_mspti_timeline.py @@ -0,0 +1,129 @@ +# coding=utf-8 +""" +Copyright (c) Huawei Technologies Co., Ltd. 2020-2028. All rights reserved. +Description: +FileName:slow_node_detection.py +Author: h00568282/huangbin +Create Date: 2025/3/26 11:23 +Notes: + +""" +import os +import json +import pandas as pd +from convert_json2csv import convert_jsons2csv + +__all__ = ['convert_mspti_timeline'] + +MODE = { + 0: "Host", + 1: "Device" +} +OP_COLORS = { + 'HcclAllreduce': "good", + 'HcclAllReduce': "good", + 'HcclAllGather': "bad", + 'HcclBroadcast': "yellow", + 'HcclReduceScatter': "olive", + 'HcclSend': "good", + 'HcclReceive': "good", + 'HcclBatchSendRecv': "thread_state_runnable" +} + + +def create_args(row): + return { + "id": row["Id"], + "comm_group": row["comm_group"], + "count": row["count"] + } + + +def split_df(df): + """ + 根据 mode 列将 DataFrame 拆分为 host 和 device 两个 DataFrame + """ + df_host = df[df['SourceKind'] == 0] + df_device = df[df['SourceKind'] == 1] + return df_host, df_device + + +def process_df(data_df, device_id, id2name_dict: dict): + """ + 对 DataFrame 进行处理,包括分组聚合、列拆分、添加新列等操作 + """ + + data_df["Name"] = data_df['Id'].map(id2name_dict) + df = data_df.groupby('Id').agg({ + 'Timestamp': ['min', 'max'], + 'Kind': 'first', + 'SourceKind': 'first', + 'Name': 'first', + }).reset_index() + df.columns = ['Id', 'start', 'end', 'Kind', 'SourceKind', 'Name'] + df[['comm_op', 'comm_group', 'data_type', 'count']] = df['Name'].str.replace('comm:', '').str.split(',', + expand=True) + df = df.drop(columns=['Name']) + df['cat'] = "hccl" + df['name'] = df['comm_op'] + df['cname'] = df['comm_op'].map(OP_COLORS) + df['end'] = df['end'] / 1000. + df['start'] = df['start'] / 1000. + df['dur'] = df['end'] - df['start'] + df['ph'] = "X" + df['pid'] = f"rank_{device_id}" + df['tid'] = df["SourceKind"].map(MODE) + df['args'] = df.apply(create_args, axis=1) + result = df[['cat', 'name', 'ph', 'pid', 'tid', 'start', 'dur', 'cname', 'args']].rename( + columns={'start': 'ts'}).to_dict(orient='records') + return result + + +def process_files(root_path, debug: bool = False): + """ + 处理指定路径下的所有 CSV 文件 + """ + csv_files = [file for file in os.listdir(root_path) if file.endswith("csv") and "device" not in file] + all_ranks = [] + for csv_file in csv_files: + print(f"start file: {csv_file}") + csv_file_path = os.path.join(root_path, csv_file) + df = pd.read_csv(csv_file_path) + if debug: + df = df.head(12) + + id2name_dict = df[df['Name'].notna()].set_index('Id')['Name'].to_dict() + # df['name'] = df.groupby('id')['name'].transform(lambda x: x.ffill().bfill()) + df_host, df_device = split_df(df) + device_id = df_device['msptiObjecId_Ds_DeviceId'].unique()[0] + host_result = process_df(df_host, device_id, id2name_dict) + all_ranks.extend(host_result) + device_result = process_df(df_device, device_id, id2name_dict) + all_ranks.extend(device_result) + return all_ranks + + +def save_to_json(all_ranks, files_path): + """ + 将处理结果保存为 JSON 文件 + """ + output = { + "traceEvents": all_ranks, + "stackFrames": {} + } + json_output = json.dumps(output, indent=4) + with open(os.path.join(files_path, f'mspti_comm_ops_timeline.json'), 'w') as f: + f.write(json_output) + + +def convert_mspti_timeline(data_path: str): + convert_jsons2csv(data_path) + all_ranks = process_files(data_path) + save_to_json(all_ranks, data_path) + + +if __name__ == "__main__": + files_path = "D:\\startwork\\AOPS\\09-25年技术规划\\Code\\mspti_test-megatron-0224\\mspti_test-megatron-0224\\data\\log\\all_merge" + convert_jsons2csv(files_path) + all_ranks = process_files(files_path) + save_to_json(all_ranks, files_path) \ No newline at end of file diff --git a/systrace/convert/convert_pytorch_to_timeline.py b/systrace/convert/convert_pytorch_to_timeline.py new file mode 100644 index 0000000..bb42d8b --- /dev/null +++ b/systrace/convert/convert_pytorch_to_timeline.py @@ -0,0 +1,50 @@ +import json +import systrace_pb2 +import argparse +import glob + +def process_timeline_file(input_path, trace_data): + with open(input_path, "rb") as f: + pytorch_data = systrace_pb2.Pytorch() + pytorch_data.ParseFromString(f.read()) + + for stage in pytorch_data.pytorch_stages: + trace_data["traceEvents"].append({ + "name": stage.stage_type, + "cat": "pytorch", + "ph": "X", + "pid": pytorch_data.rank, + "tid": pytorch_data.rank if "GC" not in stage.stage_type else f"{pytorch_data.rank}:gc", + "ts": stage.start_us, + "dur": stage.end_us - stage.start_us, + "args": { + "stage_id": stage.stage_id, + "comm": pytorch_data.comm, + "stack_frames": list(stage.stack_frames), + "gc_collected": stage.gc_debug.collected if stage.HasField("gc_debug") else 0, + "gc_uncollectable": stage.gc_debug.uncollectable if stage.HasField("gc_debug") else 0 + } + }) + +def aggregate_timeline_files(output_path): + trace_data = { + "traceEvents": [], + "displayTimeUnit": "ns", + "metadata": {"format": "Pytorch Profiler"} + } + + for timeline_file in glob.glob("*timeline"): + print(f"Processing {timeline_file}") + process_timeline_file(timeline_file, trace_data) + + trace_data["traceEvents"].sort(key=lambda x: x["args"]["stage_id"]) + + with open(output_path, "w") as f: + json.dump(trace_data, f, indent=None, separators=(',', ':')) + print(f"Aggregated {len(trace_data['traceEvents'])} events to {output_path}") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Aggregate all *.timeline files into a single JSON') + parser.add_argument('--output', required=True, help='Output JSON file path') + args = parser.parse_args() + aggregate_timeline_files(args.output) \ No newline at end of file diff --git a/systrace/hack/format.sh b/systrace/hack/format.sh new file mode 100644 index 0000000..9a05c76 --- /dev/null +++ b/systrace/hack/format.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +function install_clang_format() { + if ! command -v clang-format &> /dev/null; then + echo "Installing clang-format..." + if command -v apt &> /dev/null; then + sudo apt install -y clang-format + elif command -v yum &> /dev/null; then + sudo yum install -y clang-format + else + echo "Error: Cannot install clang-format (unsupported package manager)." + exit 1 + fi + else + echo "clang-format is already installed." + fi +} + +function setup_clang_format() { + local clang_format_file=".clang-format" + if [ ! -f "$clang_format_file" ]; then + echo "Creating .clang-format with 4-space indentation and Allman braces..." + cat > "$clang_format_file" < + +namespace systrace +{ +namespace constant +{ + +struct TorchTraceConstant +{ + public: + static constexpr int DEFAULT_TRACE_COUNT = 1000; + static constexpr std::string_view DEFAULT_TRACE_DUMP_PATH = + SYS_TRACE_ROOT_DIR "timeline"; +}; + +} // namespace constant +} // namespace systrace \ No newline at end of file diff --git a/systrace/include/common/logging.cc b/systrace/include/common/logging.cc new file mode 100644 index 0000000..f7dec97 --- /dev/null +++ b/systrace/include/common/logging.cc @@ -0,0 +1,6 @@ +#include "logging.h" + +namespace systrace +{ +void setLoggingPath() { return; } +} // namespace systrace \ No newline at end of file diff --git a/systrace/include/common/logging.h b/systrace/include/common/logging.h new file mode 100644 index 0000000..dc7ef35 --- /dev/null +++ b/systrace/include/common/logging.h @@ -0,0 +1,28 @@ +#pragma once + +enum LogLevel +{ + INFO, + WARNING, + ERROR, + FATAL +}; + +#define LOG(level) \ + if (level == INFO) \ + std::cerr << "[INFO] "; \ + else if (level == WARNING) \ + std::cerr << "[WARNING] "; \ + else if (level == ERROR) \ + std::cerr << "[ERROR] "; \ + else if (level == FATAL) \ + std::cerr << "[FATAL] "; \ + std::cerr + +#define STLOG(level) \ + LOG(level) << ::systrace::util::config::GlobalConfig::Instance().rank_str + +namespace systrace +{ +void setLoggingPath(); +} \ No newline at end of file diff --git a/systrace/include/common/macro.h b/systrace/include/common/macro.h new file mode 100644 index 0000000..56d60ab --- /dev/null +++ b/systrace/include/common/macro.h @@ -0,0 +1,17 @@ +#pragma once +#define EXPOSE_API __attribute__((visibility("default"))) + +#define SETUP_SYMBOL_FOR_LOAD_LIBRARY(handle, symbol, func_ptr, func_type, \ + msg) \ + do \ + { \ + func_ptr = (func_type)dlsym(handle, symbol); \ + const char *dlsym_error = dlerror(); \ + if (dlsym_error) \ + { \ + STLOG(WARNING) << "Load fn `" << symbol << "` error in " << msg \ + << dlsym_error; \ + is_usable_ = false; \ + return; \ + } \ + } while (0) diff --git a/systrace/include/common/shared_constants.h b/systrace/include/common/shared_constants.h new file mode 100644 index 0000000..d4408e4 --- /dev/null +++ b/systrace/include/common/shared_constants.h @@ -0,0 +1,12 @@ +#ifdef __cplusplus +extern "C" +{ +#endif + + extern int global_stage_id; + extern int global_stage_type; +#define SYS_TRACE_ROOT_DIR "/home/sysTrace/" + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/systrace/include/common/util.cc b/systrace/include/common/util.cc new file mode 100644 index 0000000..a526d0c --- /dev/null +++ b/systrace/include/common/util.cc @@ -0,0 +1,253 @@ +#include "util.h" +#include "constant.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace systrace +{ +namespace util +{ + +namespace env +{ +std::string_view EnvVarRegistry::DEFAULT_VALUE_STRING = "NONE"; +int EnvVarRegistry::DEFAULT_VALUE_INT = 0; +bool EnvVarRegistry::DEFAULT_VALUE_BOOL = false; +} // namespace env +namespace fs_utils +{ + +int CreateDirectoryIfNotExists(const std::string &path) +{ + std::filesystem::path d_path(path); + try + { + if (!std::filesystem::exists(d_path)) + { + std::filesystem::create_directories(d_path); + } + if (!std::filesystem::is_directory(d_path)) + { + LOG(ERROR) << "Path exists but is not a directory: " << path; + return 1; + } + } + catch (const std::filesystem::filesystem_error &e) + { + LOG(ERROR) << "Failed to create directory " << path << ": " << e.what(); + return 1; + } + return 0; +} + +std::string GenerateClusterUniqueFilename(const std::string &suffix) +{ + try + { + char hostname[128]; + gethostname(hostname, sizeof(hostname)); + std::ostringstream oss; + oss << hostname << "--" << std::setw(5) << std::setfill('0') + << config::GlobalConfig::Instance().rank << suffix; + return oss.str(); + } + catch (const std::exception &e) + { + LOG(ERROR) << "Filename generation failed: " << e.what(); + return "error_" + std::to_string(std::time(nullptr)) + suffix; + } +} + +} // namespace fs_utils + +namespace config +{ + +class DeviceManager +{ + public: + static constexpr uint64_t MAX_DEVICES = 16; + static constexpr const char *DEVICE_PATH_PREFIX = "/dev/davinci"; + + static std::vector DetectAvailableDevices() + { + std::vector available_devices; + available_devices.reserve(MAX_DEVICES); + + for (uint64_t device_index = 0; device_index < MAX_DEVICES; + ++device_index) + { + if (IsDevicePresent(device_index)) + { + available_devices.push_back(device_index); + if (config::GlobalConfig::Instance().local_rank == 0) + { + LOG(INFO) + << "Found device: " << GetDevicePath(device_index); + } + } + } + + std::sort(available_devices.begin(), available_devices.end()); + return available_devices; + } + + private: + static bool IsDevicePresent(uint64_t index) + { + return std::filesystem::exists(GetDevicePath(index)); + } + + static std::string GetDevicePath(uint64_t index) + { + return std::string(DEVICE_PATH_PREFIX) + std::to_string(index); + } +}; + +namespace +{ + +GlobalConfig &config = GlobalConfig::Instance(); + +void LoadEnvironmentVariables() +{ + auto loadInt = [](const char *name) + { return env::EnvVarRegistry::GetEnvVar(name); }; + + auto loadStr = [](const char *name) + { return env::EnvVarRegistry::GetEnvVar(name); }; + + config.rank = loadInt("RANK"); + config.job_name = loadStr("ENV_ARGO_WORKFLOW_NAME"); + config.local_rank = loadInt("LOCAL_RANK"); + config.local_world_size = loadInt("LOCAL_WORLD_SIZE"); + config.world_size = loadInt("WORLD_SIZE"); + config.rank_str = "[RANK " + std::to_string(config.rank) + "] "; +} + +void ValidateDeviceConfiguration() +{ + config.devices = DeviceManager::DetectAvailableDevices(); + + if (config.devices.empty()) + { + config.enable = false; + LOG(WARNING) << "No devices found, disabling tracing"; + return; + } + + if (config.local_world_size != config.devices.size()) + { + LOG(WARNING) << "Local world size mismatch, disabling hook"; + config.enable = false; + } +} + +} // namespace + +void InitializeGlobalConfiguration() +{ + LOG(INFO) << "Initializing global configuration"; + + try + { + LoadEnvironmentVariables(); + ValidateDeviceConfiguration(); + LOG(INFO) << "Global configuration initialized successfully"; + } + catch (const std::exception &e) + { + LOG(ERROR) << "Global config initialization failed: " << e.what(); + throw; + } +} + +} // namespace config + +namespace environment +{ + +bool IsValidEnvironmentVariableName(const std::string &name) +{ + if (name.empty() || !isalpha(name[0])) + { + return false; + } + + for (char c : name) + { + if (!isalnum(c) && c != '_') + { + return false; + } + } + return true; +} + +void RegisterRequiredEnvironmentVariables() +{ + try + { + if (!IsValidEnvironmentVariableName("ENV_ARGO_WORKFLOW_NAME")) + { + throw std::invalid_argument( + "Invalid env var name: ENV_ARGO_WORKFLOW_NAME"); + } + REGISTER_ENVIRONMENT_VARIABLE( + "ENV_ARGO_WORKFLOW_NAME", + env::EnvVarRegistry::DEFAULT_VALUE_STRING); + + if (!IsValidEnvironmentVariableName("SYSTRACE_SYMS_FILE")) + { + throw std::invalid_argument( + "Invalid env var name: SYSTRACE_SYMS_FILE"); + } + REGISTER_ENVIRONMENT_VARIABLE( + "SYSTRACE_SYMS_FILE", env::EnvVarRegistry::DEFAULT_VALUE_STRING); + + if (!IsValidEnvironmentVariableName("SYSTRACE_LOGGING_DIR")) + { + throw std::invalid_argument( + "Invalid env var name: SYSTRACE_LOGGING_DIR"); + } + REGISTER_ENVIRONMENT_VARIABLE( + "SYSTRACE_LOGGING_DIR", env::EnvVarRegistry::DEFAULT_VALUE_STRING); + + if (!IsValidEnvironmentVariableName("SYSTRACE_HOST_TRACING_FUNC")) + { + throw std::invalid_argument( + "Invalid env var name: SYSTRACE_HOST_TRACING_FUNC"); + } + REGISTER_ENVIRONMENT_VARIABLE( + "SYSTRACE_HOST_TRACING_FUNC", + env::EnvVarRegistry::DEFAULT_VALUE_STRING); + + REGISTER_ENVIRONMENT_VARIABLE("RANK", 0); + REGISTER_ENVIRONMENT_VARIABLE("LOCAL_RANK", 0); + REGISTER_ENVIRONMENT_VARIABLE("LOCAL_WORLD_SIZE", 1); + REGISTER_ENVIRONMENT_VARIABLE("WORLD_SIZE", 1); + REGISTER_ENVIRONMENT_VARIABLE("SYSTRACE_LOGGING_APPEND", false); + } + catch (const std::exception &e) + { + LOG(ERROR) << "Environment variable registration failed: " << e.what(); + throw; + } +} + +} // namespace environment + +void InitializeSystemUtilities() +{ + environment::RegisterRequiredEnvironmentVariables(); + config::InitializeGlobalConfiguration(); +} + +} // namespace util +} // namespace systrace \ No newline at end of file diff --git a/systrace/include/common/util.h b/systrace/include/common/util.h new file mode 100644 index 0000000..67c077a --- /dev/null +++ b/systrace/include/common/util.h @@ -0,0 +1,290 @@ +#pragma once + +#include "logging.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace systrace +{ +namespace util +{ +namespace config +{ + +struct GlobalConfig +{ + uint32_t rank{0}; + uint32_t local_rank{0}; + uint32_t world_size{0}; + uint32_t local_world_size{0}; + std::string job_name; + bool enable{true}; + std::vector devices; + std::string rank_str; + + static GlobalConfig &Instance() + { + static GlobalConfig instance; + return instance; + } + + private: + GlobalConfig() = default; +}; + +void InitializeGlobalConfiguration(); + +} // namespace config + +namespace fs_utils +{ + +std::string GenerateClusterUniqueFilename(const std::string &suffix); +int CreateDirectoryIfNotExists(const std::string &path); + +} // namespace fs_utils + +namespace resource +{ +template class TimerPool +{ + public: + TimerPool() = default; + TimerPool(const TimerPool &) = delete; + TimerPool &operator=(const TimerPool &) = delete; + + template T *getObject() + { + std::lock_guard lock(mutex_); + + T *obj = pool_.empty() ? nullptr : pool_.front(); + if (obj) + { + pool_.pop_front(); + } + + return obj ? obj : (Init ? new T() : nullptr); + } + + void returnObject(T *obj, int *size) + { + if (!obj) + { + if (size) + *size = 0; + return; + } + + std::lock_guard lock(mutex_); + pool_.push_back(obj); + if (size) + *size = static_cast(pool_.size()); + } + + void clear() + { + std::lock_guard lock(mutex_); + for (auto obj : pool_) + { + delete obj; + } + pool_.clear(); + } + + ~TimerPool() { clear(); } + + private: + std::deque pool_; + std::mutex mutex_; +}; + +} // namespace resource + +namespace env +{ + +class EnvVarRegistry +{ + public: + using VarType = std::variant; + + static std::string_view DEFAULT_VALUE_STRING; + static int DEFAULT_VALUE_INT; + static bool DEFAULT_VALUE_BOOL; + + static void RegisterEnv(const std::string &name, VarType default_value) + { + auto ®istry = GetRegistryManager(); + LOG(INFO) << "[ENV] Register ENV " << name << " with default " + << VariantToString(default_value) << std::endl; + registry[name] = std::move(default_value); + } + + // Get an env var value, with optional printing + template static T GetEnvVar(const std::string &name) + { + static_assert(is_supported_type(), + "Unsupported type for environment variable"); + + auto ®istry = GetRegistryManager(); + bool set = false; + + // Try to get from environment first + T result = getEnvInner(name, &set); + if (set) + { + LOG(INFO) << "[ENV] Get " << name << "=" << result + << " from environment" << std::endl; + return result; + } + + // Try to get from registered defaults + if (auto it = registry.find(name); it != registry.end()) + { + if (const T *val = std::get_if(&it->second)) + { + LOG(INFO) << "[ENV] Get " << name << "=" << *val + << " from register default" << std::endl; + return *val; + } + LOG(FATAL) << "[ENV] Wrong data type in `GetEnvVar`" << std::endl; + } + + // Fall back to static default + result = getDefault(); + LOG(WARNING) << "[ENV] Get not register env " << name << "=" << result + << " from default" << std::endl; + return result; + } + + template + static inline auto convert_to_variant(const T &s) + -> std::enable_if_t, VarType> + { + return std::string(s); + } + + template + static inline auto convert_to_variant(const T &val) + -> std::enable_if_t, VarType> + { + return val; + } + + private: + template static constexpr bool is_supported_type() + { + return std::is_same_v || std::is_same_v || + std::is_same_v; + } + + static std::string toLower(const std::string &str) + { + std::string lower; + lower.reserve(str.size()); + std::transform(str.begin(), str.end(), std::back_inserter(lower), + [](unsigned char c) { return std::tolower(c); }); + return lower; + } + + // 值解析器 + template static T parseEnvValue(const char *env) + { + if constexpr (std::is_same_v) + { + try + { + return std::stoi(env); + } + catch (...) + { + return DEFAULT_VALUE_INT; + } + } + else if constexpr (std::is_same_v) + { + std::string lower = toLower(env); + if (lower == "true" || lower == "1") + return true; + if (lower == "false" || lower == "0") + return false; + return std::stoi(env) != 0; + } + else if constexpr (std::is_same_v) + { + return env; + } + } + + // Get value from real environment + template + static T getEnvInner(const std::string &env_name, bool *set) + { + const char *env = std::getenv(env_name.c_str()); + if (!env) + { + *set = false; + return {}; + } + + *set = true; + return parseEnvValue(env); + } + + // Default values for fallback + template static T getDefault() + { + if constexpr (std::is_same_v) + { + return DEFAULT_VALUE_INT; + } + else if constexpr (std::is_same_v) + { + return DEFAULT_VALUE_BOOL; + } + else if constexpr (std::is_same_v) + { + return std::string(DEFAULT_VALUE_STRING); + } + } + + static inline std::unordered_map &GetRegistryManager() + { + static std::unordered_map registry_manager; + return registry_manager; + } + + static std::string VariantToString(const VarType &var) + { + return std::visit( + [](const auto &value) + { + std::stringstream ss; + ss << value; + return ss.str(); + }, + var); + } +}; + +#define REGISTER_ENVIRONMENT_VARIABLE(name, value) \ + ::systrace::util::env::EnvVarRegistry::RegisterEnv( \ + name, \ + ::systrace::util::env::EnvVarRegistry::convert_to_variant(value)) + +void REGISTER_ENV(); + +} // namespace env +void InitializeSystemUtilities(); +} // namespace util +} // namespace systrace \ No newline at end of file diff --git a/systrace/protos/CMakeLists.txt b/systrace/protos/CMakeLists.txt new file mode 100644 index 0000000..56eb91f --- /dev/null +++ b/systrace/protos/CMakeLists.txt @@ -0,0 +1,17 @@ +project(general_pb2) + +set(PB_FILES systrace.pb.cc systrace.pb-c.c) + +add_library(${PROJECT_NAME} STATIC ${PB_FILES}) + +include_directories(${PROJECT_SOURCE_DIR} + ${GOOGLE_PROTOBUF_DIR}/include +) + +link_directories(${GOOGLE_PROTOBUF_DIR}/lib/) + +target_link_libraries(${PROJECT_NAME} + protobuf + protobuf-c +) + diff --git a/systrace/protos/systrace.proto b/systrace/protos/systrace.proto new file mode 100644 index 0000000..aa0e8f7 --- /dev/null +++ b/systrace/protos/systrace.proto @@ -0,0 +1,62 @@ +syntax = "proto3"; + +message StackFrame { + uint64 address = 1; + string so_name = 2; +} + +message MemAllocEntry { + uint64 alloc_ptr = 1; + uint32 stage_id = 2; + StageType stage_type = 3; + uint64 mem_size = 4; + repeated StackFrame stack_frames = 5; +} + +message MemFreeEntry { + uint64 alloc_ptr = 1; + uint32 stage_id = 2; + StageType stage_type = 3; +} + +message ProcMem { + uint32 pid = 1; + repeated MemAllocEntry mem_alloc_stacks = 2; + repeated MemFreeEntry mem_free_stacks = 3; +} + +enum StageType { + STAGE_UNKNOWN = 0; + STAGE_DATALOADER = 1; + STAGE_FORWARD = 2; + STAGE_BACKWARD = 3; + STAGE_SYNCHRONIZATION = 4; + STAGE_GC = 5; +} + +message GcDebugData { + uint32 collected = 1; + uint32 uncollectable = 2; +} + +message PytorchStage { + uint32 stage_id = 1; + string stage_type = 2; + uint64 start_us = 3; + uint64 end_us = 4; + repeated string stack_frames = 5; + oneof debug_data { + GcDebugData gc_debug = 6; + } +} + +message Pytorch { + repeated PytorchStage pytorch_stages = 1; + uint32 rank = 2; + uint32 step_id = 3; + string comm = 4; //任务名 +} + +message Mem { + repeated ProcMem proc_mem = 1; +} \ No newline at end of file diff --git a/systrace/src/ascend/hook.cc b/systrace/src/ascend/hook.cc new file mode 100644 index 0000000..c168128 --- /dev/null +++ b/systrace/src/ascend/hook.cc @@ -0,0 +1,74 @@ +#include "hook.h" +#include "../src/trace/systrace_manager.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + static void *load_symbol(const char *func_name) + { + if (!g_hal_lib) + { + g_hal_lib = dlopen("libascendcl.so", RTLD_LAZY); + if (!g_hal_lib) + { + fprintf(stderr, "[Hook] Failed to dlopen libascendcl.so: %s\n", + dlerror()); + return nullptr; + } + } + + void *func = dlsym(g_hal_lib, func_name); + if (!func) + { + fprintf(stderr, "[Hook] Failed to dlsym %s: %s\n", func_name, + dlerror()); + } + else + { + std::cout << "[Hook] Successfully hooked " << func_name + << std::endl; + } + return func; + } + +#define HOOKED_FUNCTION(func_ptr, func_name, ...) \ + if (!func_ptr) \ + { \ + func_ptr = (decltype(func_ptr))load_symbol(func_name); \ + if (!func_ptr) \ + return -1; \ + } \ + ::systrace::SysTrace::getInstance(); \ + return func_ptr(__VA_ARGS__); + + EXPOSE_API aclError aclInit(const char *configPath) + { + HOOKED_FUNCTION(orig_aclInit, "aclInit", configPath); + } + + EXPOSE_API aclError aclrtMapMem(void *virPtr, size_t size, size_t offset, + aclrtDrvMemHandle handle, uint64_t flags) + { + HOOKED_FUNCTION(orig_aclrtMapMem, "aclrtMapMem", virPtr, size, offset, + handle, flags); + } + + EXPOSE_API aclError aclrtLaunchKernel(aclrtFuncHandle func, int workDim, + void **workGroup, + size_t *localWorkSize, + aclrtStream stream, void *event, + void *config) + { + HOOKED_FUNCTION(orig_aclrtLaunchKernel, "aclrtLaunchKernel", func, + workDim, workGroup, localWorkSize, stream, event, + config); + } + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/systrace/src/ascend/hook.h b/systrace/src/ascend/hook.h new file mode 100644 index 0000000..adbe0a0 --- /dev/null +++ b/systrace/src/ascend/hook.h @@ -0,0 +1,41 @@ +#pragma once +#include "../../include/common/macro.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + typedef int aclError; + typedef void *aclrtStream; + typedef void *aclrtFuncHandle; + typedef void *aclrtDrvMemHandle; + + typedef aclError (*aclInitFn)(const char *); + typedef aclError (*aclrtMapMemFn)(void *, size_t, size_t, aclrtDrvMemHandle, + uint64_t); + typedef aclError (*aclrtLaunchKernelFn)(aclrtFuncHandle, int, void **, + size_t *, aclrtStream, void *, + void *); + + extern void *ascend_hal_handle; + extern aclInitFn orig_aclInit; + extern aclrtMapMemFn orig_aclrtMapMem; + extern aclrtLaunchKernelFn orig_aclrtLaunchKernel; + + aclError aclInit(const char *configPath); + aclError aclrtMapMem(void *virPtr, size_t size, size_t offset, + aclrtDrvMemHandle handle, uint64_t flags); + aclError aclrtLaunchKernel(aclrtFuncHandle func, int workDim, + void **workGroup, size_t *localWorkSize, + aclrtStream stream, void *event, void *config); + + static void *g_hal_lib = nullptr; + aclInitFn orig_aclInit = nullptr; + aclrtMapMemFn orig_aclrtMapMem = nullptr; + aclrtLaunchKernelFn orig_aclrtLaunchKernel = nullptr; +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/systrace/src/cann/cann_hook.c b/systrace/src/cann/cann_hook.c new file mode 100644 index 0000000..a1bd27d --- /dev/null +++ b/systrace/src/cann/cann_hook.c @@ -0,0 +1,532 @@ +#define _GNU_SOURCE +#include "../../include/common/shared_constants.h" +#include "../../protos/systrace.pb-c.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__aarch64__) +#include "../../thirdparty/aarch64/libunwind/libunwind.h" +#elif defined(__x86_64__) +#include "../../thirdparty/x86_64/libunwind/libunwind.h" +#else +#error "Unsupported architecture - only aarch64 and x86_64 are supported" +#endif + +// export LD_PRELOAD=/home/MindSpeed-LLM-1.0.RC3/libascend_hal_jack.so +// cd /home/hbdir/mspti_test-megatron +// conda activate mspti10 +// python -m torch.distributed.launch --nproc_per_node=8 nqq_train_fsdp.py +// protoc --c_out=. tmp.proto + +// drvError_t halMemAlloc(void **pp, unsigned long long size, unsigned long long +// flag); drvError_t halMemFree(void *pp); drvError_t +// halMemCreate(drv_mem_handle_t **handle, size_t size, const struct +// drv_mem_prop *prop, uint64_t flag); drvError_t halMemRelease +// (drv_mem_handle_t *handle); + +#define LOG_INTERVAL_SEC 120 +#define LOG_ITEMS_MIN 1000 + +typedef int drvError_t; + +typedef enum aclrtMemMallocPolicy +{ + ACL_MEM_MALLOC_HUGE_FIRST, + ACL_MEM_MALLOC_HUGE_ONLY, + ACL_MEM_MALLOC_NORMAL_ONLY, + ACL_MEM_MALLOC_HUGE_FIRST_P2P, + ACL_MEM_MALLOC_HUGE_ONLY_P2P, + ACL_MEM_MALLOC_NORMAL_ONLY_P2P, + ACL_MEM_TYPE_LOW_BAND_WIDTH = 0x0100, + ACL_MEM_TYPE_HIGH_BAND_WIDTH = 0x1000, +} aclrtMemMallocPolicy; +typedef drvError_t (*halMemAllocFunc_t)(void **pp, unsigned long long size, + unsigned long long flag); +typedef drvError_t (*halMemFreeFunc_t)(void *pp); +typedef drvError_t (*halMemCreateFunc_t)(void **handle, size_t size, void *prop, + uint64_t flag); +typedef drvError_t (*halMemReleaseFunc_t)(void *handle); + +typedef drvError_t (*aclrtMallocFunc_t)(void **devPtr, size_t size, + aclrtMemMallocPolicy policy); +typedef drvError_t (*aclrtMallocCachedFunc_t)(void **devPtr, size_t size, + aclrtMemMallocPolicy policy); +typedef drvError_t (*aclrtMallocAlign32Func_t)(void **devPtr, size_t size, + aclrtMemMallocPolicy policy); +typedef drvError_t (*aclrtFreeFunc_t)(void *devPtr); + +static halMemAllocFunc_t orig_halMemAlloc = NULL; +static halMemFreeFunc_t orig_halMemFree = NULL; +static halMemCreateFunc_t orig_halMemCreate = NULL; +static halMemReleaseFunc_t orig_halMemRelease = NULL; +static aclrtMallocFunc_t orig_aclrtMalloc = NULL; +static aclrtMallocCachedFunc_t orig_aclrtMallocCached = NULL; +static aclrtMallocAlign32Func_t orig_aclrtMallocAlign32 = NULL; +static aclrtFreeFunc_t orig_aclrtFree = NULL; + +static pthread_key_t thread_data_key; +static pthread_once_t key_once = PTHREAD_ONCE_INIT; +static pthread_mutex_t file_mutex = PTHREAD_MUTEX_INITIALIZER; +extern int global_stage_id; +extern int global_stage_type; + +typedef struct +{ + ProcMem *proc_mem; + time_t last_log_time; +} ThreadData; + +static void *load_symbol(void *lib, const char *symbol_name) +{ + void *sym = dlsym(lib, symbol_name); + if (!sym) + { + fprintf(stderr, "Failed to find symbol %s: %s\n", symbol_name, + dlerror()); + } + return sym; +} + +static void free_proc_mem(ProcMem *proc_mem) +{ + if (!proc_mem) + return; + + // 释放分配记录 + for (size_t i = 0; i < proc_mem->n_mem_alloc_stacks; i++) + { + MemAllocEntry *entry = proc_mem->mem_alloc_stacks[i]; + for (size_t j = 0; j < entry->n_stack_frames; j++) + { + free((void *)entry->stack_frames[j]->so_name); + free(entry->stack_frames[j]); + } + free(entry->stack_frames); + free(entry); + } + free(proc_mem->mem_alloc_stacks); + + // 释放释放记录 + for (size_t i = 0; i < proc_mem->n_mem_free_stacks; i++) + { + free(proc_mem->mem_free_stacks[i]); + } + free(proc_mem->mem_free_stacks); + + // 重置计数 + proc_mem->n_mem_alloc_stacks = 0; + proc_mem->mem_alloc_stacks = NULL; + proc_mem->n_mem_free_stacks = 0; + proc_mem->mem_free_stacks = NULL; +} + +static void free_thread_data(void *data) +{ + ThreadData *td = (ThreadData *)data; + if (td && td->proc_mem) + { + free_proc_mem(td->proc_mem); + free(td->proc_mem); + } + free(td); +} + +static inline uint32_t get_current_pid() { return (uint32_t)getpid(); } + +static void make_key() +{ + pthread_key_create(&thread_data_key, free_thread_data); +} + +static ThreadData *get_thread_data() +{ + ThreadData *td; + + pthread_once(&key_once, make_key); + td = pthread_getspecific(thread_data_key); + + if (!td) + { + td = calloc(1, sizeof(ThreadData)); + td->proc_mem = calloc(1, sizeof(ProcMem)); + proc_mem__init(td->proc_mem); + td->proc_mem->pid = get_current_pid(); + td->last_log_time = time(NULL); + pthread_setspecific(thread_data_key, td); + } + + return td; +} + +static const char *get_so_name(uint64_t ip) +{ + Dl_info info; + const char *so_name; + if (dladdr((void *)ip, &info)) + { + so_name = strrchr(info.dli_fname, '/'); + return (so_name != NULL) ? so_name + 1 : info.dli_fname; + } + return "unknown"; +} + +static void get_log_filename(time_t current, uint32_t pid, char *buf, + size_t buf_size) +{ + const char *rank_str = getenv("RANK"); + int rank = rank_str ? atoi(rank_str) : 0; + struct tm *tm = localtime(¤t); + + const char *dir_path = SYS_TRACE_ROOT_DIR "cann"; + if (access(dir_path, F_OK) != 0) + { + if (mkdir(dir_path, 0755) != 0 && errno != EEXIST) + { + perror("Failed to create directory"); + snprintf(buf, buf_size, "mem_trace_%04d%02d%02d_%02d_%u_rank%d.pb", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, pid, rank); + return; + } + } + snprintf(buf, buf_size, "%s/mem_trace_%04d%02d%02d_%02d_%u_rank%d.pb", + dir_path, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, pid, rank); +} + +static char is_ready_to_write(ThreadData *td, time_t *current) +{ + ProcMem *proc_mem = td->proc_mem; + if (!proc_mem || + (proc_mem->n_mem_alloc_stacks + proc_mem->n_mem_free_stacks == 0)) + { + return 0; + } + + *current = time(NULL); + if (proc_mem->n_mem_alloc_stacks + proc_mem->n_mem_free_stacks < + LOG_ITEMS_MIN) + { + if (*current - td->last_log_time < LOG_INTERVAL_SEC) + { + return 0; + } + } + + return 1; +} + +static void write_protobuf_to_file() +{ + time_t current; + uint8_t *buf; + ThreadData *td = get_thread_data(); + if (!td) + { + return; + } + + if (!is_ready_to_write(td, ¤t)) + { + return; + } + + if (pthread_mutex_trylock(&file_mutex) == 0) + { // pthread_mutex_trylock or pthread_mutex_lock + char filename[256]; + get_log_filename(current, td->proc_mem->pid, filename, + sizeof(filename)); + + size_t len = proc_mem__get_packed_size(td->proc_mem); + buf = malloc(len); + proc_mem__pack(td->proc_mem, buf); + + FILE *fp = fopen(filename, "ab"); + if (fp) + { + fwrite(buf, len, 1, fp); + fclose(fp); + } + + pthread_mutex_unlock(&file_mutex); + } + else + { + return; + } + + if (buf) + { + free(buf); + } + + free_proc_mem(td->proc_mem); + td->last_log_time = current; +} + +static void exit_handler(void) { write_protobuf_to_file(); } + +int init_mem_trace() +{ + void *lib = + dlopen("/usr/local/Ascend/ascend-toolkit/latest/lib64/libascendcl.so", + RTLD_LAZY); + if (!lib) + { + fprintf(stderr, "dlopen failed: %s\n", dlerror()); + return -1; + } + + orig_halMemAlloc = (halMemAllocFunc_t)load_symbol(lib, "halMemAlloc"); + orig_halMemFree = (halMemFreeFunc_t)load_symbol(lib, "halMemFree"); + orig_halMemCreate = (halMemCreateFunc_t)load_symbol(lib, "halMemCreate"); + orig_halMemRelease = (halMemReleaseFunc_t)load_symbol(lib, "halMemRelease"); + orig_aclrtMalloc = (aclrtMallocFunc_t)load_symbol(lib, "aclrtMalloc"); + orig_aclrtMallocCached = + (aclrtMallocCachedFunc_t)load_symbol(lib, "aclrtMallocCached"); + orig_aclrtMallocAlign32 = + (aclrtMallocAlign32Func_t)load_symbol(lib, "aclrtMallocAlign32"); + orig_aclrtFree = (aclrtFreeFunc_t)load_symbol(lib, "aclrtFree"); + + if (!orig_halMemAlloc || !orig_halMemFree || !orig_aclrtMalloc || + !orig_aclrtFree || !orig_halMemCreate || !orig_halMemRelease || + !orig_aclrtMallocCached || orig_aclrtMallocAlign32) + { + return -1; + } + + atexit(exit_handler); + + return 0; +} + +unw_word_t get_so_base(unw_word_t addr) +{ + Dl_info info; + if (dladdr((void *)addr, &info) != 0) + { + return (unw_word_t)info.dli_fbase; + } + return 0; +} + +static void collect_stack_frames(MemAllocEntry *entry) +{ + unw_cursor_t cursor; + unw_context_t context; + unw_word_t ip; + int frame_count = 0; + const int max_frames = 32; + + unw_getcontext(&context); + unw_init_local(&cursor, &context); + + entry->stack_frames = calloc(max_frames, sizeof(StackFrame *)); + while (unw_step(&cursor) > 0 && frame_count < max_frames) + { + unw_get_reg(&cursor, UNW_REG_IP, &ip); + + // Get the SO name and base address for this IP + const char *so_name = get_so_name(ip); + unw_word_t so_base = get_so_base(ip); // You'll need to implement this + + StackFrame *frame = malloc(sizeof(StackFrame)); + stack_frame__init(frame); + frame->address = + ip - so_base; // Store offset within SO instead of virtual address + frame->so_name = strdup(so_name); + + entry->stack_frames[frame_count] = frame; + entry->n_stack_frames++; + + frame_count++; + } +} + +static void add_mem_alloc_entry(void *pp, size_t size) +{ + ThreadData *td = get_thread_data(); + + MemAllocEntry *entry = malloc(sizeof(MemAllocEntry)); + mem_alloc_entry__init(entry); + entry->alloc_ptr = (uint64_t)pp; + entry->mem_size = size; + entry->stage_id = global_stage_id; + entry->stage_type = global_stage_type; + entry->n_stack_frames = 0; + entry->stack_frames = NULL; + + collect_stack_frames(entry); + + td->proc_mem->n_mem_alloc_stacks++; + td->proc_mem->mem_alloc_stacks = + realloc(td->proc_mem->mem_alloc_stacks, + td->proc_mem->n_mem_alloc_stacks * sizeof(MemAllocEntry *)); + td->proc_mem->mem_alloc_stacks[td->proc_mem->n_mem_alloc_stacks - 1] = + entry; +} + +static void add_mem_free_entry(void *pp) +{ + ThreadData *td = get_thread_data(); + + MemFreeEntry *entry = malloc(sizeof(MemFreeEntry)); + mem_free_entry__init(entry); + entry->alloc_ptr = (uint64_t)pp; + entry->stage_id = global_stage_id; + entry->stage_type = global_stage_type; + + td->proc_mem->n_mem_free_stacks++; + td->proc_mem->mem_free_stacks = + realloc(td->proc_mem->mem_free_stacks, + td->proc_mem->n_mem_free_stacks * sizeof(MemFreeEntry *)); + td->proc_mem->mem_free_stacks[td->proc_mem->n_mem_free_stacks - 1] = entry; +} + +drvError_t halMemAlloc(void **pp, unsigned long long size, + unsigned long long flag) +{ + if (!orig_halMemAlloc) + { + init_mem_trace(); + } + int ret = orig_halMemAlloc(pp, size, flag); + if (ret == 0 && pp && *pp) + { + add_mem_alloc_entry(*pp, size); + } + + write_protobuf_to_file(); + + return ret; +} + +drvError_t halMemFree(void *pp) +{ + if (!orig_halMemFree) + { + init_mem_trace(); + } + int ret = orig_halMemFree(pp); + if (ret == 0 && pp) + { + add_mem_free_entry(pp); + } + + write_protobuf_to_file(); + + return ret; +} + +drvError_t aclrtMalloc(void **devPtr, size_t size, aclrtMemMallocPolicy policy) +{ + if (!orig_aclrtMalloc) + { + init_mem_trace(); + } + int ret = orig_aclrtMalloc(devPtr, size, policy); + if (ret == 0 && devPtr && *devPtr) + { + add_mem_alloc_entry(*devPtr, size); + } + + write_protobuf_to_file(); + + return ret; +} + +drvError_t aclrtMallocCached(void **devPtr, size_t size, + aclrtMemMallocPolicy policy) +{ + if (!orig_aclrtMallocCached) + { + init_mem_trace(); + } + int ret = orig_aclrtMallocCached(devPtr, size, policy); + if (ret == 0 && devPtr && *devPtr) + { + add_mem_alloc_entry(*devPtr, size); + } + + write_protobuf_to_file(); + + return ret; +} + +drvError_t aclrtMallocAlign32(void **devPtr, size_t size, + aclrtMemMallocPolicy policy) +{ + if (!orig_aclrtMallocAlign32) + { + init_mem_trace(); + } + int ret = orig_aclrtMallocAlign32(devPtr, size, policy); + if (ret == 0 && devPtr && *devPtr) + { + add_mem_alloc_entry(*devPtr, size); + } + + write_protobuf_to_file(); + + return ret; +} + +drvError_t aclrtFree(void *devPtr) +{ + if (!orig_aclrtFree) + { + init_mem_trace(); + } + int ret = orig_aclrtFree(devPtr); + if (ret == 0 && devPtr) + { + add_mem_free_entry(devPtr); + } + + write_protobuf_to_file(); + + return ret; +} + +drvError_t halMemCreate(void **handle, size_t size, void *prop, uint64_t flag) +{ + if (!orig_halMemCreate) + { + init_mem_trace(); + } + int ret = orig_halMemCreate(handle, size, prop, flag); + if (ret == 0 && handle && *handle) + { + add_mem_alloc_entry(*handle, size); + } + + write_protobuf_to_file(); + + return ret; +} + +drvError_t halMemRelease(void *handle) +{ + if (!orig_halMemRelease) + { + init_mem_trace(); + } + + int ret = orig_halMemRelease(handle); + if (ret == 0 && handle) + { + add_mem_free_entry(handle); + } + + write_protobuf_to_file(); + + return ret; +} \ No newline at end of file diff --git a/systrace/src/mspti/json_file_writer.h b/systrace/src/mspti/json_file_writer.h new file mode 100644 index 0000000..9b19788 --- /dev/null +++ b/systrace/src/mspti/json_file_writer.h @@ -0,0 +1,189 @@ +#pragma once +#include "../../include/common/shared_constants.h" +#include "../../include/common/util.h" +#include "mspti.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class MSPTIHcclFileWriter +{ + private: + std::ofstream file; + std::mutex buffermtx; + std::mutex bufferMarkerMtx; + std::mutex threadmtx; + std::atomic opened; + std::unique_ptr> markerActivityBuffer; + std::thread writerThread; + std::condition_variable cv; + std::atomic stop; + Json::Value root = Json::Value(Json::ValueType::arrayValue); + + public: + MSPTIHcclFileWriter(const std::string &filename) + { + // obtain environment variable LOCAL_RANK + // to determine the rank of the process + // and append it to the filename + const char *path = std::getenv("METRIC_PATH"); + std::string savePath = path ? path : SYS_TRACE_ROOT_DIR "mspti/"; + if (systrace::util::fs_utils::CreateDirectoryIfNotExists(savePath)) + { + STLOG(ERROR) << "[MSPTI] Failed to create dump directory"; + return; + } + std::string savePathStr = savePath; + if (!savePathStr.empty() && savePathStr.back() != '/') + { + savePathStr += "/"; + } + std::string saveFilename = savePathStr + filename; + std::string filenameWithRank = saveFilename; + this->markerActivityBuffer = + std::make_unique>(); + + const char *localRankCStr = std::getenv("RANK"); + if (localRankCStr == nullptr) + { + localRankCStr = "-1"; + } + std::string localRank = + localRankCStr; // Now safe to construct std::string + auto rank = std::stoi(localRank); + if (saveFilename.length() >= 5 && + saveFilename.substr(saveFilename.length() - 5) == ".json") + { + std::string baseName = + saveFilename.substr(0, saveFilename.length() - 5); + filenameWithRank = baseName + "." + std::to_string(rank) + ".json"; + } + else + { + filenameWithRank = saveFilename + "." + std::to_string(rank); + } + std::cout << "Filename: " << filenameWithRank << std::endl; + this->file.open(filenameWithRank, std::ios::out | std::ios::app); + this->opened.store(true); + this->stop.store(false); + this->run(); + } + + void stopWriter() + { + if (this->file.is_open()) + { + { + std::unique_lock lock(this->threadmtx); + this->stop.store(true); + } + this->cv.notify_all(); + this->hcclActivityFormatToJson(); + if (this->writerThread.joinable()) + { + this->writerThread.join(); + } + this->file.close(); + this->opened.store(false); + } + } + + ~MSPTIHcclFileWriter() { this->stopWriter(); } + + bool fileExists(const std::string &fp) + { + std::ifstream file(fp.c_str()); + return file.good() && file.is_open(); + } + + void bufferMarkerActivity(msptiActivityMarker *activity) + { + std::lock_guard lock(this->bufferMarkerMtx); + this->markerActivityBuffer->push_back(*activity); + } + + void run() + { + // a thread to periodically flush + // the buffer to the file + // watch the conditional variable for signal + this->writerThread = std::thread( + [this]() + { + while (!this->stop.load()) + { + std::unique_lock lock(this->threadmtx); + if (this->cv.wait_for(lock, std::chrono::seconds(5)) == + std::cv_status::timeout) + { + this->hcclActivityFormatToJson(); + } + else if (this->stop.load()) + { + break; + }; + } + }); + } + + void hcclActivityFormatToJson() + { + std::lock_guard lock(this->buffermtx); + if (this->file.is_open()) + { + for (auto activity : *this->markerActivityBuffer) + { + Json::Value markerJson; + markerJson["Kind"] = activity.kind; + markerJson["SourceKind"] = activity.sourceKind; + markerJson["Timestamp"] = activity.timestamp; + markerJson["Id"] = activity.id; + markerJson["Flag"] = activity.flag; + Json::Value msptiObjecId; + if (activity.sourceKind == MSPTI_ACTIVITY_SOURCE_KIND_HOST) + { + Json::Value pt; + pt["ProcessId"] = activity.objectId.pt.processId; + pt["ThreadId"] = activity.objectId.pt.threadId; + Json::Value ds; + ds["DeviceId"] = activity.objectId.pt.processId; + ds["StreamId"] = activity.objectId.pt.threadId; + msptiObjecId["Pt"] = pt; + msptiObjecId["Ds"] = ds; + } + else if (activity.sourceKind == + MSPTI_ACTIVITY_SOURCE_KIND_DEVICE) + { + Json::Value ds; + ds["DeviceId"] = activity.objectId.ds.deviceId; + ds["StreamId"] = activity.objectId.ds.streamId; + Json::Value pt; + pt["ProcessId"] = activity.objectId.ds.deviceId; + pt["ThreadId"] = activity.objectId.ds.streamId; + msptiObjecId["Pt"] = pt; + msptiObjecId["Ds"] = ds; + } + markerJson["msptiObjectId"] = msptiObjecId; + markerJson["Name"] = activity.name; + this->root.append(markerJson); + } + if (this->root.size() > 0) + { + Json::StyledWriter writer; + this->file << writer.write(this->root); + this->root.clear(); + } + this->markerActivityBuffer->clear(); + } + else + { + std::cout << "File is not open" << std::endl; + } + } +}; \ No newline at end of file diff --git a/systrace/src/mspti/mspti_tracker.cpp b/systrace/src/mspti/mspti_tracker.cpp new file mode 100644 index 0000000..d6f1285 --- /dev/null +++ b/systrace/src/mspti/mspti_tracker.cpp @@ -0,0 +1,96 @@ +#include "mspti_tracker.hpp" +#include +#include +#include + +constexpr size_t KB = 1 * 1024; +constexpr size_t MB = 1 * 1024 * KB; +constexpr size_t ALIGN_SIZE = 8; + +std::mutex MSPTITracker::mtx; + +inline uint8_t *align_buffer(uint8_t *buffer, size_t align) +{ + return reinterpret_cast( + (reinterpret_cast(buffer) + (align - 1)) & ~(align - 1)); +} + +MSPTITracker::MSPTITracker() +{ + std::cout << "Logging initialized from preloaded library." << std::endl; + hcclFileWriter = + std::make_unique("hccl_activity.json"); + msptiSubscribe(&subscriber, nullptr, nullptr); + msptiActivityRegisterCallbacks(UserBufferRequest, UserBufferComplete); + msptiActivityEnable(MSPTI_ACTIVITY_KIND_MARKER); +} + +MSPTITracker::~MSPTITracker() +{ + msptiActivityFlushAll(1); + msptiActivityDisable(MSPTI_ACTIVITY_KIND_MARKER); + finish(); +} + +MSPTITracker &MSPTITracker::getInstance() +{ + static MSPTITracker instance; + return instance; +} + +void MSPTITracker::finish() +{ + std::cout << "Finishing MSPTI Tracker" << std::endl; + if (hcclFileWriter) + { + hcclFileWriter->stopWriter(); + } +} + +void MSPTITracker::readActivityMarker(msptiActivityMarker *activity) +{ + if (hcclFileWriter) + { + hcclFileWriter->bufferMarkerActivity(activity); + } +} + +void MSPTITracker::UserBufferRequest(uint8_t **buffer, size_t *size, + size_t *maxNumRecords) +{ + auto &instance = getInstance(); + std::lock_guard lock(mtx); + constexpr uint32_t SIZE = (uint32_t)MB * 1; + instance.requestedCount.fetch_add(1); + uint8_t *pBuffer = (uint8_t *)malloc(SIZE + ALIGN_SIZE); + *buffer = align_buffer(pBuffer, ALIGN_SIZE); + *size = MB * 1; + *maxNumRecords = 0; +} + +void MSPTITracker::UserBufferComplete(uint8_t *buffer, size_t size, + size_t validSize) +{ + auto &instance = getInstance(); + if (validSize > 0) + { + msptiActivity *pRecord = nullptr; + msptiResult status = MSPTI_SUCCESS; + do + { + std::lock_guard lock(mtx); + status = msptiActivityGetNextRecord(buffer, validSize, &pRecord); + if (status == MSPTI_SUCCESS && + pRecord->kind == MSPTI_ACTIVITY_KIND_MARKER) + { + instance.readActivityMarker( + reinterpret_cast(pRecord)); + } + else if (status == MSPTI_ERROR_MAX_LIMIT_REACHED) + { + break; + } + } while (status == MSPTI_SUCCESS); + } + free(buffer); +} \ No newline at end of file diff --git a/systrace/src/mspti/mspti_tracker.hpp b/systrace/src/mspti/mspti_tracker.hpp new file mode 100644 index 0000000..a1c75bc --- /dev/null +++ b/systrace/src/mspti/mspti_tracker.hpp @@ -0,0 +1,33 @@ +#include "json_file_writer.h" +#include "mspti.h" +#include +#include +#include + +class MSPTITracker +{ + private: + static std::mutex mtx; + + msptiSubscriberHandle subscriber; + std::unique_ptr hcclFileWriter; + std::atomic requestedCount{0}; + + MSPTITracker(); + ~MSPTITracker(); + + public: + MSPTITracker(const MSPTITracker &) = delete; + MSPTITracker &operator=(const MSPTITracker &) = delete; + + static MSPTITracker &getInstance(); + + msptiSubscriberHandle *getSubscriber() { return &subscriber; } + void finish(); + void readActivityMarker(msptiActivityMarker *activity); + + static void UserBufferRequest(uint8_t **buffer, size_t *size, + size_t *maxNumRecords); + static void UserBufferComplete(uint8_t *buffer, size_t size, + size_t validSize); +}; \ No newline at end of file diff --git a/systrace/src/trace/CMakeLists.txt b/systrace/src/trace/CMakeLists.txt new file mode 100644 index 0000000..8eff8f2 --- /dev/null +++ b/systrace/src/trace/CMakeLists.txt @@ -0,0 +1,13 @@ +add_definitions(-DSYSTRACE_EXPORTS -D_GLIBCXX_USE_CXX11_ABI=1) + +set(PYTHON_TRACING_SOURCES + python/pytorch_tracing_loader.cc + python/pytorch_tracing_manager.cc +) + +set_source_files_properties( + systrace_manager.cc + library_loader.cc + ${PYTHON_TRACING_SOURCES} + PROPERTIES COMPILE_FLAGS "-fPIC -Wall -Wextra" +) \ No newline at end of file diff --git a/systrace/src/trace/library_loader.cc b/systrace/src/trace/library_loader.cc new file mode 100644 index 0000000..a74bb37 --- /dev/null +++ b/systrace/src/trace/library_loader.cc @@ -0,0 +1,46 @@ +#include "library_loader.h" +#include "../../include/common/logging.h" +#include + +namespace systrace +{ + +DynamicLibraryLoader::DynamicLibraryLoader(const std::string &library_path) + : library_handle_(nullptr), is_usable_(false), library_path_(library_path) +{ + LoadDynamicLibrary(); +} + +DynamicLibraryLoader::~DynamicLibraryLoader() +{ + if (library_handle_) + { + dlclose(library_handle_); + library_handle_ = nullptr; + } +} + +void DynamicLibraryLoader::LoadDynamicLibrary() +{ + if (library_handle_) + { + STLOG(WARNING) << "Library already loaded: " << library_path_; + return; + } + + dlerror(); + + library_handle_ = dlopen(library_path_.c_str(), RTLD_LAZY); + if (!library_handle_) + { + const char *error_message = dlerror(); + STLOG(WARNING) << "Failed to load library: " + << (error_message ? error_message : "Unknown error"); + is_usable_ = false; + return; + } + + is_usable_ = true; +} + +} // namespace systrace \ No newline at end of file diff --git a/systrace/src/trace/library_loader.h b/systrace/src/trace/library_loader.h new file mode 100644 index 0000000..abae8d7 --- /dev/null +++ b/systrace/src/trace/library_loader.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../../include/common/util.h" +#include +#include +#include +#include + +namespace systrace +{ + +class DynamicLibraryLoader +{ + protected: + void *library_handle_; + bool is_usable_; + const std::string library_path_; + + void LoadDynamicLibrary(); + + public: + explicit DynamicLibraryLoader(const std::string &library_path); + virtual ~DynamicLibraryLoader(); + + bool IsLibraryLoaded() const + { + return library_handle_ != nullptr && is_usable_; + } + void *GetLibraryHandle() const { return library_handle_; } +}; + +} // namespace systrace \ No newline at end of file diff --git a/systrace/src/trace/python/pytorch_tracing.c b/systrace/src/trace/python/pytorch_tracing.c new file mode 100644 index 0000000..ce3b651 --- /dev/null +++ b/systrace/src/trace/python/pytorch_tracing.c @@ -0,0 +1,618 @@ +#include "pytorch_tracing.h" +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 11 +#include +#endif + +Stagetype determine_stage_type(const char *function_name) +{ + if (function_name == NULL) + { + return UNKNOWN; + } + + if (strcmp(function_name, "GC") == 0) + { + return GC; + } + if (strcmp(function_name, + "torch.utils.data.dataloader@_BaseDataLoaderIter@__next__") == 0) + { + return DATALOADER; + } + if (strcmp(function_name, "torch_npu@npu@synchronize") == 0 || + strcmp(function_name, "torch_npu.npu@Event@synchronize") == 0 || + strcmp(function_name, "torch_npu.npu@Event@wait") == 0 || + strcmp(function_name, "torch_npu.npu@Stream@synchronize") == 0 || + strcmp(function_name, "torch_npu.npu@Stream@wait_event") == 0 || + strcmp(function_name, "torch_npu.npu@Stream@wait_stream") == 0) + { + return SYNCHRONIZATION; + } + if (strcmp(function_name, "torch@autograd@backward") == 0 || + strcmp(function_name, "torch@autograd@grad") == 0) + { + return BACKWARD; + } + if (strcmp(function_name, + "megatron.core.pipeline_parallel@schedules@forward_step") == 0) + { + return FORWARD; + } + if (strcmp(function_name, + "megatron.core.pipeline_parallel@schedules@backward_step") == 0) + { + return BACKWARD; + } + return UNKNOWN; +} + +static int register_tracing_function(const char *name, int index, char **errors) +{ + int64_t code_address; + int is_native; + int ret = + GetFuncAddressByPython(name, errors + index, &code_address, &is_native); + + if (ret) + { + printf("register function `%s` error\n", name); + return ret; + } + + printf("register function `%s` at address %ld\n", name, code_address); + addTracingData(index, name); + + TracingFunction *traced_function = + (TracingFunction *)malloc(sizeof(TracingFunction)); + traced_function->tag_name = index; + traced_function->function_name = strdup(name); + traced_function->py_code_address = code_address; + traced_function->is_native = is_native; + + HASH_ADD(hh, pytorch_tracing_func_map, py_code_address, sizeof(int64_t), + traced_function); + + return 0; +} + +static void set_profiler_for_all_threads() +{ + PyEval_SetProfile(profiler, NULL); + + PyThreadState *tstate = PyThreadState_Get(); + PyThreadState *thread_array[PY_TRACING_MAX_THREADS]; + memset(thread_array, 0, sizeof(thread_array)); + + int thread_count = 0; + while (tstate != NULL && thread_count < PY_TRACING_MAX_THREADS) + { + thread_array[thread_count++] = tstate; + printf("Set profiler for thread %ld\n", tstate->thread_id); + tstate = PyThreadState_Next(tstate); + } + + for (int i = 0; i < thread_count; i++) + { + PyThreadState_Swap(thread_array[i]); + PyEval_SetProfile(profiler, NULL); + } + + PyThreadState_Swap(thread_array[0]); +} + +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 11 +static void capture_stack(PyFrameObject *frame, PyTorchTracingData *trace_entry) +{ + PyGILState_STATE gstate = PyGILState_Ensure(); + int depth = 0; + while (frame && depth < MAX_STACK_DEPTH) + { + PyCodeObject *code = PyFrame_GetCode(frame); + if (!code) + { + break; + } + + const char *name = PyUnicode_AsUTF8(code->co_name); + const char *file = PyUnicode_AsUTF8(code->co_filename); + int line = PyFrame_GetLineNumber(frame); + + snprintf(trace_entry->stack_info[depth], 256, "%s@%s:%d", + name ? name : "unknown", file ? file : "unknown", line); + + PyFrameObject *next_frame = PyFrame_GetBack(frame); + Py_DECREF(code); + frame = next_frame; + + depth++; + } + trace_entry->stack_depth = depth; + PyGILState_Release(gstate); +} + +uint64_t getCodeOfFrame(PyFrameObject *frame) +{ + return (int64_t)(uintptr_t)PyFrame_GetCode(frame); +} +#else +static void capture_stack(PyFrameObject *frame, PyTorchTracingData *trace_entry) +{ + PyGILState_STATE gstate = PyGILState_Ensure(); + int depth = 0; + while (frame && depth < MAX_STACK_DEPTH) + { + snprintf(trace_entry->stack_info[depth], 256, "%s@%s:%d", + PyUnicode_AsUTF8(frame->f_code->co_name), + PyUnicode_AsUTF8(frame->f_code->co_filename), + PyFrame_GetLineNumber(frame)); + frame = frame->f_back; + depth++; + } + trace_entry->stack_depth = depth; + PyGILState_Release(gstate); +} + +uint64_t getCodeOfFrame(PyFrameObject *frame) +{ + return (int64_t)(uintptr_t)(frame->f_code); +} + +#endif + +uint64_t getMsTime() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec; +} + +static void ensure_python_initialized() +{ + if (!Py_IsInitialized()) + { + Py_Initialize(); + } +} + +TracingFunction *isTracedPyTorchFunction(PyFrameObject *frame) +{ + uint64_t code_address = getCodeOfFrame(frame); + TracingFunction *traced_function = NULL; + HASH_FIND(hh, pytorch_tracing_func_map, &code_address, sizeof(int64_t), + traced_function); + return traced_function; +} + +static int profiler(PyObject *obj, PyFrameObject *frame, int what, + PyObject *arg) +{ + TracingFunction *func_data = isTracedPyTorchFunction(frame); + if (!func_data) + return 0; + int tag_name = func_data->tag_name; + int stage_type = determine_stage_type(func_data->function_name); + if ((what == PyTrace_CALL) && start_tracing) + { + pthread_mutex_lock(&mutex); + TracingData *tracing_data = receiveTracingData(tag_name); + PyTorchTracingDataArray *curr_data = tracing_data->curr_data; + if (curr_data->cur == PY_TRACING_BUFFER_SIZE) + { + systrace_return_pytorch_tracing_data_array( + curr_data, PY_TRACING_READY_POOL, tag_name); + tracing_data->curr_data = + systrace_get_empty_pytorch_tracing_data_array(tag_name); + curr_data = tracing_data->curr_data; + } + curr_data->data[curr_data->cur].start = getMsTime(); + if (stage_type == DATALOADER) + { + global_stage_id++; + } + curr_data->data[curr_data->cur].stage_id = global_stage_id; + curr_data->data[curr_data->cur].stage_type = stage_type; + global_stage_type = stage_type; + capture_stack(frame, &curr_data->data[curr_data->cur]); + + pthread_mutex_unlock(&mutex); + } + else if (what == PyTrace_RETURN) + { + pthread_mutex_lock(&mutex); + TracingData *tracing_data = receiveTracingData(tag_name); + if (start_tracing) + { + PyTorchTracingDataArray *curr_data = tracing_data->curr_data; + curr_data->data[curr_data->cur].count = tracing_data->count; + curr_data->data[curr_data->cur++].end = getMsTime(); + } + tracing_data->count++; + pthread_mutex_unlock(&mutex); + } + return 0; +} + +static int set_error_message(char **error_message, const char *format, ...) { + va_list args; + va_start(args, format); + int size = vsnprintf(NULL, 0, format, args) + 1; + va_end(args); + + *error_message = malloc(size); + if (!*error_message) return 0; + + va_start(args, format); + vsnprintf(*error_message, size, format, args); + va_end(args); + + return 1; +} + +static int parse_input_string(const char *code, char ***tokens, int *token_count) { + char *copy = strdup(code); + if (!copy) return 0; + + char *saveptr = NULL; + *token_count = 0; + *tokens = malloc(3 * sizeof(char*)); + if (!*tokens) { + free(copy); + return 0; + } + + for (char *token = strtok_r(copy, "@", &saveptr); + token && *token_count < 3; + token = strtok_r(NULL, "@", &saveptr)) { + (*tokens)[(*token_count)++] = strdup(token); + } + + free(copy); + return 1; +} + +static char* build_python_code(const char *code, char **tokens, int token_count) { + const char *template = + "try:\n" + " obj = None\n" + "%s\n" + " while hasattr(obj, '__wrapped__'):\n" + " obj = getattr(obj, '__wrapped__')\n" + " if hasattr(obj, '__code__'):\n" + " address = id(obj.__code__)\n" + " is_native = 0\n" + " else:\n" + " address = id(obj)\n" + " is_native = 1\n" + "except Exception as e:\n" + " raise\n"; + + char *import_part = NULL; + if (token_count == 3) { + asprintf(&import_part, + " from %s import %s as mm\n" + " obj = getattr(mm, '%s')", + tokens[0], tokens[1], tokens[2]); + } else if (token_count == 2) { + asprintf(&import_part, + " from %s import %s as obj", + tokens[0], tokens[1]); + } else { + asprintf(&import_part, + " obj = globals().get('%s')\n" + " if obj is None:\n" + " raise ValueError('Global object not found: %s')", + code, code); + } + + char *python_code = NULL; + asprintf(&python_code, template, import_part); + free(import_part); + + return python_code; +} + +static int execute_python_code(const char *python_code, int use_globals, + int64_t *address, int *is_native, char **error_message) { + PyObject *globals = use_globals ? PyEval_GetGlobals() : PyDict_New(); + PyObject *locals = PyDict_New(); + + if (!globals || !locals) { + if (!use_globals && globals) Py_DECREF(globals); + if (locals) Py_DECREF(locals); + return set_error_message(error_message, "Failed to create Python dictionaries"); + } + + PyObject *result = PyRun_String(python_code, Py_file_input, globals, locals); + if (!result) { + PyObject *ptype, *pvalue, *ptraceback; + PyErr_Fetch(&ptype, &pvalue, &ptraceback); + PyErr_NormalizeException(&ptype, &pvalue, &ptraceback); + + if (pvalue) { + PyObject *py_str = PyObject_Str(pvalue); + if (py_str) { + const char *str_error = PyUnicode_AsUTF8(py_str); + set_error_message(error_message, "Python error: %s", str_error ? str_error : "Unknown error"); + Py_DECREF(py_str); + } + } + + Py_XDECREF(ptype); + Py_XDECREF(pvalue); + Py_XDECREF(ptraceback); + PyErr_Clear(); + + if (!use_globals) Py_DECREF(globals); + Py_DECREF(locals); + return 1; + } + Py_DECREF(result); + + PyObject *py_address = PyDict_GetItemString(locals, "address"); + PyObject *py_is_native = PyDict_GetItemString(locals, "is_native"); + + if (!py_address || !py_is_native) { + if (!use_globals) Py_DECREF(globals); + Py_DECREF(locals); + return set_error_message(error_message, "Failed to get address or is_native from execution"); + } + + *address = PyLong_AsLongLong(py_address); + *is_native = PyLong_AsLongLong(py_is_native); + + if (!use_globals) Py_DECREF(globals); + Py_DECREF(locals); + return 0; +} + +static int GetFuncAddressByPython(const char *code, char **error_message, + int64_t *address, int *is_native) { + *error_message = NULL; + *address = 0; + *is_native = 0; + + if (!code || !*code) { + return set_error_message(error_message, "Empty or NULL code parameter"); + } + + char **tokens = NULL; + int token_count = 0; + if (!parse_input_string(code, &tokens, &token_count)) { + return set_error_message(error_message, "Failed to parse input string"); + } + + char *python_code = build_python_code(code, tokens, token_count); + if (!python_code) { + for (int i = 0; i < token_count; i++) free(tokens[i]); + free(tokens); + return set_error_message(error_message, "Failed to build Python code"); + } + + int use_globals = (token_count == 0); + int result = execute_python_code(python_code, use_globals, address, is_native, error_message); + + free(python_code); + for (int i = 0; i < token_count; i++) free(tokens[i]); + free(tokens); + + if (result == 0) { + set_error_message(error_message, "Get __code__ attribute for '%s' OK", code); + } + + return result; +} +static TracingData *receiveTracingData(int name) +{ + return pytorch_tracing_data_array + name; +} + +static void addTracingData(int name, const char *func_name) +{ + TracingData *v = receiveTracingData(name); + v->tag_name = name; + v->curr_data = systrace_get_empty_pytorch_tracing_data_array(name); + v->function_name = strdup(func_name); +} + +static void getGcInfo(PyTorchTracingData *data, PyObject *info) +{ + if (!PyDict_Check(info)) + return; + PyObject *collected = PyDict_GetItemString(info, "collected"); + PyObject *uncollectable = PyDict_GetItemString(info, "uncollectable"); + + if (collected && PyLong_Check(collected)) + { + data->payload.gc_debug[0] = PyLong_AsLong(collected); + } + else + { + data->payload.gc_debug[0] = -1; + } + + if (uncollectable && PyLong_Check(uncollectable)) + { + data->payload.gc_debug[1] = PyLong_AsLong(uncollectable); + } + else + { + data->payload.gc_debug[1] = -1; + } +} + +static void gcCallback(PyObject *phase, PyObject *info) +{ + pthread_mutex_lock(&mutex); + if (PyUnicode_CompareWithASCIIString(phase, "start") == 0 && start_tracing) + { + TracingData *tracing_data = receiveTracingData(PY_TRACING_GC); + PyTorchTracingDataArray *curr_data = tracing_data->curr_data; + if (curr_data->cur == PY_TRACING_BUFFER_SIZE) + { + systrace_return_pytorch_tracing_data_array( + curr_data, PY_TRACING_READY_POOL, PY_TRACING_GC); + tracing_data->curr_data = + systrace_get_empty_pytorch_tracing_data_array(PY_TRACING_GC); + curr_data = tracing_data->curr_data; + } + curr_data->data[curr_data->cur].start = getMsTime(); + pthread_mutex_unlock(&mutex); + } + else if (PyUnicode_CompareWithASCIIString(phase, "stop") == 0) + { + TracingData *tracing_data = receiveTracingData(PY_TRACING_GC); + if (start_tracing) + { + PyTorchTracingDataArray *curr_data = tracing_data->curr_data; + if (start_tracing) + { + curr_data->data[curr_data->cur].count = tracing_data->count; + curr_data->data[curr_data->cur].type = PAYLOAD_GC; + getGcInfo(curr_data->data + curr_data->cur, info); + curr_data->data[curr_data->cur++].end = getMsTime(); + } + curr_data->data[curr_data->cur].count = tracing_data->count; + curr_data->data[curr_data->cur].stage_id = global_stage_id; + curr_data->data[curr_data->cur++].end = getMsTime(); + } + tracing_data->count++; + } + pthread_mutex_unlock(&mutex); +} + +static PyObject *gcCallbackWrapper(PyObject *self, PyObject *args, + PyObject *kwargs) +{ + PyObject *phase, *info; + if (!PyArg_ParseTuple(args, "OO", &phase, &info)) + { + return NULL; + } + gcCallback(phase, info); + Py_RETURN_NONE; +} + +static PyTypeObject GcCallbackType = { + PyVarObject_HEAD_INIT(NULL, 0) "gc_callback", /* tp_name */ + sizeof(PyObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_vectorcall_offset */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_as_async */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + gcCallbackWrapper, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyTorchTracingDataArray * +systrace_get_partial_pytorch_tracing_data_array(int name) +{ + pthread_mutex_lock(&mutex); + TracingData *tracing_data = receiveTracingData(name); + if ((!tracing_data || !tracing_data->curr_data) || + (tracing_data->curr_data->cur == 0)) + { + pthread_mutex_unlock(&mutex); + return NULL; + } + PyTorchTracingDataArray *result = tracing_data->curr_data; + tracing_data->curr_data = + systrace_get_empty_pytorch_tracing_data_array(name); + pthread_mutex_unlock(&mutex); + return result; +} + +void systrace_register_gc(char **error_message) +{ + addTracingData(PY_TRACING_GC, "GC"); + PyObject *gc_module = PyImport_ImportModule("gc"); + if (!gc_module) + { + return; + } + + PyObject *callbacks_list = PyObject_GetAttrString(gc_module, "callbacks"); + if (!callbacks_list || !PyList_Check(callbacks_list)) + { + Py_XDECREF(callbacks_list); + Py_DECREF(gc_module); + return; + } + + PyObject *py_callback = PyObject_New(PyObject, &GcCallbackType); + + if (!py_callback) + { + Py_DECREF(callbacks_list); + Py_DECREF(gc_module); + return; + } + + if (PyList_Append(callbacks_list, py_callback) != 0) + { + Py_DECREF(py_callback); + Py_DECREF(callbacks_list); + Py_DECREF(gc_module); + return; + } + + Py_DECREF(callbacks_list); + Py_DECREF(gc_module); + *error_message = strdup("Import gc Ok"); +} + +static void init_tracing_data_array(int count) +{ + tracing_data_count = count; + pytorch_tracing_data_array = + (TracingData *)malloc(sizeof(TracingData) * tracing_data_count); + memset(pytorch_tracing_data_array, 0, + sizeof(TracingData) * tracing_data_count); +} + +void systrace_register_tracing(const char **names, int count, char **errors) +{ + ensure_python_initialized(); + + PyGILState_STATE gstate = PyGILState_Ensure(); + + init_tracing_data_array(count); + systrace_register_gc(errors); + + for (int i = 1; i < count; i++) + { + register_tracing_function(names[i], i, errors); + } + + set_profiler_for_all_threads(); + + PyGILState_Release(gstate); +} \ No newline at end of file diff --git a/systrace/src/trace/python/pytorch_tracing.h b/systrace/src/trace/python/pytorch_tracing.h new file mode 100644 index 0000000..5209b28 --- /dev/null +++ b/systrace/src/trace/python/pytorch_tracing.h @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include + +#include "../../../include/common/shared_constants.h" +#include "../../../thirdparty/uthash.h" +#include "pytorch_tracing_data.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + __attribute__((visibility("default"))) PyTorchTracingDataArray * + systrace_get_empty_pytorch_tracing_data_array(int); + __attribute__((visibility("default"))) PyTorchTracingDataArray * + systrace_get_full_pytorch_tracing_data_array(int); + + __attribute__((visibility("default"))) PyTorchTracingDataArray * + systrace_get_partial_pytorch_tracing_data_array(int); + + __attribute__((visibility("default"))) void + systrace_return_pytorch_tracing_data_array(PyTorchTracingDataArray *, + int type, int name); + __attribute__((visibility("default"))) void + systrace_register_tracing(const char **, int, char **); +#ifdef __cplusplus +} +#endif +typedef struct +{ + int64_t py_code_address; + const char *function_name; + int tag_name; + int is_native; + UT_hash_handle hh; +} TracingFunction; + +typedef struct +{ + int tag_name; + PyTorchTracingDataArray *curr_data; + int64_t count; + const char *function_name; +} TracingData; + +typedef struct _frame PyFrameObject; +uint64_t getCodeOfFrame(PyFrameObject *frame); +static void capture_stack(PyFrameObject *frame, + PyTorchTracingData *trace_entry); + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +static TracingData *pytorch_tracing_data_array = NULL; + +static TracingFunction *pytorch_tracing_func_map = NULL; +static int start_tracing = 1; +static int tracing_data_count = 0; + +static int GetFuncAddressByPython(const char *input, char **error_message, + int64_t *code_address, int *is_native); +static uint64_t getMsTime(); +static TracingFunction *isTracedPyTorchFunction(PyFrameObject *frame); +static TracingData *receiveTracingData(int name); +static void addTracingData(int name, const char *func_name); +static int profiler(PyObject *obj, PyFrameObject *frame, int what, + PyObject *arg); \ No newline at end of file diff --git a/systrace/src/trace/python/pytorch_tracing_data.h b/systrace/src/trace/python/pytorch_tracing_data.h new file mode 100644 index 0000000..f8a601d --- /dev/null +++ b/systrace/src/trace/python/pytorch_tracing_data.h @@ -0,0 +1,54 @@ +#pragma once +#include + +#ifndef PY_TRACING_BUFFER_SIZE +#define PY_TRACING_BUFFER_SIZE 512 +#define PY_TRACING_MAX_THREADS 256 +#endif +#define PY_TRACING_READY_POOL 0 +#define PY_TRACING_EMPTY_POOL 1 +#define PY_TRACING_GC 0 +#define PY_DATALOADER 1 + +#define MAX_STACK_DEPTH 32 +#define MAX_STACK_FRAME_LENGTH 256 + +typedef enum +{ + PAYLOAD_UNINITIALIZED = 0, + PAYLOAD_GC = 1, +} PayloadType; + +typedef enum +{ + UNKNOWN = 0, + DATALOADER, + FORWARD, + BACKWARD, + SYNCHRONIZATION, + GC, +} Stagetype; + +typedef union +{ + int gc_debug[2]; +} Payload; + +typedef struct +{ + uint64_t start; + uint64_t end; + uint32_t count; + uint32_t stage_id; + Stagetype stage_type; + Payload payload; + PayloadType type; + char stack_info[MAX_STACK_DEPTH][256]; + int stack_depth; +} PyTorchTracingData; + +typedef struct +{ + PyTorchTracingData data[PY_TRACING_BUFFER_SIZE]; + uint64_t cur; +} PyTorchTracingDataArray; \ No newline at end of file diff --git a/systrace/src/trace/python/pytorch_tracing_loader.cc b/systrace/src/trace/python/pytorch_tracing_loader.cc new file mode 100644 index 0000000..fab92ff --- /dev/null +++ b/systrace/src/trace/python/pytorch_tracing_loader.cc @@ -0,0 +1,121 @@ +#include "pytorch_tracing_loader.h" +#include "../../../include/common/logging.h" +#include +#include + +namespace systrace +{ +namespace pytorch_tracing +{ + +PyTorchTracingLibrary::PyTorchTracingLibrary(const std::string &library_path) + : DynamicLibraryLoader(library_path), register_tracing_(nullptr), + get_tracing_data_(nullptr), get_partial_tracing_data_(nullptr), + return_tracing_data_(nullptr) +{ + if (library_handle_) + { + InitializeSymbols(); + } +} + +void PyTorchTracingLibrary::InitializeSymbols() +{ + std::vector configs = { + {"systrace_register_tracing", + [this]() { return reinterpret_cast(®ister_tracing_); }, + "TracingRegistrationFunc"}, + + {"systrace_get_full_pytorch_tracing_data_array", + [this]() { return reinterpret_cast(&get_tracing_data_); }, + "DataArrayRetrievalAllFunc"}, + + {"systrace_return_pytorch_tracing_data_array", + [this]() { return reinterpret_cast(&return_tracing_data_); }, + "DataArrayReleaseFunc"}, + + {"systrace_get_partial_pytorch_tracing_data_array", [this]() + { return reinterpret_cast(&get_partial_tracing_data_); }, + "GetPartialTracingDataArrayPartFunc"}}; + + is_usable_ = std::all_of(configs.begin(), configs.end(), + [this](const SymbolConfig &config) + { return LoadSymbol(config); }); +} + +bool PyTorchTracingLibrary::LoadSymbol(const SymbolConfig &config) +{ + void *symbol = dlsym(library_handle_, config.name); + if (!symbol) + { + STLOG(WARNING) << "Failed to load symbol: " << config.name + << " (type: " << config.type_name + << "), error: " << dlerror(); + return false; + } + + *reinterpret_cast(config.loader()) = symbol; + return true; +} + +std::vector +PyTorchTracingLibrary::Register(const std::vector &names) +{ + if (!is_usable_) + { + return {}; + } + + auto error_holder = std::unique_ptr>( + new char *[names.size()], + [size = names.size()](char **ptr) + { + for (size_t i = 0; i < size; ++i) + { + free(ptr[i]); + } + delete[] ptr; + }); + std::memset(error_holder.get(), 0, names.size() * sizeof(char *)); + + std::vector c_str_array; + c_str_array.reserve(names.size()); + std::transform(names.begin(), names.end(), std::back_inserter(c_str_array), + [](const std::string &str) { return str.c_str(); }); + + register_tracing_(c_str_array.data(), c_str_array.size(), + error_holder.get()); + + std::vector result; + for (size_t i = 0; i < names.size(); ++i) + { + if (error_holder[i]) + { + result.emplace_back(error_holder[i]); + } + } + return result; +} + +PyTorchTracingDataArray *PyTorchTracingLibrary::RetrieveAllTracingData(int name) +{ + return is_usable_ ? get_tracing_data_(name) : nullptr; +} + +PyTorchTracingDataArray * +PyTorchTracingLibrary::RetrievePartialTracingData(int name) +{ + return is_usable_ ? get_partial_tracing_data_(name) : nullptr; +} + +void PyTorchTracingLibrary::ReleaseTracingData(PyTorchTracingDataArray *data, + int type, int name) +{ + if (is_usable_ && data) + { + return_tracing_data_(data, type, name); + } +} + +} // namespace pytorch_tracing +} // namespace systrace \ No newline at end of file diff --git a/systrace/src/trace/python/pytorch_tracing_loader.h b/systrace/src/trace/python/pytorch_tracing_loader.h new file mode 100644 index 0000000..9d5aaa4 --- /dev/null +++ b/systrace/src/trace/python/pytorch_tracing_loader.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#include "../../../include/common/macro.h" +#include "../library_loader.h" +#include "pytorch_tracing_data.h" + +namespace systrace +{ +namespace pytorch_tracing +{ + +class PyTorchTracingLibrary : public DynamicLibraryLoader +{ + public: + explicit PyTorchTracingLibrary(const std::string &); + using TracingRegistrationFunc = void (*)(const char **, int, char **); + using DataArrayRetrievalAllFunc = PyTorchTracingDataArray *(*)(int); + using GetPartialTracingDataArrayPartFunc = + PyTorchTracingDataArray *(*)(int); + using DataArrayReleaseFunc = void (*)(PyTorchTracingDataArray *, int, int); + PyTorchTracingDataArray *RetrieveAllTracingData(int); + PyTorchTracingDataArray *RetrievePartialTracingData(int); + std::vector Register(const std::vector &names); + void ReleaseTracingData(PyTorchTracingDataArray *data, int type, int name); + + private: + TracingRegistrationFunc register_tracing_; + DataArrayRetrievalAllFunc get_tracing_data_; + GetPartialTracingDataArrayPartFunc get_partial_tracing_data_; + DataArrayReleaseFunc return_tracing_data_; + void InitializeSymbols(); + struct SymbolConfig + { + const char *name; + std::function loader; + const char *type_name; + }; + bool LoadSymbol(const SymbolConfig &config); +}; + +} // namespace pytorch_tracing +} // namespace systrace \ No newline at end of file diff --git a/systrace/src/trace/python/pytorch_tracing_manager.cc b/systrace/src/trace/python/pytorch_tracing_manager.cc new file mode 100644 index 0000000..3be99ca --- /dev/null +++ b/systrace/src/trace/python/pytorch_tracing_manager.cc @@ -0,0 +1,58 @@ +#include "pytorch_tracing_manager.h" +#include "pytorch_tracing_data.h" +#include +#include + +namespace systrace +{ +namespace pytorch_tracing_manager +{ + +PyTorchTracingManager &PyTorchTracingManager::getInstance() +{ + std::call_once(init_flag_, &PyTorchTracingManager::initSingleton); + return *instance_; +} + +void PyTorchTracingManager::initSingleton() +{ + instance_ = new PyTorchTracingManager(); +} + +PyTorchTracingDataArray * +PyTorchTracingManager::getEmptyPyTorchTracingDataArray(int name) +{ + auto &pool_item = pool_[name]; + auto *data = pool_item.empty_pool.getObject(); + std::memset(data, 0, sizeof(PyTorchTracingDataArray)); + return data; +} + +void PyTorchTracingManager::returnPyTorchTracingDataArray( + PyTorchTracingDataArray *array, int type, int name) +{ + + if (!array) + return; + + auto &pool_item = pool_[name]; + int pool_queue_size = 0; + + switch (type) + { + case PY_TRACING_READY_POOL: + pool_item.ready_pool.returnObject(array, &pool_queue_size); + break; + case PY_TRACING_EMPTY_POOL: + pool_item.empty_pool.returnObject(array, &pool_queue_size); + break; + } +} + +PyTorchTracingDataArray * +PyTorchTracingManager::getPyTorchTracingDataArray(int name) +{ + return pool_[name].ready_pool.getObject(); +} +} // namespace pytorch_tracing_manager +} // namespace systrace \ No newline at end of file diff --git a/systrace/src/trace/python/pytorch_tracing_manager.h b/systrace/src/trace/python/pytorch_tracing_manager.h new file mode 100644 index 0000000..ead4e5a --- /dev/null +++ b/systrace/src/trace/python/pytorch_tracing_manager.h @@ -0,0 +1,73 @@ +#pragma once +#include +#include +#include + +#include "../../../include/common/util.h" +#include "pytorch_tracing.h" +#include "pytorch_tracing_data.h" + +namespace systrace +{ +namespace pytorch_tracing_manager +{ + +class PyTorchTracingManager +{ + public: + PyTorchTracingManager(const PyTorchTracingManager &) = delete; + PyTorchTracingManager &operator=(const PyTorchTracingManager &) = delete; + static void initSingleton(); + static PyTorchTracingManager &getInstance(); + + PyTorchTracingDataArray *getEmptyPyTorchTracingDataArray(int name); + void returnPyTorchTracingDataArray(PyTorchTracingDataArray *, int, + int name); + PyTorchTracingDataArray *getPyTorchTracingDataArray(int name); + PyTorchTracingDataArray *getCurPyTorchTracingDataArray(int name); + + private: + PyTorchTracingManager() = default; + inline static PyTorchTracingManager *instance_ = nullptr; + inline static std::once_flag init_flag_; + struct Pool + { + util::resource::TimerPool empty_pool; + util::resource::TimerPool ready_pool; + }; + std::unordered_map pool_; +}; +} // namespace pytorch_tracing_manager +} // namespace systrace + +#ifdef __cplusplus +extern "C" +{ +#endif + PyTorchTracingDataArray * + systrace_get_empty_pytorch_tracing_data_array(int name) + { + return systrace::pytorch_tracing_manager::PyTorchTracingManager:: + getInstance() + .getEmptyPyTorchTracingDataArray(name); + } + + PyTorchTracingDataArray * + systrace_get_full_pytorch_tracing_data_array(int name) + { + return systrace::pytorch_tracing_manager::PyTorchTracingManager:: + getInstance() + .getPyTorchTracingDataArray(name); + } + + void + systrace_return_pytorch_tracing_data_array(PyTorchTracingDataArray *array, + int type, int name) + { + systrace::pytorch_tracing_manager::PyTorchTracingManager::getInstance() + .returnPyTorchTracingDataArray(array, type, name); + } + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/systrace/src/trace/systrace_manager.cc b/systrace/src/trace/systrace_manager.cc new file mode 100644 index 0000000..eef5606 --- /dev/null +++ b/systrace/src/trace/systrace_manager.cc @@ -0,0 +1,243 @@ +#include +#include +#include +#include + +#include "../../include/common/constant.h" +#include "../../include/common/shared_constants.h" +#include "systrace_manager.h" + +int global_stage_id = 0; +int global_stage_type = 0; +namespace systrace +{ + +namespace +{ +constexpr uint64_t TRACE_INTERVAL = 100; +constexpr std::chrono::milliseconds POLL_INTERVAL(10); +} // namespace + +PyTorchTrace &PyTorchTrace::getInstance() +{ + std::call_once(init_flag_, + []() + { + instance_ = new PyTorchTrace(); + instance_->initialize(); + }); + return *instance_; +} + +void PyTorchTrace::initialize() +{ + pytorch_trace_.set_rank(config::GlobalConfig::Instance().rank); + STLOG(INFO) << "[PyTorchTrace] Rank set to: " + << config::GlobalConfig::Instance().rank; + + pytorch_tracing_library_ = + new pytorch_tracing::PyTorchTracingLibrary("libsysTrace.so"); + STLOG(INFO) << "[PyTorchTrace] Tracing library loaded"; + + registerTracingFunctions(); +} + +void PyTorchTrace::registerTracingFunctions() +{ + pytorch_tracing_functions_ = { + "GC", + "torch.utils.data.dataloader@_BaseDataLoaderIter@__next__", + "torch_npu@npu@synchronize", + "torch_npu.npu@Event@synchronize", + "torch_npu.npu@Event@wait", + "torch_npu.npu@Stream@synchronize", + "torch_npu.npu@Stream@wait_event", + "torch_npu.npu@Stream@wait_stream", + "torch@autograd@backward", + "torch@autograd@grad", + "megatron.core.pipeline_parallel@schedules@forward_step", + "megatron.core.pipeline_parallel@schedules@backward_step"}; + + auto errors = + pytorch_tracing_library_->Register(pytorch_tracing_functions_); + for (size_t i = 0; i < pytorch_tracing_functions_.size(); ++i) + { + STLOG(INFO) << "Registered function: " << pytorch_tracing_functions_[i] + << ", status: " << errors[i]; + } +} + +bool PyTorchTrace::triggerTrace() { return has_trigger_trace_.exchange(true); } + +void PyTorchTrace::dumpPyTorchTracing() +{ + const std::string &dump_path = + std::string(constant::TorchTraceConstant::DEFAULT_TRACE_DUMP_PATH); + + if (util::fs_utils::CreateDirectoryIfNotExists(dump_path)) + { + STLOG(ERROR) << "[PyTorchTrace] Failed to create dump directory"; + return; + } + + std::lock_guard lock(trace_mutex_); + + pytorch_trace_.set_rank(config::GlobalConfig::Instance().local_rank); + pytorch_trace_.set_comm(config::GlobalConfig::Instance().job_name); + + for (size_t i = 0; i < pytorch_tracing_functions_.size(); ++i) + { + processFunctionTracingData(i); + } + + writeTraceToFile(); +} + +void PyTorchTrace::processFunctionTracingData(size_t function_index) +{ + std::vector data_holders; + + if (auto data = pytorch_tracing_library_->RetrievePartialTracingData( + function_index)) + { + data_holders.push_back(data); + } + + while (auto data = + pytorch_tracing_library_->RetrieveAllTracingData(function_index)) + { + data_holders.push_back(data); + } + + for (auto data : data_holders) + { + for (uint32_t i = 0; i < data->cur; ++i) + { + if (data->data[i].start == 0) + continue; + + auto trace = pytorch_trace_.add_pytorch_stages(); + trace->set_start_us(data->data[i].start); + trace->set_end_us(data->data[i].end); + trace->set_stage_id(data->data[i].count); + trace->set_stage_type(pytorch_tracing_functions_[function_index]); + + if (data->data[i].stack_depth > 0) + { + trace->mutable_stack_frames()->Reserve( + data->data[i].stack_depth); + for (int j = 0; j < data->data[i].stack_depth; ++j) + { + if (data->data[i].stack_info[j][0] != '\0') + { + trace->add_stack_frames(data->data[i].stack_info[j]); + } + } + } + + if (data->data[i].type == PAYLOAD_GC) + { + auto gc_debug = trace->mutable_gc_debug(); + gc_debug->set_collected(data->data[i].payload.gc_debug[0]); + gc_debug->set_uncollectable(data->data[i].payload.gc_debug[1]); + } + } + } + + for (auto data : data_holders) + { + pytorch_tracing_library_->ReleaseTracingData( + data, PY_TRACING_EMPTY_POOL, function_index); + } +} + +void PyTorchTrace::writeTraceToFile() +{ + const std::string &dump_path = + std::string(constant::TorchTraceConstant::DEFAULT_TRACE_DUMP_PATH); + std::string file_path = + dump_path + "/" + + util::fs_utils::GenerateClusterUniqueFilename(".timeline"); + + std::ofstream file(file_path, std::ios::binary | std::ios::out); + if (!file) + { + STLOG(ERROR) << "[PyTorchTrace] Failed to open file: " << file_path; + return; + } + + std::string binary_data; + if (!pytorch_trace_.SerializeToString(&binary_data)) + { + STLOG(ERROR) << "[PyTorchTrace] Failed to serialize trace data"; + return; + } + + file << binary_data; +} + +SysTrace &SysTrace::getInstance() +{ + std::call_once(init_flag_, + []() + { + instance_ = new SysTrace(); + instance_->initializeSystem(); + }); + return *instance_; +} + +SysTrace::~SysTrace() { stopEventPoller(); } + +void SysTrace::initializeSystem() +{ + if (!config::GlobalConfig::Instance().enable) + return; + + systrace::util::InitializeSystemUtilities(); + MSPTITracker::getInstance(); + PyTorchTrace::getInstance(); + + startEventPoller(); +} + +void SysTrace::startEventPoller() +{ +#ifdef _GNU_SOURCE + should_run_ = true; + event_poller_ = std::thread(&SysTrace::eventPollerMain, this); + pthread_setname_np(event_poller_.native_handle(), "systrace_poller"); +#endif + STLOG(INFO) << "[SysTrace] Event poller started"; +} + +void SysTrace::stopEventPoller() +{ + should_run_ = false; + if (event_poller_.joinable()) + { + event_poller_.join(); + } +} + +void SysTrace::eventPollerMain() +{ + while (should_run_) + { + if (loop_count_++ % TRACE_INTERVAL == 0) + { + if (PyTorchTrace::getInstance().triggerTrace()) + { + PyTorchTrace::getInstance().dumpPyTorchTracing(); + } + } + std::this_thread::sleep_for(POLL_INTERVAL); + } + + if (PyTorchTrace::getInstance().triggerTrace()) + { + PyTorchTrace::getInstance().dumpPyTorchTracing(); + } +} + +} // namespace systrace \ No newline at end of file diff --git a/systrace/src/trace/systrace_manager.h b/systrace/src/trace/systrace_manager.h new file mode 100644 index 0000000..c043aba --- /dev/null +++ b/systrace/src/trace/systrace_manager.h @@ -0,0 +1,76 @@ +#pragma once +#include +#include +#include +#include +#include + +#include "../../include/common/logging.h" +#include "../../include/common/util.h" +#include "../../protos/systrace.pb.h" +#include "../mspti/mspti_tracker.hpp" +#include "library_loader.h" +#include "python/pytorch_tracing_loader.h" + +namespace systrace +{ +using namespace util; + +class PyTorchTrace +{ + public: + static PyTorchTrace &getInstance(); + + void dumpPyTorchTracing(); + void dumpPyTorchTracing(bool incremental, bool async); + bool triggerTrace(); + + PyTorchTrace(const PyTorchTrace &) = delete; + PyTorchTrace &operator=(const PyTorchTrace &) = delete; + + private: + PyTorchTrace() = default; + ~PyTorchTrace() = default; + + void initialize(); + void registerTracingFunctions(); + void processFunctionTracingData(size_t function_index); + void writeTraceToFile(); + + inline static PyTorchTrace *instance_ = nullptr; + inline static std::once_flag init_flag_; + + Pytorch pytorch_trace_; + std::atomic has_trigger_trace_{false}; + std::mutex trace_mutex_; + + std::vector pytorch_tracing_functions_; + pytorch_tracing::PyTorchTracingLibrary *pytorch_tracing_library_; +}; + +class SysTrace +{ + public: + static SysTrace &getInstance(); + + SysTrace(const SysTrace &) = delete; + SysTrace &operator=(const SysTrace &) = delete; + + private: + SysTrace() = default; + ~SysTrace(); + + void initializeSystem(); + void startEventPoller(); + void stopEventPoller(); + void eventPollerMain(); + + inline static SysTrace *instance_ = nullptr; + inline static std::once_flag init_flag_; + + std::atomic should_run_{true}; + std::atomic loop_count_{0}; + std::thread event_poller_; +}; + +} // namespace systrace \ No newline at end of file diff --git a/systrace/thirdparty/aarch64/libunwind/libunwind-aarch64.h b/systrace/thirdparty/aarch64/libunwind/libunwind-aarch64.h new file mode 100644 index 0000000..f794600 --- /dev/null +++ b/systrace/thirdparty/aarch64/libunwind/libunwind-aarch64.h @@ -0,0 +1,291 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2004 Hewlett-Packard Co + Contributed by David Mosberger-Tang + Copyright (C) 2013 Linaro Limited + Copyright 2022 Blackberry Limited + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef LIBUNWIND_H +#define LIBUNWIND_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" +{ +#endif + +#include +#include +#include +#include +#include + +#ifndef UNW_EMPTY_STRUCT +#define UNW_EMPTY_STRUCT uint8_t unused; +#endif + +#define UNW_TARGET aarch64 +#define UNW_TARGET_AARCH64 1 + +#define _U_TDEP_QP_TRUE 0 /* see libunwind-dynamic.h */ + + /* This needs to be big enough to accommodate "struct cursor", while + leaving some slack for future expansion. Changing this value will + require recompiling all users of this library. Stack allocation is + relatively cheap and unwind-state copying is relatively rare, so we + want to err on making it rather too big than too small. + + Calculation is regs used (64 + 34) * 2 + 40 (bytes of rest of + cursor) + padding + */ + +#define UNW_TDEP_CURSOR_LEN 250 + + typedef uint64_t unw_word_t; + typedef int64_t unw_sword_t; + + typedef long double unw_tdep_fpreg_t; + +#define UNW_WORD_MAX UINT64_MAX + + typedef struct + { + /* no aarch64-specific auxiliary proc-info */ + UNW_EMPTY_STRUCT + } unw_tdep_proc_info_t; + + typedef enum + { + /* 64-bit general registers. */ + UNW_AARCH64_X0, + UNW_AARCH64_X1, + UNW_AARCH64_X2, + UNW_AARCH64_X3, + UNW_AARCH64_X4, + UNW_AARCH64_X5, + UNW_AARCH64_X6, + UNW_AARCH64_X7, + UNW_AARCH64_X8, + + /* Temporary registers. */ + UNW_AARCH64_X9, + UNW_AARCH64_X10, + UNW_AARCH64_X11, + UNW_AARCH64_X12, + UNW_AARCH64_X13, + UNW_AARCH64_X14, + UNW_AARCH64_X15, + + /* Intra-procedure-call temporary registers. */ + UNW_AARCH64_X16, + UNW_AARCH64_X17, + + /* Callee-saved registers. */ + UNW_AARCH64_X18, + UNW_AARCH64_X19, + UNW_AARCH64_X20, + UNW_AARCH64_X21, + UNW_AARCH64_X22, + UNW_AARCH64_X23, + UNW_AARCH64_X24, + UNW_AARCH64_X25, + UNW_AARCH64_X26, + UNW_AARCH64_X27, + UNW_AARCH64_X28, + + /* 64-bit frame pointer. */ + UNW_AARCH64_X29, + + /* 64-bit link register. */ + UNW_AARCH64_X30, + + /* 64-bit stack pointer. */ + UNW_AARCH64_SP = 31, + UNW_AARCH64_PC, + UNW_AARCH64_PSTATE, + + /* Pseudo-register */ + UNW_AARCH64_RA_SIGN_STATE = 34, + + /* SVE Vector Granule pseudo register */ + UNW_AARCH64_VG = 46, + + /* 128-bit FP/Advanced SIMD registers. */ + UNW_AARCH64_V0 = 64, + UNW_AARCH64_V1, + UNW_AARCH64_V2, + UNW_AARCH64_V3, + UNW_AARCH64_V4, + UNW_AARCH64_V5, + UNW_AARCH64_V6, + UNW_AARCH64_V7, + UNW_AARCH64_V8, + UNW_AARCH64_V9, + UNW_AARCH64_V10, + UNW_AARCH64_V11, + UNW_AARCH64_V12, + UNW_AARCH64_V13, + UNW_AARCH64_V14, + UNW_AARCH64_V15, + UNW_AARCH64_V16, + UNW_AARCH64_V17, + UNW_AARCH64_V18, + UNW_AARCH64_V19, + UNW_AARCH64_V20, + UNW_AARCH64_V21, + UNW_AARCH64_V22, + UNW_AARCH64_V23, + UNW_AARCH64_V24, + UNW_AARCH64_V25, + UNW_AARCH64_V26, + UNW_AARCH64_V27, + UNW_AARCH64_V28, + UNW_AARCH64_V29, + UNW_AARCH64_V30, + UNW_AARCH64_V31, + + UNW_AARCH64_FPSR, + UNW_AARCH64_FPCR, + + /* For AArch64, the CFA is the value of SP (x31) at the call site of the + previous frame. */ + UNW_AARCH64_CFA = UNW_AARCH64_SP, + + UNW_TDEP_LAST_REG = UNW_AARCH64_FPCR, + + UNW_TDEP_IP = UNW_AARCH64_X30, + UNW_TDEP_SP = UNW_AARCH64_SP, + UNW_TDEP_EH = UNW_AARCH64_X0 + + } aarch64_regnum_t; + +/* Use R0 through R3 to pass exception handling information. */ +#define UNW_TDEP_NUM_EH_REGS 4 + + typedef struct unw_tdep_save_loc + { + /* Additional target-dependent info on a save location. */ + UNW_EMPTY_STRUCT + } unw_tdep_save_loc_t; + +#ifdef __linux__ + /* On AArch64, we can directly use ucontext_t as the unwind context, + * however, the __reserved struct is quite large: tune it down to only + * the necessary used fields. */ + + struct unw_sigcontext + { + uint64_t fault_address; + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; + alignas(16) uint8_t __reserved[(66 * 8)]; + }; + + typedef struct + { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; +#ifndef __ANDROID__ + sigset_t uc_sigmask; +#else + union + { + sigset_t uc_sigmask; + sigset64_t uc_sigmask64; + }; + char __padding[128 - sizeof(sigset_t)]; +#endif + struct unw_sigcontext uc_mcontext; + } unw_tdep_context_t; + + typedef struct + { + uint32_t _ctx_magic; + uint32_t _ctx_size; + uint32_t fpsr; + uint32_t fpcr; + uint64_t vregs[64]; + } unw_fpsimd_context_t; +#else +/* On AArch64, we can directly use ucontext_t as the unwind context. */ +typedef ucontext_t unw_tdep_context_t; +#endif + +#include "libunwind-common.h" +#include "libunwind-dynamic.h" + +#if defined(__FreeBSD__) +#define UNW_BASE \ + register uint64_t unw_base __asm__("x0") = \ + (uint64_t)unw_ctx->uc_mcontext.mc_gpregs.gp_x; +#elif defined(__QNX__) +#define UNW_BASE \ + register uint64_t unw_base __asm__("x0") = \ + (uint64_t)unw_ctx->uc_mcontext.cpu.gpr; +#else +#define UNW_BASE \ + register uint64_t unw_base __asm__("x0") = \ + (uint64_t)unw_ctx->uc_mcontext.regs; +#endif + +#define unw_tdep_getcontext(uc) \ + ({ \ + unw_tdep_context_t *unw_ctx = (uc); \ + UNW_BASE \ + __asm__ __volatile__("stp x0, x1, [%[base], #0]\n" \ + "stp x2, x3, [%[base], #16]\n" \ + "stp x4, x5, [%[base], #32]\n" \ + "stp x6, x7, [%[base], #48]\n" \ + "stp x8, x9, [%[base], #64]\n" \ + "stp x10, x11, [%[base], #80]\n" \ + "stp x12, x13, [%[base], #96]\n" \ + "stp x14, x15, [%[base], #112]\n" \ + "stp x16, x17, [%[base], #128]\n" \ + "stp x18, x19, [%[base], #144]\n" \ + "stp x20, x21, [%[base], #160]\n" \ + "stp x22, x23, [%[base], #176]\n" \ + "stp x24, x25, [%[base], #192]\n" \ + "stp x26, x27, [%[base], #208]\n" \ + "stp x28, x29, [%[base], #224]\n" \ + "mov x1, sp\n" \ + "stp x30, x1, [%[base], #240]\n" \ + "adr x1, ret%=\n" \ + "str x1, [%[base], #256]\n" \ + "mov %[base], #0\n" \ + "ret%=:\n" \ + : [base] "+r"(unw_base) \ + : \ + : "x1", "memory"); \ + (int)unw_base; \ + }) +#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg) + + extern int unw_tdep_is_fpreg(int); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* LIBUNWIND_H */ diff --git a/systrace/thirdparty/aarch64/libunwind/libunwind-common.h b/systrace/thirdparty/aarch64/libunwind/libunwind-common.h new file mode 100644 index 0000000..9c0db22 --- /dev/null +++ b/systrace/thirdparty/aarch64/libunwind/libunwind-common.h @@ -0,0 +1,335 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2004 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#define UNW_VERSION_MAJOR 1 +#define UNW_VERSION_MINOR 9 +#define UNW_VERSION_EXTRA -pre + +#define UNW_VERSION_CODE(maj, min) (((maj) << 16) | (min)) +#define UNW_VERSION UNW_VERSION_CODE(UNW_VERSION_MAJOR, UNW_VERSION_MINOR) + +#ifdef __sun +// On SmartOS, gcc fails with the following error: +// +// ../include/libunwind-common.h:43:41: error: expected identifier or '(' before +// numeric constant # define UNW_PREFIX UNW_PASTE(UNW_PASTE(_U,UNW_TARGET),_) +// ^ +// +// workaround is to undefine _U explicitly. +// see https://github.com/libunwind/libunwind/issues/118 for more details. +// +#undef _U +#endif + +#define UNW_PASTE2(x, y) x##y +#define UNW_PASTE(x, y) UNW_PASTE2(x, y) +#define UNW_OBJ(fn) UNW_PASTE(UNW_PREFIX, fn) +#define UNW_ARCH_OBJ(fn) UNW_PASTE(UNW_PASTE(UNW_PASTE(_U, UNW_TARGET), _), fn) + +#ifdef UNW_LOCAL_ONLY +#define UNW_PREFIX UNW_PASTE(UNW_PASTE(_UL, UNW_TARGET), _) +#else /* !UNW_LOCAL_ONLY */ +#define UNW_PREFIX UNW_PASTE(UNW_PASTE(_U, UNW_TARGET), _) +#endif /* !UNW_LOCAL_ONLY */ + +/* Error codes. The unwind routines return the *negated* values of + these error codes on error and a non-negative value on success. */ +typedef enum +{ + UNW_ESUCCESS = 0, /* no error */ + UNW_EUNSPEC, /* unspecified (general) error */ + UNW_ENOMEM, /* out of memory */ + UNW_EBADREG, /* bad register number */ + UNW_EREADONLYREG, /* attempt to write read-only register */ + UNW_ESTOPUNWIND, /* stop unwinding */ + UNW_EINVALIDIP, /* invalid IP */ + UNW_EBADFRAME, /* bad frame */ + UNW_EINVAL, /* unsupported operation or bad value */ + UNW_EBADVERSION, /* unwind info has unsupported version */ + UNW_ENOINFO /* no unwind info found */ +} unw_error_t; + +/* The following enum defines the indices for a couple of + (pseudo-)registers which have the same meaning across all + platforms. (RO) means read-only. (RW) means read-write. General + registers (aka "integer registers") are expected to start with + index 0. The number of such registers is architecture-dependent. + The remaining indices can be used as an architecture sees fit. The + last valid register index is given by UNW_REG_LAST. */ +typedef enum +{ + UNW_REG_IP = UNW_TDEP_IP, /* (rw) instruction pointer (pc) */ + UNW_REG_SP = UNW_TDEP_SP, /* (ro) stack pointer */ + UNW_REG_EH = UNW_TDEP_EH, /* (rw) exception-handling reg base */ + UNW_REG_LAST = UNW_TDEP_LAST_REG +} unw_frame_regnum_t; + +/* Number of exception-handler argument registers: */ +#define UNW_NUM_EH_REGS UNW_TDEP_NUM_EH_REGS + +typedef enum +{ + UNW_CACHE_NONE, /* no caching */ + UNW_CACHE_GLOBAL, /* shared global cache */ + UNW_CACHE_PER_THREAD /* per-thread caching */ +} unw_caching_policy_t; + +typedef enum +{ + UNW_INIT_SIGNAL_FRAME = 1 /* We know this is a signal frame */ +} unw_init_local2_flags_t; + +typedef int unw_regnum_t; + +/* The unwind cursor starts at the youngest (most deeply nested) frame + and is used to track the frame state as the unwinder steps from + frame to frame. It is safe to make (shallow) copies of variables + of this type. */ +typedef struct unw_cursor +{ + unw_word_t opaque[UNW_TDEP_CURSOR_LEN]; +} unw_cursor_t; + +/* This type encapsulates the entire (preserved) machine-state. */ +typedef unw_tdep_context_t unw_context_t; + +/* unw_getcontext() fills the unw_context_t pointed to by UC with the + machine state as it exists at the call-site. For implementation + reasons, this needs to be a target-dependent macro. It's easiest + to think of unw_getcontext() as being identical to getcontext(). */ +#define unw_getcontext(uc) unw_tdep_getcontext(uc) + +/* Return 1 if register number R is a floating-point register, zero + otherwise. + This routine is signal-safe. */ +#define unw_is_fpreg(r) unw_tdep_is_fpreg(r) + +typedef unw_tdep_fpreg_t unw_fpreg_t; + +typedef struct unw_addr_space *unw_addr_space_t; + +/* Each target may define it's own set of flags, but bits 0-15 are + reserved for general libunwind-use. */ +#define UNW_PI_FLAG_FIRST_TDEP_BIT 16 +/* The information comes from a .debug_frame section. */ +#define UNW_PI_FLAG_DEBUG_FRAME 32 + +typedef struct unw_proc_info +{ + unw_word_t start_ip; /* first IP covered by this procedure */ + unw_word_t end_ip; /* first IP NOT covered by this procedure */ +#if defined(NEED_LAST_IP) + unw_word_t last_ip; /* first IP that could begin another procedure */ +#endif + unw_word_t lsda; /* address of lang.-spec. data area (if any) */ + unw_word_t handler; /* optional personality routine */ + unw_word_t gp; /* global-pointer value for this procedure */ + unw_word_t flags; /* misc. flags */ + + int format; /* unwind-info format (arch-specific) */ + int unwind_info_size; /* size of the information (if applicable) */ + void *unwind_info; /* unwind-info (arch-specific) */ + unw_tdep_proc_info_t extra; /* target-dependent auxiliary proc-info */ +} unw_proc_info_t; + +typedef int (*unw_reg_states_callback)(void *token, void *reg_states_data, + size_t reg_states_data_size, + unw_word_t start_ip, unw_word_t end_ip); + +/* These are backend callback routines that provide access to the + state of a "remote" process. This can be used, for example, to + unwind another process through the ptrace() interface. */ +typedef struct unw_accessors +{ + /* Look up the unwind info associated with instruction-pointer IP. + On success, the routine fills in the PROC_INFO structure. */ + int (*find_proc_info)(unw_addr_space_t, unw_word_t, unw_proc_info_t *, int, + void *); + + /* Release any resources (e.g., memory) that were allocated for + the unwind info returned in by a previous call to + find_proc_info() with NEED_UNWIND_INFO set to 1. */ + void (*put_unwind_info)(unw_addr_space_t, unw_proc_info_t *, void *); + + /* Return the list-head of the dynamically registered unwind + info. */ + int (*get_dyn_info_list_addr)(unw_addr_space_t, unw_word_t *, void *); + + /* Access aligned word at address ADDR. The value is returned + according to the endianness of the host (e.g., if the host is + little-endian and the target is big-endian, access_mem() needs + to byte-swap the value before returning it). */ + int (*access_mem)(unw_addr_space_t, unw_word_t, unw_word_t *, int, void *); + + /* Access register number REG at address ADDR. */ + int (*access_reg)(unw_addr_space_t, unw_regnum_t, unw_word_t *, int, + void *); + + /* Access register number REG at address ADDR. */ + int (*access_fpreg)(unw_addr_space_t, unw_regnum_t, unw_fpreg_t *, int, + void *); + + int (*resume)(unw_addr_space_t, unw_cursor_t *, void *); + + /* Optional call back to obtain the name of a (static) procedure. + Dynamically generated procedures are handled automatically by + libunwind. This callback is optional and may be set to + NULL. */ + int (*get_proc_name)(unw_addr_space_t, unw_word_t, char *, size_t, + unw_word_t *, void *); + + /* Optional call back to obtain the name of a elf file where the ip belongs + to. This callback is optional and may be set to NULL. */ + int (*get_elf_filename)(unw_addr_space_t, unw_word_t, char *, size_t, + unw_word_t *, void *); + + /* Optional call back to obtain the start and end ip of a procedure. + * procedure ip range is [start, end), the range is without end. + * This callback is optional and may be set to NULL. + */ + int (*get_proc_ip_range)(unw_addr_space_t, unw_word_t, unw_word_t *, + unw_word_t *, void *); + + /* Optional call back to return a mask to be used with pointer + * authentication on arm64. + * + * The on bits in the returned mask indicate which bits in a return address + * are part of a pointer authentication code. These are the bits in the + * return address to turn off so that the calling frame can be found + * for the unwinding to continue. + * + * The return value must be host-endian. e.g. if the target is big-endian + * and the host is little endian, the implementation of this function + * must byte swap. + * + * This callback is optional and may be set to NULL. In this case all + * the bits in the return address are used, as if no masking were done. + */ + unw_word_t (*ptrauth_insn_mask)(unw_addr_space_t, void *); + +} unw_accessors_t; + +typedef enum unw_save_loc_type +{ + UNW_SLT_NONE, /* register is not saved ("not an l-value") */ + UNW_SLT_MEMORY, /* register has been saved in memory */ + UNW_SLT_REG /* register has been saved in (another) register */ +} unw_save_loc_type_t; + +typedef struct unw_save_loc +{ + unw_save_loc_type_t type; + union + { + unw_word_t addr; /* valid if type==UNW_SLT_MEMORY */ + unw_regnum_t regnum; /* valid if type==UNW_SLT_REG */ + } u; + unw_tdep_save_loc_t extra; /* target-dependent additional information */ +} unw_save_loc_t; + +struct dl_phdr_info; +typedef int (*unw_iterate_phdr_callback_t)(struct dl_phdr_info *, size_t, + void *); +typedef int (*unw_iterate_phdr_func_t)(unw_iterate_phdr_callback_t, void *); + +/* These routines work both for local and remote unwinding. */ + +#define unw_local_addr_space UNW_OBJ(local_addr_space) +#define unw_create_addr_space UNW_OBJ(create_addr_space) +#define unw_destroy_addr_space UNW_OBJ(destroy_addr_space) +#define unw_get_accessors UNW_ARCH_OBJ(get_accessors) +#define unw_get_accessors_int UNW_ARCH_OBJ(get_accessors_int) +#define unw_init_local UNW_OBJ(init_local) +#define unw_init_local2 UNW_OBJ(init_local2) +#define unw_init_remote UNW_OBJ(init_remote) +#define unw_step UNW_OBJ(step) +#define unw_resume UNW_OBJ(resume) +#define unw_get_proc_info UNW_OBJ(get_proc_info) +#define unw_get_proc_info_by_ip UNW_OBJ(get_proc_info_by_ip) +#define unw_get_proc_info_in_range UNW_OBJ(get_proc_info_in_range) +#define unw_reg_states_iterate UNW_OBJ(reg_states_iterate) +#define unw_apply_reg_state UNW_OBJ(apply_reg_state) +#define unw_get_reg UNW_OBJ(get_reg) +#define unw_set_reg UNW_OBJ(set_reg) +#define unw_get_fpreg UNW_OBJ(get_fpreg) +#define unw_set_fpreg UNW_OBJ(set_fpreg) +#define unw_get_save_loc UNW_OBJ(get_save_loc) +#define unw_is_signal_frame UNW_OBJ(is_signal_frame) +#define unw_is_plt_entry UNW_OBJ(is_plt_entry) +#define unw_get_proc_name UNW_OBJ(get_proc_name) +#define unw_get_proc_name_by_ip UNW_OBJ(get_proc_name_by_ip) +#define unw_get_elf_filename UNW_OBJ(get_elf_filename) +#define unw_get_elf_filename_by_ip UNW_OBJ(get_elf_filename_by_ip) +#define unw_set_caching_policy UNW_OBJ(set_caching_policy) +#define unw_set_cache_size UNW_OBJ(set_cache_size) +#define unw_set_iterate_phdr_function UNW_OBJ(set_iterate_phdr_function) +#define unw_regname UNW_ARCH_OBJ(regname) +#define unw_flush_cache UNW_ARCH_OBJ(flush_cache) +#define unw_strerror UNW_ARCH_OBJ(strerror) + +extern unw_addr_space_t unw_create_addr_space(unw_accessors_t *, int); +extern void unw_destroy_addr_space(unw_addr_space_t); +extern unw_accessors_t *unw_get_accessors(unw_addr_space_t); +extern unw_accessors_t *unw_get_accessors_int(unw_addr_space_t); +extern void unw_flush_cache(unw_addr_space_t, unw_word_t, unw_word_t); +extern int unw_set_caching_policy(unw_addr_space_t, unw_caching_policy_t); +extern int unw_set_cache_size(unw_addr_space_t, size_t, int); +extern void unw_set_iterate_phdr_function(unw_addr_space_t, + unw_iterate_phdr_func_t); +extern const char *unw_regname(unw_regnum_t); + +extern int unw_init_local(unw_cursor_t *, unw_context_t *); +extern int unw_init_local2(unw_cursor_t *, unw_context_t *, int); +extern int unw_init_remote(unw_cursor_t *, unw_addr_space_t, void *); +extern int unw_step(unw_cursor_t *); +extern int unw_resume(unw_cursor_t *); +extern int unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *); +extern int unw_get_proc_info_by_ip(unw_addr_space_t, unw_word_t, + unw_proc_info_t *, void *); +extern int unw_get_proc_info_in_range(unw_word_t, unw_word_t, unw_word_t, + unw_word_t, unw_word_t, unw_word_t, + unw_addr_space_t, unw_word_t, + unw_proc_info_t *, int, void *); +extern int unw_reg_states_iterate(unw_cursor_t *, unw_reg_states_callback, + void *); +extern int unw_apply_reg_state(unw_cursor_t *, void *); +extern int unw_get_reg(unw_cursor_t *, int, unw_word_t *); +extern int unw_set_reg(unw_cursor_t *, int, unw_word_t); +extern int unw_get_fpreg(unw_cursor_t *, int, unw_fpreg_t *); +extern int unw_set_fpreg(unw_cursor_t *, int, unw_fpreg_t); +extern int unw_get_save_loc(unw_cursor_t *, int, unw_save_loc_t *); +extern int unw_is_signal_frame(unw_cursor_t *); +extern int unw_is_plt_entry(unw_cursor_t *); +extern int unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *); +extern int unw_get_proc_name_by_ip(unw_addr_space_t, unw_word_t, char *, size_t, + unw_word_t *, void *); +extern int unw_get_elf_filename(unw_cursor_t *, char *, size_t, unw_word_t *); +extern int unw_get_elf_filename_by_ip(unw_addr_space_t, unw_word_t, char *, + size_t, unw_word_t *, void *); +extern const char *unw_strerror(int); +extern int unw_backtrace(void **, int); +extern int unw_backtrace2(void **, int, unw_context_t *, int); + +extern unw_addr_space_t unw_local_addr_space; diff --git a/systrace/thirdparty/aarch64/libunwind/libunwind-dynamic.h b/systrace/thirdparty/aarch64/libunwind/libunwind-dynamic.h new file mode 100644 index 0000000..13caf16 --- /dev/null +++ b/systrace/thirdparty/aarch64/libunwind/libunwind-dynamic.h @@ -0,0 +1,201 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2002-2004 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +/* This file defines the runtime-support routines for dynamically +generated code. Even though it is implemented as part of libunwind, +it is logically separate from the interface to perform the actual +unwinding. In particular, this interface is always used in the +context of the unwind target, whereas the rest of the unwind API is +used in context of the process that is doing the unwind (which may be +a debugger running on another machine, for example). + +Note that the data-structures declared here server a dual purpose: +when a program registers a dynamically generated procedure, it uses +these structures directly. On the other hand, with remote-unwinding, +the data-structures are read from the remote process's memory and +translated into internalized versions. To facilitate remote-access, +the following rules should be followed in declaring these structures: + + (1) Declare a member as a pointer only if the the information the + member points to needs to be internalized as well (e.g., a + string representing a procedure name should be declared as + "const char *", but the instruction pointer should be declared + as unw_word_t). + + (2) Provide sufficient padding to ensure that no implicit padding + will be needed on any of the supported target architectures. For + the time being, padding data structures with the assumption that + sizeof (unw_word_t) == 8 should be sufficient. (Note: it's not + impossible to internalize structures with internal padding, but + it does make the process a bit harder). + + (3) Don't declare members that contain bitfields or floating-point + values. + + (4) Don't declare members with enumeration types. Declare them as + int32_t instead. */ + +typedef enum +{ + UNW_DYN_STOP = 0, /* end-of-unwind-info marker */ + UNW_DYN_SAVE_REG, /* save register to another register */ + UNW_DYN_SPILL_FP_REL, /* frame-pointer-relative register spill */ + UNW_DYN_SPILL_SP_REL, /* stack-pointer-relative register spill */ + UNW_DYN_ADD, /* add constant value to a register */ + UNW_DYN_POP_FRAMES, /* drop one or more stack frames */ + UNW_DYN_LABEL_STATE, /* name the current state */ + UNW_DYN_COPY_STATE, /* set the region's entry-state */ + UNW_DYN_ALIAS /* get unwind info from an alias */ +} unw_dyn_operation_t; + +typedef enum +{ + UNW_INFO_FORMAT_DYNAMIC, /* unw_dyn_proc_info_t */ + UNW_INFO_FORMAT_TABLE, /* unw_dyn_table_t */ + UNW_INFO_FORMAT_REMOTE_TABLE, /* unw_dyn_remote_table_t */ + UNW_INFO_FORMAT_ARM_EXIDX, /* ARM specific unwind info */ + UNW_INFO_FORMAT_IP_OFFSET /* Like UNW_INFO_FORMAT_REMOTE_TABLE, but + table entries are considered + relative to di->start_ip, rather + than di->segbase */ +} unw_dyn_info_format_t; + +typedef struct unw_dyn_op +{ + int8_t tag; /* what operation? */ + int8_t qp; /* qualifying predicate register */ + int16_t reg; /* what register */ + int32_t when; /* when does it take effect? */ + unw_word_t val; /* auxiliary value */ +} unw_dyn_op_t; + +typedef struct unw_dyn_region_info +{ + struct unw_dyn_region_info *next; /* linked list of regions */ + int32_t insn_count; /* region length (# of instructions) */ + uint32_t op_count; /* length of op-array */ + unw_dyn_op_t op[1]; /* variable-length op-array */ +} unw_dyn_region_info_t; + +typedef struct unw_dyn_proc_info +{ + unw_word_t name_ptr; /* address of human-readable procedure name */ + unw_word_t handler; /* address of personality routine */ + uint32_t flags; + int32_t pad0; + unw_dyn_region_info_t *regions; +} unw_dyn_proc_info_t; + +typedef struct unw_dyn_table_info +{ + unw_word_t name_ptr; /* addr. of table name (e.g., library name) */ + unw_word_t segbase; /* segment base */ + unw_word_t table_len; /* must be a multiple of sizeof(unw_word_t)! */ + unw_word_t *table_data; +} unw_dyn_table_info_t; + +typedef struct unw_dyn_remote_table_info +{ + unw_word_t name_ptr; /* addr. of table name (e.g., library name) */ + unw_word_t segbase; /* segment base */ + unw_word_t table_len; /* must be a multiple of sizeof(unw_word_t)! */ + unw_word_t table_data; +} unw_dyn_remote_table_info_t; + +typedef struct unw_dyn_info +{ + /* doubly-linked list of dyn-info structures: */ + struct unw_dyn_info *next; + struct unw_dyn_info *prev; + unw_word_t start_ip; /* first IP covered by this entry */ + unw_word_t end_ip; /* first IP NOT covered by this entry */ + unw_word_t gp; /* global-pointer in effect for this entry */ + int32_t format; /* real type: unw_dyn_info_format_t */ + int32_t pad; + unw_word_t load_offset; /* ELF load offset */ + union + { + unw_dyn_proc_info_t pi; + unw_dyn_table_info_t ti; + unw_dyn_remote_table_info_t rti; + } u; +} unw_dyn_info_t; + +typedef struct unw_dyn_info_list +{ + uint32_t version; + uint32_t generation; + unw_dyn_info_t *first; +} unw_dyn_info_list_t; + +/* Return the size (in bytes) of an unw_dyn_region_info_t structure that can + hold OP_COUNT ops. */ +#define _U_dyn_region_info_size(op_count) \ + ((char *)(((unw_dyn_region_info_t *)NULL)->op + (op_count)) - (char *)NULL) + +/* Register the unwind info for a single procedure. + This routine is NOT signal-safe. */ +extern void _U_dyn_register(unw_dyn_info_t *); + +/* Cancel the unwind info for a single procedure. + This routine is NOT signal-safe. */ +extern void _U_dyn_cancel(unw_dyn_info_t *); + +/* Convenience routines. */ + +#define _U_dyn_op(_tag, _qp, _when, _reg, _val) \ + ((unw_dyn_op_t){(_tag), (_qp), (_reg), (_when), (_val)}) + +#define _U_dyn_op_save_reg(op, qp, when, reg, dst) \ + (*(op) = _U_dyn_op(UNW_DYN_SAVE_REG, (qp), (when), (reg), (dst))) + +#define _U_dyn_op_spill_fp_rel(op, qp, when, reg, offset) \ + (*(op) = _U_dyn_op(UNW_DYN_SPILL_FP_REL, (qp), (when), (reg), (offset))) + +#define _U_dyn_op_spill_sp_rel(op, qp, when, reg, offset) \ + (*(op) = _U_dyn_op(UNW_DYN_SPILL_SP_REL, (qp), (when), (reg), (offset))) + +#define _U_dyn_op_add(op, qp, when, reg, value) \ + (*(op) = _U_dyn_op(UNW_DYN_ADD, (qp), (when), (reg), (value))) + +#define _U_dyn_op_pop_frames(op, qp, when, num_frames) \ + (*(op) = _U_dyn_op(UNW_DYN_POP_FRAMES, (qp), (when), 0, (num_frames))) + +#define _U_dyn_op_label_state(op, label) \ + (*(op) = _U_dyn_op(UNW_DYN_LABEL_STATE, _U_QP_TRUE, -1, 0, (label))) + +#define _U_dyn_op_copy_state(op, label) \ + (*(op) = _U_dyn_op(UNW_DYN_COPY_STATE, _U_QP_TRUE, -1, 0, (label))) + +#define _U_dyn_op_alias(op, qp, when, addr) \ + (*(op) = _U_dyn_op(UNW_DYN_ALIAS, (qp), (when), 0, (addr))) + +#define _U_dyn_op_stop(op) \ + (*(op) = _U_dyn_op(UNW_DYN_STOP, _U_QP_TRUE, -1, 0, 0)) + +/* The target-dependent qualifying predicate which is always TRUE. On + IA-64, that's p0 (0), on non-predicated architectures, the value is + ignored. */ +#define _U_QP_TRUE _U_TDEP_QP_TRUE diff --git a/systrace/thirdparty/aarch64/libunwind/libunwind.h b/systrace/thirdparty/aarch64/libunwind/libunwind.h new file mode 100644 index 0000000..1624c7f --- /dev/null +++ b/systrace/thirdparty/aarch64/libunwind/libunwind.h @@ -0,0 +1,40 @@ +/* Provide a real file - not a symlink - as it would cause multiarch conflicts + when multiple different arch releases are installed simultaneously. */ + +#ifndef UNW_REMOTE_ONLY + +#if defined __aarch64__ +#include "libunwind-aarch64.h" +#elif defined __arm__ +#include "libunwind-arm.h" +#elif defined __hppa__ +#include "libunwind-hppa.h" +#elif defined __ia64__ +#include "libunwind-ia64.h" +#elif defined __mips__ +#include "libunwind-mips.h" +#elif defined __powerpc__ && !defined __powerpc64__ +#include "libunwind-ppc32.h" +#elif defined __powerpc64__ +#include "libunwind-ppc64.h" +#elif defined __sh__ +#include "libunwind-sh.h" +#elif defined __i386__ +#include "libunwind-x86.h" +#elif defined __x86_64__ +#include "libunwind-x86_64.h" +#elif defined __s390x__ +#include "libunwind-s390x.h" +#elif defined __riscv || defined __riscv__ +#include "libunwind-riscv.h" +#elif defined __loongarch64 +#include "libunwind-loongarch64.h" +#else +#error "Unsupported arch" +#endif + +#else /* UNW_REMOTE_ONLY */ + +#include "libunwind-aarch64.h" + +#endif /* UNW_REMOTE_ONLY */ diff --git a/systrace/thirdparty/aarch64/libunwind/unwind.h b/systrace/thirdparty/aarch64/libunwind/unwind.h new file mode 100644 index 0000000..69201dc --- /dev/null +++ b/systrace/thirdparty/aarch64/libunwind/unwind.h @@ -0,0 +1,158 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _UNWIND_H +#define _UNWIND_H + +/* For uint64_t */ +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Minimal interface as per C++ ABI draft standard: + + http://www.codesourcery.com/cxx-abi/abi-eh.html */ + + typedef enum + { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 + } _Unwind_Reason_Code; + + typedef int _Unwind_Action; + +#define _UA_SEARCH_PHASE 1 +#define _UA_CLEANUP_PHASE 2 +#define _UA_HANDLER_FRAME 4 +#define _UA_FORCE_UNWIND 8 + + struct _Unwind_Context; /* opaque data-structure */ + struct _Unwind_Exception; /* forward-declaration */ + + typedef void (*_Unwind_Exception_Cleanup_Fn)(_Unwind_Reason_Code, + struct _Unwind_Exception *); + + typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)(int, _Unwind_Action, + uint64_t, + struct _Unwind_Exception *, + struct _Unwind_Context *, + void *); + + /* The C++ ABI requires exception_class, private_1, and private_2 to + be of type uint64 and the entire structure to be + double-word-aligned. Please note that exception_class stays 64-bit + even on 32-bit machines for gcc compatibility. */ + struct _Unwind_Exception + { + alignas(8) uint64_t exception_class; + _Unwind_Exception_Cleanup_Fn exception_cleanup; + unsigned long private_1; + unsigned long private_2; + }; + + extern _Unwind_Reason_Code + _Unwind_RaiseException(struct _Unwind_Exception *); + extern _Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception *, + _Unwind_Stop_Fn, void *); + extern void _Unwind_Resume(struct _Unwind_Exception *); + extern void _Unwind_DeleteException(struct _Unwind_Exception *); + extern unsigned long _Unwind_GetGR(struct _Unwind_Context *, int); + extern void _Unwind_SetGR(struct _Unwind_Context *, int, unsigned long); + extern unsigned long _Unwind_GetIP(struct _Unwind_Context *); + extern unsigned long _Unwind_GetIPInfo(struct _Unwind_Context *, int *); + extern void _Unwind_SetIP(struct _Unwind_Context *, unsigned long); + extern unsigned long + _Unwind_GetLanguageSpecificData(struct _Unwind_Context *); + extern unsigned long _Unwind_GetRegionStart(struct _Unwind_Context *); + +#ifdef _GNU_SOURCE + + /* Callback for _Unwind_Backtrace(). The backtrace stops immediately + if the callback returns any value other than _URC_NO_REASON. */ + typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *, + void *); + +/* See http://gcc.gnu.org/ml/gcc-patches/2001-09/msg00082.html for why + _UA_END_OF_STACK exists. */ +#define _UA_END_OF_STACK 16 + + /* If the unwind was initiated due to a forced unwind, resume that + operation, else re-raise the exception. This is used by + __cxa_rethrow(). */ + extern _Unwind_Reason_Code + _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *); + + /* See http://gcc.gnu.org/ml/gcc-patches/2003-09/msg00154.html for why + _Unwind_GetBSP() exists. */ + extern unsigned long _Unwind_GetBSP(struct _Unwind_Context *); + + /* Return the "canonical frame address" for the given context. + This is used by NPTL... */ + extern unsigned long _Unwind_GetCFA(struct _Unwind_Context *); + + /* Return the base-address for data references. */ + extern unsigned long _Unwind_GetDataRelBase(struct _Unwind_Context *); + + /* Return the base-address for text references. */ + extern unsigned long _Unwind_GetTextRelBase(struct _Unwind_Context *); + + /* Call _Unwind_Trace_Fn once for each stack-frame, without doing any + cleanup. The first frame for which the callback is invoked is the + one for the caller of _Unwind_Backtrace(). _Unwind_Backtrace() + returns _URC_END_OF_STACK when the backtrace stopped due to + reaching the end of the call-chain or _URC_FATAL_PHASE1_ERROR if it + stops for any other reason. */ + extern _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *); + + /* Find the start-address of the procedure containing the specified IP + or NULL if it cannot be found (e.g., because the function has no + unwind info). Note: there is not necessarily a one-to-one + correspondence between source-level functions and procedures: some + functions don't have unwind-info and others are split into multiple + procedures. */ + extern void *_Unwind_FindEnclosingFunction(void *); + + /* See also Linux Standard Base Spec: + http://www.linuxbase.org/spec/refspecs/LSB_1.3.0/gLSB/gLSB/libgcc-s.html + */ + +#endif /* _GNU_SOURCE */ + +#ifdef __cplusplus +}; +#endif + +#endif /* _UNWIND_H */ diff --git a/systrace/thirdparty/aarch64/mspti/include/mspti.h b/systrace/thirdparty/aarch64/mspti/include/mspti.h new file mode 100644 index 0000000..e83c454 --- /dev/null +++ b/systrace/thirdparty/aarch64/mspti/include/mspti.h @@ -0,0 +1,19 @@ +/** + * @file mspti.h + * + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef MSPTI_H +#define MSPTI_H + +#include "mspti_activity.h" +#include "mspti_callback.h" +#include "mspti_cbid.h" +#include "mspti_result.h" + +#endif diff --git a/systrace/thirdparty/aarch64/mspti/include/mspti_activity.h b/systrace/thirdparty/aarch64/mspti/include/mspti_activity.h new file mode 100644 index 0000000..30f7159 --- /dev/null +++ b/systrace/thirdparty/aarch64/mspti/include/mspti_activity.h @@ -0,0 +1,424 @@ +/** + * @file mspti_activity.h + * + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef MSPTI_ACTIVITY_H +#define MSPTI_ACTIVITY_H + +#define ACTIVITY_STRUCT_ALIGNMENT 8 +#if defined(_WIN32) +#define START_PACKED_ALIGNMENT __pragma(pack(push, 1)) +#define PACKED_ALIGNMENT __declspec(align(ACTIVITY_STRUCT_ALIGNMENT)) +#define END_PACKED_ALIGNMENT __pragma(pack(pop)) +#elif defined(__GNUC__) +#define START_PACKED_ALIGNMENT +#define PACKED_ALIGNMENT \ + __attribute__((__packed__)) \ + __attribute__((aligned(ACTIVITY_STRUCT_ALIGNMENT))) +#define END_PACKED_ALIGNMENT +#else +#define START_PACKED_ALIGNMENT +#define PACKED_ALIGNMENT +#define END_PACKED_ALIGNMENT +#endif + +#include "mspti_result.h" +#include +#include + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#if defined(__GNUC__) && defined(MSPTI_LIB) +#pragma GCC visibility push(default) +#endif + + /** + * @brief The kinds of activity records. + * + * Each kind is associated with a + * activity record structure that holds the information associated + * with the kind. + */ + typedef enum + { + /** + * The activity record is invalid. + */ + MSPTI_ACTIVITY_KIND_INVALID = 0, + MSPTI_ACTIVITY_KIND_MARKER = 1, + MSPTI_ACTIVITY_KIND_KERNEL = 2, + MSPTI_ACTIVITY_KIND_API = 3, + MSPTI_ACTIVITY_KIND_COUNT, + MSPTI_ACTIVITY_KIND_FORCE_INT = 0x7fffffff + } msptiActivityKind; + + /** + * @brief The source kinds of mark data. + * + * Each mark activity record kind represents information about host or + * device + */ + typedef enum + { + MSPTI_ACTIVITY_SOURCE_KIND_HOST = 0, + MSPTI_ACTIVITY_SOURCE_KIND_DEVICE = 1 + } msptiActivitySourceKind; + + /** + * @brief Flags linked to activity records. + * + * These are the Flags that pertain to activity records. + * Flags can be combined by bitwise OR to + * associated multiple flags with an activity record. + */ + typedef enum + { + /** + * Signifies that the activity record lacks any flags. + */ + MSPTI_ACTIVITY_FLAG_NONE = 0, + /** + * Represents the activity as a pure host instantaneous marker. Works + * with MSPTI_ACTIVITY_KIND_MARKER. + */ + MSPTI_ACTIVITY_FLAG_MARKER_INSTANTANEOUS = 1 << 0, + /** + * Represents the activity as a pure host region start marker. Works + * with MSPTI_ACTIVITY_KIND_MARKER. + */ + MSPTI_ACTIVITY_FLAG_MARKER_START = 1 << 1, + /** + * Represents the activity as a pure host region end marker. Works with + * MSPTI_ACTIVITY_KIND_MARKER. + */ + MSPTI_ACTIVITY_FLAG_MARKER_END = 1 << 2, + /** + * Represents the activity as an instantaneous marker with device. Works + * with MSPTI_ACTIVITY_KIND_MARKER. + */ + MSPTI_ACTIVITY_FLAG_MARKER_INSTANTANEOUS_WITH_DEVICE = 1 << 3, + /** + * Represents the activity as a pure start marker with device. Works + * with MSPTI_ACTIVITY_KIND_MARKER. + */ + MSPTI_ACTIVITY_FLAG_MARKER_START_WITH_DEVICE = 1 << 4, + /** + * Represents the activity as a pure end marker with device. Works with + * MSPTI_ACTIVITY_KIND_MARKER. + */ + MSPTI_ACTIVITY_FLAG_MARKER_END_WITH_DEVICE = 1 << 5 + } msptiActivityFlag; + + START_PACKED_ALIGNMENT + + typedef struct PACKED_ALIGNMENT + { + msptiActivityKind kind; + } msptiActivity; + + typedef union PACKED_ALIGNMENT + { + /** + * A thread object requires that we identify both the process and + * thread ID. + */ + struct + { + uint32_t processId; + uint32_t threadId; + } pt; + /** + * A stream object requires that we identify device and stream ID. + */ + struct + { + uint32_t deviceId; + uint32_t streamId; + } ds; + } msptiObjectId; + + /** + * @brief This activity record serves as a marker, representing a specific + * moment in time. + * + * The marker is characterized by a distinctive name and a unique identifier + */ + typedef struct PACKED_ALIGNMENT + { + /** + * The activity record kind, always be MSPTI_ACTIVITY_KIND_MARKER. + */ + msptiActivityKind kind; + + /** + * The flags associated with the marker. + * @see msptiActivityFlag + */ + msptiActivityFlag flag; + + /** + * The source kinds of mark data. + * @see msptiActivitySourceKind + */ + msptiActivitySourceKind sourceKind; + + /** + * The timestamp for the marker, in ns. A value of 0 indicates that + * timestamp information could not be collected for the marker. + */ + uint64_t timestamp; + + /** + * The marker ID. + */ + uint64_t id; + + /** + * The identifier for the activity object associated with this + * marker. 'objectKind' indicates which ID is valid for this record. + */ + msptiObjectId objectId; + + /** + * The marker name for an instantaneous or start marker. + * This will be NULL for an end marker. + */ + const char *name; + + /** + * The name of the domain to which this marker belongs to. + * This will be NULL for default domain. + */ + const char *domain; + } msptiActivityMarker; + + typedef struct PACKED_ALIGNMENT + { + /** + * The activity record kind, must be MSPTI_ACTIVITY_KIND_API. + */ + msptiActivityKind kind; + + /** + * The start timestamp for the api, in ns. + */ + uint64_t start; + + /** + * The end timestamp for the api, in ns. + */ + uint64_t end; + + /** + * A thread object requires that we identify both the process and + * thread ID. + */ + struct + { + uint32_t processId; + uint32_t threadId; + } pt; + + /** + * The correlation ID of the kernel. + */ + uint64_t correlationId; + + /** + * The api name. + */ + const char *name; + } msptiActivityApi; + + typedef struct PACKED_ALIGNMENT + { + /** + * The activity record kind, must be MSPTI_ACTIVITY_KIND_KERNEL. + */ + msptiActivityKind kind; + + /** + * The start timestamp for the kernel, in ns. + */ + uint64_t start; + + /** + * The end timestamp for the kernel, in ns. + */ + uint64_t end; + + /** + * A stream object requires that we identify device and stream ID. + */ + struct + { + uint32_t deviceId; + uint32_t streamId; + } ds; + + /** + * The correlation ID of the kernel. + */ + uint64_t correlationId; + + /** + * The kernel type. + */ + const char *type; + + /** + * The kernel name. + */ + const char *name; + } msptiActivityKernel; + + END_PACKED_ALIGNMENT + + /** + * @brief Function type for callback used by MSPTI to request an empty + * buffer for storing activity records. + * + * This callback function signals the MSPTI client that an activity + * buffer is needed by MSPTI. The activity buffer is used by MSPTI to + * store activity records. The callback function can decline the + * request by setting **buffer to NULL. In this case MSPTI may drop + * activity records. + * + * @param buffer Returns the new buffer. If set to NULL then no buffer + * is returned. + * @param size Returns the size of the returned buffer. + * @param maxNumRecords Returns the maximum number of records that + * should be placed in the buffer. If 0 then the buffer is filled with + * as many records as possible. If > 0 the buffer is filled with at + * most that many records before it is returned. + */ + typedef void (*msptiBuffersCallbackRequestFunc)(uint8_t **buffer, + size_t *size, + size_t *maxNumRecords); + + /** + * @brief Function type for callback used by MSPTI to return a buffer + * of activity records. + * + * This callback function returns to the MSPTI client a buffer + * containing activity records. The buffer contains @p validSize + * bytes of activity records which should be read using + * msptiActivityGetNextRecord. After this call MSPTI + * relinquished ownership of the buffer and will not use it + * anymore. The client may return the buffer to MSPTI using the + * msptiBuffersCallbackRequestFunc callback. + * + * @param buffer The activity record buffer. + * @param size The total size of the buffer in bytes as set in + * MSPTI_BuffersCallbackRequestFunc. + * @param validSize The number of valid bytes in the buffer. + */ + typedef void (*msptiBuffersCallbackCompleteFunc)(uint8_t *buffer, + size_t size, + size_t validSize); + + /** + * @brief Registers callback functions with MSPTI for activity buffer + * handling. + * + * This function registers two callback functions to be used in asynchronous + * buffer handling. If registered, activity record buffers are handled using + * asynchronous requested/completed callbacks from MSPTI. + * + * @param funcBufferRequested callback which is invoked when an empty + * buffer is requested by MSPTI + * @param funcBufferCompleted callback which is invoked when a buffer + * containing activity records is available from MSPTI + * + * @retval MSPTI_SUCCESS + * @retval MSPTI_ERROR_INVALID_PARAMETER if either + * funcBufferRequested or funcBufferCompleted is NULL + */ + msptiResult msptiActivityRegisterCallbacks( + msptiBuffersCallbackRequestFunc funcBufferRequested, + msptiBuffersCallbackCompleteFunc funcBufferCompleted); + + /** + * @brief Enable collection of a specific kind of activity record. + * + * Enable collection of a specific kind of activity record. Multiple + * kinds can be enabled by calling this function multiple times. + * By default, the collection of all activity types is inactive. + * + * @param kind The kind of activity record to collect + * + * @retval MSPTI_SUCCESS + */ + msptiResult msptiActivityEnable(msptiActivityKind kind); + + /** + * @brief Disable collection of a specific kind of activity record. + * + * Disable collection of a specific kind of activity record. Multiple + * kinds can be disabled by calling this function multiple times. + * By default, the collection of all activity types is inactive. + * + * @param kind The kind of activity record to stop collecting + * + * @retval MSPTI_SUCCESS + */ + msptiResult msptiActivityDisable(msptiActivityKind kind); + + /** + * @brief Iterate over the activity records in a buffer. + * + * This is a function to iterate over the activity records in buffer. + * + * @param buffer The buffer containing activity records + * @param validBufferSizeBytes The number of valid bytes in the buffer. + * @param record Inputs the previous record returned by + * msptiActivityGetNextRecord and returns the next activity record + * from the buffer. If input value is NULL, returns the first activity + * record in the buffer. + * + * @retval MSPTI_SUCCESS + * @retval MSPTI_ERROR_MAX_LIMIT_REACHED if no more records in the buffer + * @retval MSPTI_ERROR_INVALID_PARAMETER if buffer is NULL. + */ + msptiResult msptiActivityGetNextRecord(uint8_t *buffer, + size_t validBufferSizeBytes, + msptiActivity **record); + + /** + * @brief Request to deliver activity records via the buffer completion + * callback. + * + * This function returns the activity records associated with all + * contexts/streams (and the global buffers not associated with any stream) + * to the MSPTI client using the callback registered in + * msptiActivityRegisterCallbacks. It return all activity buffers that + * contain completed activity records, even if these buffers are not + * completely filled. + * + * Before calling this function, the buffer handling callback api must be + * activated by calling msptiActivityRegisterCallbacks. + * + * @param flag Reserved for internal use. + * + * @retval MSPTI_SUCCESS + */ + msptiResult msptiActivityFlushAll(uint32_t flag); + +#if defined(__GNUC__) && defined(MSPTI_LIB) +#pragma GCC visibility pop +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/systrace/thirdparty/aarch64/mspti/include/mspti_callback.h b/systrace/thirdparty/aarch64/mspti/include/mspti_callback.h new file mode 100644 index 0000000..2e6f7ee --- /dev/null +++ b/systrace/thirdparty/aarch64/mspti/include/mspti_callback.h @@ -0,0 +1,258 @@ +/** + * @file mspti_callback.h + * + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef MSPTI_CALLBACK_H +#define MSPTI_CALLBACK_H + +#include "mspti_cbid.h" +#include "mspti_result.h" +#include + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#if defined(__GNUC__) && defined(MSPTI_LIB) +#pragma GCC visibility push(default) +#endif + + /** + * @brief Callback domains. + * + * Callback domains. Each domain represents callback points for a + * group of related API functions or CANN driver activity. + */ + typedef enum + { + /** + * Invalid domain. + */ + MSPTI_CB_DOMAIN_INVALID = 0, + /** + * Domain containing callback points for all runtime API functions. + */ + MSPTI_CB_DOMAIN_RUNTIME = 1, + MSPTI_CB_DOMAIN_HCCL = 2, + MSPTI_CB_DOMAIN_SIZE, + MSPTI_CB_DOMAIN_FORCE_INT = 0x7fffffff + } msptiCallbackDomain; + + typedef uint32_t msptiCallbackId; + + /** + * @brief Specifies the point in an API call that a callback is issued. + * + * Specifies the point in an API call that a callback is issued. This + * value is communicated to the callback function by @ref + * msptiCallbackData::callbackSite. + */ + typedef enum + { + /** + * The callback is at the entry of the API call. + */ + MSPTI_API_ENTER = 0, + /** + * The callback is at the exit of the API call. + */ + MSPTI_API_EXIT = 1, + MSPTI_API_CBSITE_FORCE_INT = 0x7fffffff + } msptiApiCallbackSite; + + typedef struct + { + /** + * Point in the runtime or driver function from where the callback + * was issued. + */ + msptiApiCallbackSite callbackSite; + + /** + * Name of the runtime or driver API function which issued the + * callback. + */ + const char *functionName; + + /** + * Params of the runtime or driver API function which issued the + * callback. + */ + const void *functionParams; + + /** + * Pointer to the return value of the runtime or driver API + * call. + */ + const void *functionReturnValue; + + /** + * Name of the symbol operated on by the runtime or driver API + * function which issued the callback. This entry is valid only for + * driver and runtime launch callbacks, where it returns the name of + * the kernel. + */ + const char *symbolName; + + /** + * The activity record correlation ID for this callback. For a + * driver domain callback (i.e. @p domain + * MSPTI_CB_DOMAIN_DRIVER_API) this ID will equal the correlation ID + * in the MSPTI_ActivityAPI record corresponding to the CANN driver + * function call. For a runtime domain callback (i.e. @p domain + * MSPTI_CB_DOMAIN_RUNTIME_API) this ID will equal the correlation + * ID in the MSPTI_ActivityAPI record corresponding to the CANN + * runtime function call. Within the callback, this ID can be + * recorded to correlate user data with the activity record. + */ + uint64_t correlationId; + + /** + * Undefined. Reserved for internal use. + */ + uint64_t reserved1; + + /** + * Undefined. Reserved for internal use. + */ + uint64_t reserved2; + + /** + * Pointer to data shared between the entry and exit callbacks of + * a given runtime or drive API function invocation. This field + * can be used to pass 64-bit values from the entry callback to + * the corresponding exit callback. + */ + uint64_t *correlationData; + } msptiCallbackData; + + /** + * @brief Function type for a callback. + * + * Function type for a callback. The type of the data passed to the + * callback in @p cbdata depends on the @p domain. If @p domain is + * MSPTI_CB_DOMAIN_RUNTIME the type + * of @p cbdata will be msptiCallbackData. + * + * @param userdata User data supplied at subscription of the callback + * @param domain The domain of the callback + * @param cbid The ID of the callback + * @param cbdata Data passed to the callback. + */ + typedef void (*msptiCallbackFunc)(void *userdata, + msptiCallbackDomain domain, + msptiCallbackId cbid, + const msptiCallbackData *cbdata); + + struct msptiSubscriber_st; + + /** + * @brief A callback subscriber. + */ + typedef struct msptiSubscriber_st *msptiSubscriberHandle; + + /** + * @brief Initialize a callback subscriber with a callback function + * and user data. + * + * Initializes a callback subscriber with a callback function and + * (optionally) a pointer to user data. The returned subscriber handle + * can be used to enable and disable the callback for specific domains + * and callback IDs. + * @note Only a single subscriber can be registered at a time. To ensure + * that no other MSPTI client interrupts the profiling session, it's the + * responsibility of all the MSPTI clients to call this function before + * starting the profling session. + * @note This function does not enable any callbacks. + * @note @b Thread-safety: this function is thread safe. + * + * @param subscriber handle to initialize subscriber + * @param callback The callback function + * @param userdata A pointer to user data. This data will be passed to + * the callback function via the @p userdata paramater. + * + * @retval MSPTI_SUCCESS on success + * @retval MSPTI_ERROR_INNER if unable to initialize MSPTI + * @retval MSPTI_ERROR_MULTIPLE_SUBSCRIBERS_NOT_SUPPORTED if there is + * already a MSPTI subscriber + * @retval MSPTI_ERROR_INVALID_PARAMETER if @p subscriber is NULL + */ + msptiResult msptiSubscribe(msptiSubscriberHandle *subscriber, + msptiCallbackFunc callback, void *userdata); + + /** + * @brief Unregister a callback subscriber. + * + * Removes a callback subscriber so that no future callbacks will be + * issued to that subscriber. + * + * @param subscriber Handle to the initialize subscriber + * + * @retval MSPTI_SUCCESS on success + * @retval MSPTI_ERROR_INVALID_PARAMETER if @p subscriber is NULL or not + * initialized + */ + msptiResult msptiUnsubscribe(msptiSubscriberHandle subscriber); + + /** + * @brief Enable or disabled callbacks for a specific domain and + * callback ID. + * + * Enable or disabled callbacks for a subscriber for a specific domain + * and callback ID. + * + * @note @b Thread-safety: a subscriber must serialize access to + * msptiEnableCallback, msptiEnableDomain. + * + * @param enable New enable state for the callback. Zero disables the + * callback, non-zero enables the callback. + * @param subscriber Handle to callback subscription + * @param domain The domain of the callback + * @param cbid The ID of the callback + * + * @retval MSPTI_SUCCESS on success + * @retval MSPTI_ERROR_INVALID_PARAMETER if @p subscriber, @p domain or @p + * cbid is invalid. + */ + msptiResult msptiEnableCallback(uint32_t enable, + msptiSubscriberHandle subscriber, + msptiCallbackDomain domain, + msptiCallbackId cbid); + + /** + * @brief Enable or disabled callbacks for a specific domain + * + * Enable or disabled callbacks for a subscriber for a specific domain + * + * @note @b Thread-safety: a subscriber must serialize access to + * msptiEnableCallback, msptiEnableDomain. + * + * @param enable New enable state for the callback. Zero disables the + * callback, non-zero enables the callback. + * @param subscriber Handle to callback subscription + * @param domain The domain of the callback + * + * @retval MSPTI_SUCCESS on success + * @retval MSPTI_ERROR_INVALID_PARAMETER if @p subscriber, @p domain is + * invalid. + */ + msptiResult msptiEnableDomain(uint32_t enable, + msptiSubscriberHandle subscriber, + msptiCallbackDomain domain); + +#if defined(__GNUC__) && defined(MSPTI_LIB) +#pragma GCC visibility pop +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/systrace/thirdparty/aarch64/mspti/include/mspti_cbid.h b/systrace/thirdparty/aarch64/mspti/include/mspti_cbid.h new file mode 100644 index 0000000..540ad39 --- /dev/null +++ b/systrace/thirdparty/aarch64/mspti/include/mspti_cbid.h @@ -0,0 +1,83 @@ +/** + * @file mspti_cbid.h + * + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef MSPTI_CBID_H +#define MSPTI_CBID_H + +/** + * @brief Definitions of indices for Runtime API functions, unique across entire + * API + */ +typedef enum +{ + MSPTI_CBID_RUNTIME_INVALID = 0, + MSPTI_CBID_RUNTIME_DEVICE_SET = 1, + MSPTI_CBID_RUNTIME_DEVICE_RESET = 2, + MSPTI_CBID_RUNTIME_DEVICE_SET_EX = 3, + MSPTI_CBID_RUNTIME_CONTEXT_CREATED_EX = 4, + MSPTI_CBID_RUNTIME_CONTEXT_CREATED = 5, + MSPTI_CBID_RUNTIME_CONTEXT_DESTROY = 6, + MSPTI_CBID_RUNTIME_STREAM_CREATED = 7, + MSPTI_CBID_RUNTIME_STREAM_DESTROY = 8, + MSPTI_CBID_RUNTIME_STREAM_SYNCHRONIZED = 9, + MSPTI_CBID_RUNTIME_LAUNCH = 10, + MSPTI_CBID_RUNTIME_CPU_LAUNCH = 11, + MSPTI_CBID_RUNTIME_AICPU_LAUNCH = 12, + MSPTI_CBID_RUNTIME_AIV_LAUNCH = 13, + MSPTI_CBID_RUNTIME_FFTS_LAUNCH = 14, + MSPTI_CBID_RUNTIME_MALLOC = 15, + MSPTI_CBID_RUNTIME_FREE = 16, + MSPTI_CBID_RUNTIME_MALLOC_HOST = 17, + MSPTI_CBID_RUNTIME_FREE_HOST = 18, + MSPTI_CBID_RUNTIME_MALLOC_CACHED = 19, + MSPTI_CBID_RUNTIME_FLUSH_CACHE = 20, + MSPTI_CBID_RUNTIME_INVALID_CACHE = 21, + MSPTI_CBID_RUNTIME_MEMCPY = 22, + MSPTI_CBID_RUNTIME_MEMCPY_HOST = 23, + MSPTI_CBID_RUNTIME_MEMCPY_ASYNC = 24, + MSPTI_CBID_RUNTIME_MEM_CPY2D = 25, + MSPTI_CBID_RUNTIME_MEM_CPY2D_ASYNC = 26, + MSPTI_CBID_RUNTIME_MEM_SET = 27, + MSPTI_CBID_RUNTIME_MEM_SET_ASYNC = 28, + MSPTI_CBID_RUNTIME_MEM_GET_INFO = 29, + MSPTI_CBID_RUNTIME_RESERVE_MEM_ADDRESS = 30, + MSPTI_CBID_RUNTIME_RELEASE_MEM_ADDRESS = 31, + MSPTI_CBID_RUNTIME_MALLOC_PHYSICAL = 32, + MSPTI_CBID_RUNTIME_FREE_PHYSICAL = 33, + MSPTI_CBID_RUNTIME_MEM_EXPORT_TO_SHAREABLE_HANDLE = 34, + MSPTI_CBID_RUNTIME_MEM_IMPORT_FROM_SHAREABLE_HANDLE = 35, + MSPTI_CBID_RUNTIME_MEM_SET_PID_TO_SHAREABLE_HANDLE = 36, + MSPTI_CBID_RUNTIME_SIZE, + MSPTI_CBID_RUNTIME_FORCE_INT = 0x7fffffff +} msptiCallbackIdRuntime; + +/** + * @brief Definitions of indices for hccl API functions + */ +typedef enum +{ + MSPTI_CBID_HCCL_INVALID = 0, + MSPTI_CBID_HCCL_ALLREDUCE = 1, + MSPTI_CBID_HCCL_BROADCAST = 2, + MSPTI_CBID_HCCL_ALLGATHER = 3, + MSPTI_CBID_HCCL_REDUCE_SCATTER = 4, + MSPTI_CBID_HCCL_REDUCE = 5, + MSPTI_CBID_HCCL_ALL_TO_ALL = 6, + MSPTI_CBID_HCCL_ALL_TO_ALLV = 7, + MSPTI_CBID_HCCL_BARRIER = 8, + MSPTI_CBID_HCCL_SCATTER = 9, + MSPTI_CBID_HCCL_SEND = 10, + MSPTI_CBID_HCCL_RECV = 11, + MSPTI_CBID_HCCL_SENDRECV = 12, + MSPTI_CBID_HCCL_SIZE, + MSPTI_CBID_HCCL_FORCE_INT = 0x7fffffff +} msptiCallbackIdHccl; + +#endif diff --git a/systrace/thirdparty/aarch64/mspti/include/mspti_result.h b/systrace/thirdparty/aarch64/mspti/include/mspti_result.h new file mode 100644 index 0000000..902647e --- /dev/null +++ b/systrace/thirdparty/aarch64/mspti/include/mspti_result.h @@ -0,0 +1,30 @@ +/** + * @file mspti_result.h + * + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef MSPTI_BASE_H +#define MSPTI_BASE_H + +/** + * @brief MSPTI result codes. + * + * Error and result codes returned by MSPTI functions. + */ +typedef enum +{ + MSPTI_SUCCESS = 0, + MSPTI_ERROR_INVALID_PARAMETER = 1, + MSPTI_ERROR_MULTIPLE_SUBSCRIBERS_NOT_SUPPORTED = 2, + MSPTI_ERROR_MAX_LIMIT_REACHED = 3, + MSPTI_ERROR_DEVICE_OFFLINE = 4, + MSPTI_ERROR_INNER = 999, + MSPTI_ERROR_FOECE_INT = 0x7fffffff +} msptiResult; + +#endif diff --git a/systrace/thirdparty/aarch64/mspti/lib64/libmspti.so b/systrace/thirdparty/aarch64/mspti/lib64/libmspti.so new file mode 100644 index 0000000000000000000000000000000000000000..c6bc165910ce21933220f48149ef4f3ba240b8dd GIT binary patch literal 534512 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

O&@;?K%6+0Zp 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

${8t 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ψ^p^s=q82e)=w%-ce0<~CD4_I=$Z~HOgGEVuK~IV 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ΝXoL|IE=R|CO(; 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

BjYglyx6zmUX3pm*w~BJwlN$N$oPM9P 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�V za5LV<*)Bt02se0aE_xT>R=}-Vha0Xp^;OsrquxBY=VH`b1J@UW-b`$C!QGc1d2Zq? zoJu&saWdyjg5xi3#sXaTD`IbB}=fn5$OnwaXCDeLRThj6NMAzmM zNf%^^oJn>WRV5csT(Ha6YL2o=!lS;5)<#L4I|#Q0u77P<)%PmiLE|G?8(ZtD81pA} zBA3M(M09BX^(C=0&|i`n z6eOdDml4*QB~DGqokFg4L%Gtc)~H2p7`gEmDVK@dve-%9k+d`G58@VRpG)|<7mgE- z8oU4Gi^6+JV|ORIswsqn_38^Ty5)DX=@Pn9NZ;SZCW%}ApQ5Lbd}ZZB_}sNxK2xI%wXDWM+AY#m3|dH2^LvsG zspzT2o_ZVk)7>Y!0)3g#v6-yJ1&vdXmAr6H^yer&Ox`M9x1hsPm&%_aoM>ChNUq&b8SR<}JkM zqv$z{o|HJ!>!jMkV3&W3D@RBGSON&d4WMQdC2I+Ts6HT!{)*Z z3`OChThcr0b@W>f`p3!-hw2X~FOpK&rPkJhGiVO-bI6yKUfqVt{-i@~2{)fFCT9li zPZHm!{u$#3WafD$K2d$255MWqAJ+G$FipaB9p>%;@%^K{VryVthdm3jD4=#Q9P-5V zXpWA}tkriWzMmQJxmo)Z2{GfHHrkqy1qBh4Nghv}EG>{6Q?qk+B`j*tp!O*a6ZSm! zlT=ALdVOfvs-jox7otgRq@dbYyLE!9eS5N`Ps8h(4>|hc(q|BvhMQtfpLzJ%H~-<& zN5ai3_ovU1&;G&ENAgA`&aJ!ovhIr~eQs1?&nFa~u03T=k%{CHr`7s>F(pt$b07Em&S0R}Dw%QIiusmdt{4Hj17SILi_ANZnEkr=*_yHiE*U-|3IDBjh}X@ICNH zZjOE2G6sJyd9KdT9~cv^w+2i1&*Fn$1Ai!pulo4GdvfWa?c?{oHxfU%Zi5?WV>&gp zlGvV5+!4QjQo7Jn=AM)S*_S4=)yS4{Z_(KZvPajE6(95>JBDl;^&IJ=!XTj77E|7ta+No_e?K025)<=-ONh*K(kZ)Q?KGJ^K667TP zrQI^~QTUjm7EUVZKVfhJZKbSkINKA8T$|Y=D4WM`Wo2z@QfA%Q@1n+wENb|FLl!lb zeEx`EhIrO~)FWYr#8vH8d3#9oEctMkkSjgT9Wgv36S)ty&i=?u#B$%j`X2EZHguTt zezH2MjCJyT1)O0x(moMRAg-$2_AdyzHs8^u-J-U)26gC9uuf$3nWf*m7d?6J_xF2k zDxNQS9JYwAbmG2b*!_~mVUoVNZ=_tJ#~=U5J`CGmFXf>g`F!r_YN7pP4u9z{3Qj+j zaONusGFEOINN_#L8pIO`9gioxU512fdyPEM_aW=Lg?uJ`XE}gx&Yg;|EYdo=H}xdA zHy5{UEp%^cUh1Y!tyqeFaeG2xM|}At>)X^gW=|$;cUsYf$&&Vj ziVMft0^UJ97)r;tCm8b~jf7EshcTAWUa7(eSr?TJr)I?>ZPpwshP_ zZ)46|O$M=vlkHz^X@y-|-`7HO>Hd&oFtJna?yKkSyLq11$&*)&6_?;=aVOp%(#}Rq zt9diy)O%QKk~)t$ha~&y0qdD3)tp0DpViUvWot>>6V?LP<5uUy%`)5I-x6>a-xRpN zXzl!x^+0>S_2h*EoO_tC^DUqFlAol8;!D0VKHn}32S4+T-4IoM(?x7px_9F_cxi9k zaB2^8SE$sl&$h4ST{?~%?O*Jm;D@h&N}aKQu9~m-`?+6Mx}x@3TWMfC$W$SZpA88 z(H1zZaQuC*koq`yAEa>4!d;AkEAd=_Tk_Sb`eq?}CKKOE`HjGl^ptxf>nP)Hp0(Mw z^~#&|3q7&oL9u2>gaHKIvSE9jY%T z9J#-8DFP=8PU73>_u|h!o=rQYy=~LfsVt>S{GJ0ZPwv#biWhW_k1+#T%TfvV5;_MA zZlHe}I3Fg94jY`yl(0O=H^&f`aL>U_dmy@9bbl?&aC_k<8hz7%UDA$+?QwE;#NDm- zI609ULawfXdnct|a^=(Fr@%N!=BU*XheMs(yV&v`7*{xwOJ&ON;8cN38LEDP4?W`_ z=DthnLhZX7=y4*$PNc)^&Xh5gIbu2BEq2Ep^cG0{l#jj<{tP_GU7b8*PhbwHErp#u z#jfPS3HI_`P>W81nI4(jIZg*J8IcP9Yj*ySPENxgfjFrcIoxrW&$blTrgG|8nLF~D zt#eR+%N-}@0q&T%FvaEo*CsVTnu*#fbW1sv{D6IC{TEnkk?@<~BvST%-{DJI&!c-8 z-K|FWfwW%RBXsrJl9WTaqqTG}P#%Kh)W|BNU6s0_1pX-e#;|Y#d0loz1kE*yZ4Jnz zevNenLq^pNY7D^mTa`NhxlSa_uBas+?htZi$elCff^0L-be2qryuTNfEYqCEGhH25 zyegY=7q8rHJ7VaFJcbFIOBdVs6%b$K^Nl0vVXzok~3OJI(Wg^o>Y2)(?a6c zI^-&`z268|*{o9NEyN-5x%UWZC7oF^;T8^dfrto(`1x zz<896!)+YFOej&O?A`HBrmLsDbTB5>7B7Fn3EPS&bJb-NlkD(P8@$+X*s+N)GWlPI z(axcb^ZPzH^>EJNyFs3b(>i0M_4o5h85%@33;j!m-&H(hkANCd%O**ga930BHW|VM z&*mkI|Cf-T_(p90Z-g&*507DM2L9J>`$R|@s4cbb_#(Cax;u_ZjLwAHnPQ%qHmV6D z(_S^xZ*1hW`-uf&xty5nj{6Yv&C@f+wro)+d$S4N(pQMTusJ1K9=J6$53sF0kOxZ8 zBlk+TKEpl25|_`mNL-?h$JRRwn~$!2Mfj0=cb@0<({SB!ZeTyQ#H#@vOW&l<2ue#S z|ADm)CWSgS$w~9T^oUwlteECP2>-fbHF`?Xv$v74sYbkh{&BGm2wg#ymgaYD6r%F9us68EM``SiglIYP{NVu30zEXlV+gmbEiv4i;UITcQ8 z;M|;?i*vSb?byPpIlGt~XtIobx5zeqi*i9+wN51)XlDX#CugM8>aJguWjiI?S4=

5$>2}jw;6M9ls>FIQExcAw3{a# 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 literal 0 HcmV?d00001 diff --git a/systrace/thirdparty/uthash.h b/systrace/thirdparty/uthash.h new file mode 100644 index 0000000..6d89200 --- /dev/null +++ b/systrace/thirdparty/uthash.h @@ -0,0 +1,1417 @@ +/* +Copyright (c) 2003-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.3.0 + +#include /* ptrdiff_t */ +#include /* exit */ +#include /* memcmp, memset, strlen */ + +#if defined(HASH_NO_STDINT) && HASH_NO_STDINT +/* The user doesn't have , and must figure out their own way + to provide definitions for uint8_t and uint32_t. */ +#else +#include /* uint8_t, uint32_t */ +#endif + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__MCST__) /* Elbrus C Compiler */ +#define DECLTYPE(x) (__typeof(x)) +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || \ + defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE(x) +#define DECLTYPE_ASSIGN(dst, src) \ + do \ + { \ + char **_da_dst = (char **)(&(dst)); \ + *_da_dst = (char *)(src); \ + } while (0) +#else +#define DECLTYPE_ASSIGN(dst, src) \ + do \ + { \ + (dst) = DECLTYPE(dst)(src); \ + } while (0) +#endif + +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr, sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_bzero +#define uthash_bzero(a, n) memset(a, '\0', n) +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif + +#ifndef HASH_FUNCTION +#define HASH_FUNCTION(keyptr, keylen, hashv) HASH_JEN(keyptr, keylen, hashv) +#endif + +#ifndef HASH_KEYCMP +#define HASH_KEYCMP(a, b, n) memcmp(a, b, n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +#ifndef HASH_NONFATAL_OOM +#define HASH_NONFATAL_OOM 0 +#endif + +#if HASH_NONFATAL_OOM +/* malloc failures can be recovered from */ + +#ifndef uthash_nonfatal_oom +#define uthash_nonfatal_oom(obj) \ + do \ + { \ + } while (0) /* non-fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) \ + do \ + { \ + (oomed) = 1; \ + } while (0) +#define IF_HASH_NONFATAL_OOM(x) x + +#else +/* malloc failures result in lost memory, hash tables are unusable */ + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") +#define IF_HASH_NONFATAL_OOM(x) + +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 \ + 5U /* lg2 of initial number of buckets \ + */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl, hhp) ((void *)(((char *)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl, elp) \ + ((UT_hash_handle *)(void *)(((char *)(elp)) + ((tbl)->hho))) + +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ + do \ + { \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ + } while (0) + +#define HASH_VALUE(keyptr, keylen, hashv) \ + do \ + { \ + HASH_FUNCTION(keyptr, keylen, hashv); \ + } while (0) + +#define HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, hashval, out) \ + do \ + { \ + (out) = NULL; \ + if (head) \ + { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval)) \ + { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, \ + (head)->hh.tbl->buckets[_hf_bkt], keyptr, \ + keylen, hashval, out); \ + } \ + } \ + } while (0) + +#define HASH_FIND(hh, head, keyptr, keylen, out) \ + do \ + { \ + (out) = NULL; \ + if (head) \ + { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ + } \ + } while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN \ + (HASH_BLOOM_BITLEN / 8UL) + (((HASH_BLOOM_BITLEN % 8UL) != 0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl, oomed) \ + do \ + { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t *)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!(tbl)->bloom_bv) \ + { \ + HASH_RECORD_OOM(oomed); \ + } \ + else \ + { \ + uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + } \ + } while (0) + +#define HASH_BLOOM_FREE(tbl) \ + do \ + { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + } while (0) + +#define HASH_BLOOM_BITSET(bv, idx) (bv[(idx) / 8U] |= (1U << ((idx) % 8U))) +#define HASH_BLOOM_BITTEST(bv, idx) \ + ((bv[(idx) / 8U] & (1U << ((idx) % 8U))) != 0) + +#define HASH_BLOOM_ADD(tbl, hashv) \ + HASH_BLOOM_BITSET( \ + (tbl)->bloom_bv, \ + ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl, hashv) \ + HASH_BLOOM_BITTEST( \ + (tbl)->bloom_bv, \ + ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl, oomed) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl, hashv) +#define HASH_BLOOM_TEST(tbl, hashv) 1 +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh, head, oomed) \ + do \ + { \ + (head)->hh.tbl = \ + (UT_hash_table *)uthash_malloc(sizeof(UT_hash_table)); \ + if (!(head)->hh.tbl) \ + { \ + HASH_RECORD_OOM(oomed); \ + } \ + else \ + { \ + uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char *)(&(head)->hh) - (char *)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket *)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ + if (!(head)->hh.tbl->buckets) \ + { \ + HASH_RECORD_OOM(oomed); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } \ + else \ + { \ + uthash_bzero((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * \ + sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ + IF_HASH_NONFATAL_OOM(if (oomed) { \ + uthash_free((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * \ + sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + }) \ + } \ + } \ + } while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, \ + hashval, add, replaced, cmpfcn) \ + do \ + { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, \ + hashval, replaced); \ + if (replaced) \ + { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), \ + keylen_in, hashval, add, cmpfcn); \ + } while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, hashval, add, \ + replaced) \ + do \ + { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, \ + hashval, replaced); \ + if (replaced) \ + { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, \ + hashval, add); \ + } while (0) + +#define HASH_REPLACE(hh, head, fieldname, keylen_in, add, replaced) \ + do \ + { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, \ + add, replaced); \ + } while (0) + +#define HASH_REPLACE_INORDER(hh, head, fieldname, keylen_in, add, replaced, \ + cmpfcn) \ + do \ + { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, \ + _hr_hashv, add, replaced, cmpfcn); \ + } while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ + do \ + { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ + } while (0) + +#define HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn) \ + do \ + { \ + do \ + { \ + if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) \ + { \ + break; \ + } \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ + } while (0) + +#ifdef NO_DECLTYPE +#undef HASH_AKBI_INNER_LOOP +#define HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn) \ + do \ + { \ + char *_hs_saved_head = (char *)(head); \ + do \ + { \ + DECLTYPE_ASSIGN(head, _hs_iter); \ + if (cmpfcn(head, add) > 0) \ + { \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + break; \ + } \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ + } while (0) +#endif + +#if HASH_NONFATAL_OOM + +#define HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, oomed) \ + do \ + { \ + if (!(oomed)) \ + { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, \ + oomed); \ + if (oomed) \ + { \ + HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ + HASH_DELETE_HH(hh, head, &(add)->hh); \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } \ + else \ + { \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } \ + } \ + else \ + { \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } \ + } while (0) + +#else + +#define HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, oomed) \ + do \ + { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, \ + oomed); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } while (0) + +#endif + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, \ + hashval, add, cmpfcn) \ + do \ + { \ + IF_HASH_NONFATAL_OOM(int _ha_oomed = 0;) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char *)(keyptr); \ + (add)->hh.keylen = (unsigned)(keylen_in); \ + if (!(head)) \ + { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM(if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( \ + }) \ + } \ + else \ + { \ + void *_hs_iter = (head); \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ + if (_hs_iter) \ + { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) \ + { \ + HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = \ + (add); \ + } \ + else \ + { \ + (head) = (add); \ + } \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ + } \ + else \ + { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, \ + _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ + } while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh, head, keyptr, keylen_in, add, cmpfcn) \ + do \ + { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, \ + _hs_hashv, add, cmpfcn); \ + } while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, hashval, \ + add, cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), \ + keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh, head, fieldname, keylen_in, add, cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, \ + cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, hashval, add) \ + do \ + { \ + IF_HASH_NONFATAL_OOM(int _ha_oomed = 0;) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (const void *)(keyptr); \ + (add)->hh.keylen = (unsigned)(keylen_in); \ + if (!(head)) \ + { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM(if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( \ + }) \ + } \ + else \ + { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, \ + _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ + } while (0) + +#define HASH_ADD_KEYPTR(hh, head, keyptr, keylen_in, add) \ + do \ + { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, \ + add); \ + } while (0) + +#define HASH_ADD_BYHASHVALUE(hh, head, fieldname, keylen_in, hashval, add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, \ + hashval, add) + +#define HASH_ADD(hh, head, fieldname, keylen_in, add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv, num_bkts, bkt) \ + do \ + { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ + } while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh, head, delptr) HASH_DELETE_HH(hh, head, &(delptr)->hh) + +#define HASH_DELETE_HH(hh, head, delptrhh) \ + do \ + { \ + const struct UT_hash_handle *_hd_hh_del = (delptrhh); \ + if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) \ + { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * \ + sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } \ + else \ + { \ + unsigned _hd_bkt; \ + if (_hd_hh_del == (head)->hh.tbl->tail) \ + { \ + (head)->hh.tbl->tail = \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ + } \ + if (_hd_hh_del->prev != NULL) \ + { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = \ + _hd_hh_del->next; \ + } \ + else \ + { \ + DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ + } \ + if (_hd_hh_del->next != NULL) \ + { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, \ + _hd_bkt); \ + HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ + } while (0) + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head, findstr, out) \ + do \ + { \ + unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ + HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ + } while (0) +#define HASH_ADD_STR(head, strfield, add) \ + do \ + { \ + unsigned _uthash_hastr_keylen = \ + (unsigned)uthash_strlen((add)->strfield); \ + HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ + } while (0) +#define HASH_REPLACE_STR(head, strfield, add, replaced) \ + do \ + { \ + unsigned _uthash_hrstr_keylen = \ + (unsigned)uthash_strlen((add)->strfield); \ + HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, \ + replaced); \ + } while (0) +#define HASH_FIND_INT(head, findint, out) \ + HASH_FIND(hh, head, findint, sizeof(int), out) +#define HASH_ADD_INT(head, intfield, add) \ + HASH_ADD(hh, head, intfield, sizeof(int), add) +#define HASH_REPLACE_INT(head, intfield, add, replaced) \ + HASH_REPLACE(hh, head, intfield, sizeof(int), add, replaced) +#define HASH_FIND_PTR(head, findptr, out) \ + HASH_FIND(hh, head, findptr, sizeof(void *), out) +#define HASH_ADD_PTR(head, ptrfield, add) \ + HASH_ADD(hh, head, ptrfield, sizeof(void *), add) +#define HASH_REPLACE_PTR(head, ptrfield, add, replaced) \ + HASH_REPLACE(hh, head, ptrfield, sizeof(void *), add, replaced) +#define HASH_DEL(head, delptr) HASH_DELETE(hh, head, delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is + * defined. This is for uthash developer only; it compiles away if HASH_DEBUG + * isn't defined. + */ +#ifdef HASH_DEBUG +#include /* fprintf, stderr */ +#define HASH_OOPS(...) \ + do \ + { \ + fprintf(stderr, __VA_ARGS__); \ + exit(-1); \ + } while (0) +#define HASH_FSCK(hh, head, where) \ + do \ + { \ + struct UT_hash_handle *_thh; \ + if (head) \ + { \ + unsigned _bkt_i; \ + unsigned _count = 0; \ + char *_prev; \ + for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) \ + { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) \ + { \ + if (_prev != (char *)(_thh->hh_prev)) \ + { \ + HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ + (where), (void *)_thh->hh_prev, \ + (void *)_prev); \ + } \ + _bkt_count++; \ + _prev = (char *)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) \ + { \ + HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ + (where), (head)->hh.tbl->buckets[_bkt_i].count, \ + _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) \ + { \ + HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) \ + { \ + _count++; \ + if (_prev != (char *)_thh->prev) \ + { \ + HASH_OOPS("%s: invalid prev %p, actual %p\n", (where), \ + (void *)_thh->prev, (void *)_prev); \ + } \ + _prev = (char *)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) \ + : NULL); \ + } \ + if (_count != (head)->hh.tbl->num_items) \ + { \ + HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + } \ + } while (0) +#else +#define HASH_FSCK(hh, head, where) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh, head, keyptr, fieldlen) \ + do \ + { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ + } while (0) +#else +#define HASH_EMIT_KEY(hh, head, keyptr, fieldlen) +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. + */ +#define HASH_BER(key, keylen, hashv) \ + do \ + { \ + unsigned _hb_keylen = (unsigned)keylen; \ + const unsigned char *_hb_key = (const unsigned char *)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) \ + { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ + } while (0) + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx + * (archive link: https://archive.is/Ivcan ) + */ +#define HASH_SAX(key, keylen, hashv) \ + do \ + { \ + unsigned _sx_i; \ + const unsigned char *_hs_key = (const unsigned char *)(key); \ + hashv = 0; \ + for (_sx_i = 0; _sx_i < keylen; _sx_i++) \ + { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ + } while (0) +/* FNV-1a variation */ +#define HASH_FNV(key, keylen, hashv) \ + do \ + { \ + unsigned _fn_i; \ + const unsigned char *_hf_key = (const unsigned char *)(key); \ + (hashv) = 2166136261U; \ + for (_fn_i = 0; _fn_i < keylen; _fn_i++) \ + { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ + } while (0) + +#define HASH_OAT(key, keylen, hashv) \ + do \ + { \ + unsigned _ho_i; \ + const unsigned char *_ho_key = (const unsigned char *)(key); \ + hashv = 0; \ + for (_ho_i = 0; _ho_i < keylen; _ho_i++) \ + { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ + } while (0) + +#define HASH_JEN_MIX(a, b, c) \ + do \ + { \ + a -= b; \ + a -= c; \ + a ^= (c >> 13); \ + b -= c; \ + b -= a; \ + b ^= (a << 8); \ + c -= a; \ + c -= b; \ + c ^= (b >> 13); \ + a -= b; \ + a -= c; \ + a ^= (c >> 12); \ + b -= c; \ + b -= a; \ + b ^= (a << 16); \ + c -= a; \ + c -= b; \ + c ^= (b >> 5); \ + a -= b; \ + a -= c; \ + a ^= (c >> 3); \ + b -= c; \ + b -= a; \ + b ^= (a << 10); \ + c -= a; \ + c -= b; \ + c ^= (b >> 15); \ + } while (0) + +#define HASH_JEN(key, keylen, hashv) \ + do \ + { \ + unsigned _hj_i, _hj_j, _hj_k; \ + unsigned const char *_hj_key = (unsigned const char *)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) \ + { \ + _hj_i += \ + (_hj_key[0] + ((unsigned)_hj_key[1] << 8) + \ + ((unsigned)_hj_key[2] << 16) + ((unsigned)_hj_key[3] << 24)); \ + _hj_j += \ + (_hj_key[4] + ((unsigned)_hj_key[5] << 8) + \ + ((unsigned)_hj_key[6] << 16) + ((unsigned)_hj_key[7] << 24)); \ + hashv += (_hj_key[8] + ((unsigned)_hj_key[9] << 8) + \ + ((unsigned)_hj_key[10] << 16) + \ + ((unsigned)_hj_key[11] << 24)); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch (_hj_k) \ + { \ + case 11: \ + hashv += ((unsigned)_hj_key[10] << 24); /* FALLTHROUGH */ \ + case 10: \ + hashv += ((unsigned)_hj_key[9] << 16); /* FALLTHROUGH */ \ + case 9: \ + hashv += ((unsigned)_hj_key[8] << 8); /* FALLTHROUGH */ \ + case 8: \ + _hj_j += ((unsigned)_hj_key[7] << 24); /* FALLTHROUGH */ \ + case 7: \ + _hj_j += ((unsigned)_hj_key[6] << 16); /* FALLTHROUGH */ \ + case 6: \ + _hj_j += ((unsigned)_hj_key[5] << 8); /* FALLTHROUGH */ \ + case 5: \ + _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: \ + _hj_i += ((unsigned)_hj_key[3] << 24); /* FALLTHROUGH */ \ + case 3: \ + _hj_i += ((unsigned)_hj_key[2] << 16); /* FALLTHROUGH */ \ + case 2: \ + _hj_i += ((unsigned)_hj_key[1] << 8); /* FALLTHROUGH */ \ + case 1: \ + _hj_i += _hj_key[0]; /* FALLTHROUGH */ \ + default:; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + } while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) || \ + defined(_MSC_VER) || defined(__BORLANDC__) || defined(__TURBOC__) +#define get16bits(d) (*((const uint16_t *)(d))) +#endif + +#if !defined(get16bits) +#define get16bits(d) \ + ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) + \ + (uint32_t)(((const uint8_t *)(d))[0])) +#endif +#define HASH_SFH(key, keylen, hashv) \ + do \ + { \ + unsigned const char *_sfh_key = (unsigned const char *)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (; _sfh_len > 0U; _sfh_len--) \ + { \ + hashv += get16bits(_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits(_sfh_key + 2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U * sizeof(uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) \ + { \ + case 3: \ + hashv += get16bits(_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof(uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: \ + hashv += get16bits(_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: \ + hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + break; \ + default:; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ + } while (0) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl, hh, head, keyptr, keylen_in, hashval, out) \ + do \ + { \ + if ((head).hh_head != NULL) \ + { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } \ + else \ + { \ + (out) = NULL; \ + } \ + while ((out) != NULL) \ + { \ + if ((out)->hh.hashv == (hashval) && \ + (out)->hh.keylen == (keylen_in)) \ + { \ + if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) \ + { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) \ + { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } \ + else \ + { \ + (out) = NULL; \ + } \ + } \ + } while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head, hh, addhh, oomed) \ + do \ + { \ + UT_hash_bucket *_ha_head = &(head); \ + _ha_head->count++; \ + (addhh)->hh_next = _ha_head->hh_head; \ + (addhh)->hh_prev = NULL; \ + if (_ha_head->hh_head != NULL) \ + { \ + _ha_head->hh_head->hh_prev = (addhh); \ + } \ + _ha_head->hh_head = (addhh); \ + if ((_ha_head->count >= \ + ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) && \ + !(addhh)->tbl->noexpand) \ + { \ + HASH_EXPAND_BUCKETS(addhh, (addhh)->tbl, oomed); \ + IF_HASH_NONFATAL_OOM(if (oomed) { HASH_DEL_IN_BKT(head, addhh); }) \ + } \ + } while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(head, delhh) \ + do \ + { \ + UT_hash_bucket *_hd_head = &(head); \ + _hd_head->count--; \ + if (_hd_head->hh_head == (delhh)) \ + { \ + _hd_head->hh_head = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_prev) \ + { \ + (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_next) \ + { \ + (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ + } \ + } while (0) + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(hh, tbl, oomed) \ + do \ + { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket *)uthash_malloc( \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + if (!_he_new_buckets) \ + { \ + HASH_RECORD_OOM(oomed); \ + } \ + else \ + { \ + uthash_bzero(_he_new_buckets, sizeof(struct UT_hash_bucket) * \ + (tbl)->num_buckets * 2U); \ + (tbl)->ideal_chain_maxlen = \ + ((tbl)->num_items >> ((tbl)->log2_num_buckets + 1U)) + \ + ((((tbl)->num_items & (((tbl)->num_buckets * 2U) - 1U)) != 0U) \ + ? 1U \ + : 0U); \ + (tbl)->nonideal_items = 0; \ + for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = (tbl)->buckets[_he_bkt_i].hh_head; \ + while (_he_thh != NULL) \ + { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, \ + _he_bkt); \ + _he_newbkt = &(_he_new_buckets[_he_bkt]); \ + if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) \ + { \ + (tbl)->nonideal_items++; \ + if (_he_newbkt->count > _he_newbkt->expand_mult * \ + (tbl)->ideal_chain_maxlen) \ + { \ + _he_newbkt->expand_mult++; \ + } \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) \ + { \ + _he_newbkt->hh_head->hh_prev = _he_thh; \ + } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free((tbl)->buckets, \ + (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->num_buckets *= 2U; \ + (tbl)->log2_num_buckets++; \ + (tbl)->buckets = _he_new_buckets; \ + (tbl)->ineff_expands = \ + ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) \ + ? ((tbl)->ineff_expands + 1U) \ + : 0U; \ + if ((tbl)->ineff_expands > 1U) \ + { \ + (tbl)->noexpand = 1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ + } \ + } while (0) + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head, cmpfcn) HASH_SRT(hh, head, cmpfcn) +#define HASH_SRT(hh, head, cmpfcn) \ + do \ + { \ + unsigned _hs_i; \ + unsigned _hs_looping, _hs_nmerges, _hs_insize, _hs_psize, _hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) \ + { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) \ + { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) \ + { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) \ + { \ + _hs_psize++; \ + _hs_q = \ + ((_hs_q->next != NULL) \ + ? HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) \ + : NULL); \ + if (_hs_q == NULL) \ + { \ + break; \ + } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize != 0U) || \ + ((_hs_qsize != 0U) && (_hs_q != NULL))) \ + { \ + if (_hs_psize == 0U) \ + { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) \ + ? HH_FROM_ELMT((head)->hh.tbl, \ + _hs_q->next) \ + : NULL); \ + _hs_qsize--; \ + } \ + else if ((_hs_qsize == 0U) || (_hs_q == NULL)) \ + { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) \ + { \ + _hs_p = ((_hs_p->next != NULL) \ + ? HH_FROM_ELMT((head)->hh.tbl, \ + _hs_p->next) \ + : NULL); \ + } \ + _hs_psize--; \ + } \ + else if ((cmpfcn(DECLTYPE(head)(ELMT_FROM_HH( \ + (head)->hh.tbl, _hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH( \ + (head)->hh.tbl, _hs_q)))) <= 0) \ + { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) \ + { \ + _hs_p = ((_hs_p->next != NULL) \ + ? HH_FROM_ELMT((head)->hh.tbl, \ + _hs_p->next) \ + : NULL); \ + } \ + _hs_psize--; \ + } \ + else \ + { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) \ + ? HH_FROM_ELMT((head)->hh.tbl, \ + _hs_q->next) \ + : NULL); \ + _hs_qsize--; \ + } \ + if (_hs_tail != NULL) \ + { \ + _hs_tail->next = \ + ((_hs_e != NULL) \ + ? ELMT_FROM_HH((head)->hh.tbl, _hs_e) \ + : NULL); \ + } \ + else \ + { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) \ + { \ + _hs_e->prev = \ + ((_hs_tail != NULL) \ + ? ELMT_FROM_HH((head)->hh.tbl, _hs_tail) \ + : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL) \ + { \ + _hs_tail->next = NULL; \ + } \ + if (_hs_nmerges <= 1U) \ + { \ + _hs_looping = 0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head, \ + ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh, head, "HASH_SRT"); \ + } \ + } while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ + do \ + { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt = NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh = NULL; \ + ptrdiff_t _dst_hho = ((char *)(&(dst)->hh_dst) - (char *)(dst)); \ + if ((src) != NULL) \ + { \ + for (_src_bkt = 0; _src_bkt < (src)->hh_src.tbl->num_buckets; \ + _src_bkt++) \ + { \ + for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; _src_hh = _src_hh->hh_next) \ + { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) \ + { \ + IF_HASH_NONFATAL_OOM(int _hs_oomed = 0;) \ + _dst_hh = (UT_hash_handle *)(void *)(((char *)_elt) + \ + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) \ + { \ + _last_elt_hh->next = _elt; \ + } \ + if ((dst) == NULL) \ + { \ + DECLTYPE_ASSIGN(dst, _elt); \ + HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ + IF_HASH_NONFATAL_OOM(if (_hs_oomed) { \ + uthash_nonfatal_oom(_elt); \ + (dst) = NULL; \ + continue; \ + }) \ + } \ + else \ + { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, \ + _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], \ + hh_dst, _dst_hh, _hs_oomed); \ + (dst)->hh_dst.tbl->num_items++; \ + IF_HASH_NONFATAL_OOM(if (_hs_oomed) { \ + HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ + HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ + _dst_hh->tbl = NULL; \ + uthash_nonfatal_oom(_elt); \ + continue; \ + }) \ + HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ + } while (0) + +#define HASH_CLEAR(hh, head) \ + do \ + { \ + if ((head) != NULL) \ + { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * \ + sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } \ + } while (0) + +#define HASH_OVERHEAD(hh, head) \ + (((head) != NULL) \ + ? ((size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + (HASH_BLOOM_BYTELEN))) \ + : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh, head, el, tmp) \ + for (((el) = (head)), \ + ((*(char **)(&(tmp))) = \ + (char *)((head != NULL) ? (head)->hh.next : NULL)); \ + (el) != NULL; ((el) = (tmp)), \ + ((*(char **)(&(tmp))) = \ + (char *)((tmp != NULL) ? (tmp)->hh.next : NULL))) +#else +#define HASH_ITER(hh, head, el, tmp) \ + for (((el) = (head)), \ + ((tmp) = DECLTYPE(el)((head != NULL) ? (head)->hh.next : NULL)); \ + (el) != NULL; \ + ((el) = (tmp)), \ + ((tmp) = DECLTYPE(el)((tmp != NULL) ? (tmp)->hh.next : NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh, head) +#define HASH_CNT(hh, head) ((head != NULL) ? ((head)->hh.tbl->num_items) : 0U) + +typedef struct UT_hash_bucket +{ + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table +{ + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps + */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t + bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle +{ + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + const void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ \ No newline at end of file diff --git a/systrace/thirdparty/x86_64/libunwind/libunwind-common.h b/systrace/thirdparty/x86_64/libunwind/libunwind-common.h new file mode 100644 index 0000000..9c0db22 --- /dev/null +++ b/systrace/thirdparty/x86_64/libunwind/libunwind-common.h @@ -0,0 +1,335 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2004 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#define UNW_VERSION_MAJOR 1 +#define UNW_VERSION_MINOR 9 +#define UNW_VERSION_EXTRA -pre + +#define UNW_VERSION_CODE(maj, min) (((maj) << 16) | (min)) +#define UNW_VERSION UNW_VERSION_CODE(UNW_VERSION_MAJOR, UNW_VERSION_MINOR) + +#ifdef __sun +// On SmartOS, gcc fails with the following error: +// +// ../include/libunwind-common.h:43:41: error: expected identifier or '(' before +// numeric constant # define UNW_PREFIX UNW_PASTE(UNW_PASTE(_U,UNW_TARGET),_) +// ^ +// +// workaround is to undefine _U explicitly. +// see https://github.com/libunwind/libunwind/issues/118 for more details. +// +#undef _U +#endif + +#define UNW_PASTE2(x, y) x##y +#define UNW_PASTE(x, y) UNW_PASTE2(x, y) +#define UNW_OBJ(fn) UNW_PASTE(UNW_PREFIX, fn) +#define UNW_ARCH_OBJ(fn) UNW_PASTE(UNW_PASTE(UNW_PASTE(_U, UNW_TARGET), _), fn) + +#ifdef UNW_LOCAL_ONLY +#define UNW_PREFIX UNW_PASTE(UNW_PASTE(_UL, UNW_TARGET), _) +#else /* !UNW_LOCAL_ONLY */ +#define UNW_PREFIX UNW_PASTE(UNW_PASTE(_U, UNW_TARGET), _) +#endif /* !UNW_LOCAL_ONLY */ + +/* Error codes. The unwind routines return the *negated* values of + these error codes on error and a non-negative value on success. */ +typedef enum +{ + UNW_ESUCCESS = 0, /* no error */ + UNW_EUNSPEC, /* unspecified (general) error */ + UNW_ENOMEM, /* out of memory */ + UNW_EBADREG, /* bad register number */ + UNW_EREADONLYREG, /* attempt to write read-only register */ + UNW_ESTOPUNWIND, /* stop unwinding */ + UNW_EINVALIDIP, /* invalid IP */ + UNW_EBADFRAME, /* bad frame */ + UNW_EINVAL, /* unsupported operation or bad value */ + UNW_EBADVERSION, /* unwind info has unsupported version */ + UNW_ENOINFO /* no unwind info found */ +} unw_error_t; + +/* The following enum defines the indices for a couple of + (pseudo-)registers which have the same meaning across all + platforms. (RO) means read-only. (RW) means read-write. General + registers (aka "integer registers") are expected to start with + index 0. The number of such registers is architecture-dependent. + The remaining indices can be used as an architecture sees fit. The + last valid register index is given by UNW_REG_LAST. */ +typedef enum +{ + UNW_REG_IP = UNW_TDEP_IP, /* (rw) instruction pointer (pc) */ + UNW_REG_SP = UNW_TDEP_SP, /* (ro) stack pointer */ + UNW_REG_EH = UNW_TDEP_EH, /* (rw) exception-handling reg base */ + UNW_REG_LAST = UNW_TDEP_LAST_REG +} unw_frame_regnum_t; + +/* Number of exception-handler argument registers: */ +#define UNW_NUM_EH_REGS UNW_TDEP_NUM_EH_REGS + +typedef enum +{ + UNW_CACHE_NONE, /* no caching */ + UNW_CACHE_GLOBAL, /* shared global cache */ + UNW_CACHE_PER_THREAD /* per-thread caching */ +} unw_caching_policy_t; + +typedef enum +{ + UNW_INIT_SIGNAL_FRAME = 1 /* We know this is a signal frame */ +} unw_init_local2_flags_t; + +typedef int unw_regnum_t; + +/* The unwind cursor starts at the youngest (most deeply nested) frame + and is used to track the frame state as the unwinder steps from + frame to frame. It is safe to make (shallow) copies of variables + of this type. */ +typedef struct unw_cursor +{ + unw_word_t opaque[UNW_TDEP_CURSOR_LEN]; +} unw_cursor_t; + +/* This type encapsulates the entire (preserved) machine-state. */ +typedef unw_tdep_context_t unw_context_t; + +/* unw_getcontext() fills the unw_context_t pointed to by UC with the + machine state as it exists at the call-site. For implementation + reasons, this needs to be a target-dependent macro. It's easiest + to think of unw_getcontext() as being identical to getcontext(). */ +#define unw_getcontext(uc) unw_tdep_getcontext(uc) + +/* Return 1 if register number R is a floating-point register, zero + otherwise. + This routine is signal-safe. */ +#define unw_is_fpreg(r) unw_tdep_is_fpreg(r) + +typedef unw_tdep_fpreg_t unw_fpreg_t; + +typedef struct unw_addr_space *unw_addr_space_t; + +/* Each target may define it's own set of flags, but bits 0-15 are + reserved for general libunwind-use. */ +#define UNW_PI_FLAG_FIRST_TDEP_BIT 16 +/* The information comes from a .debug_frame section. */ +#define UNW_PI_FLAG_DEBUG_FRAME 32 + +typedef struct unw_proc_info +{ + unw_word_t start_ip; /* first IP covered by this procedure */ + unw_word_t end_ip; /* first IP NOT covered by this procedure */ +#if defined(NEED_LAST_IP) + unw_word_t last_ip; /* first IP that could begin another procedure */ +#endif + unw_word_t lsda; /* address of lang.-spec. data area (if any) */ + unw_word_t handler; /* optional personality routine */ + unw_word_t gp; /* global-pointer value for this procedure */ + unw_word_t flags; /* misc. flags */ + + int format; /* unwind-info format (arch-specific) */ + int unwind_info_size; /* size of the information (if applicable) */ + void *unwind_info; /* unwind-info (arch-specific) */ + unw_tdep_proc_info_t extra; /* target-dependent auxiliary proc-info */ +} unw_proc_info_t; + +typedef int (*unw_reg_states_callback)(void *token, void *reg_states_data, + size_t reg_states_data_size, + unw_word_t start_ip, unw_word_t end_ip); + +/* These are backend callback routines that provide access to the + state of a "remote" process. This can be used, for example, to + unwind another process through the ptrace() interface. */ +typedef struct unw_accessors +{ + /* Look up the unwind info associated with instruction-pointer IP. + On success, the routine fills in the PROC_INFO structure. */ + int (*find_proc_info)(unw_addr_space_t, unw_word_t, unw_proc_info_t *, int, + void *); + + /* Release any resources (e.g., memory) that were allocated for + the unwind info returned in by a previous call to + find_proc_info() with NEED_UNWIND_INFO set to 1. */ + void (*put_unwind_info)(unw_addr_space_t, unw_proc_info_t *, void *); + + /* Return the list-head of the dynamically registered unwind + info. */ + int (*get_dyn_info_list_addr)(unw_addr_space_t, unw_word_t *, void *); + + /* Access aligned word at address ADDR. The value is returned + according to the endianness of the host (e.g., if the host is + little-endian and the target is big-endian, access_mem() needs + to byte-swap the value before returning it). */ + int (*access_mem)(unw_addr_space_t, unw_word_t, unw_word_t *, int, void *); + + /* Access register number REG at address ADDR. */ + int (*access_reg)(unw_addr_space_t, unw_regnum_t, unw_word_t *, int, + void *); + + /* Access register number REG at address ADDR. */ + int (*access_fpreg)(unw_addr_space_t, unw_regnum_t, unw_fpreg_t *, int, + void *); + + int (*resume)(unw_addr_space_t, unw_cursor_t *, void *); + + /* Optional call back to obtain the name of a (static) procedure. + Dynamically generated procedures are handled automatically by + libunwind. This callback is optional and may be set to + NULL. */ + int (*get_proc_name)(unw_addr_space_t, unw_word_t, char *, size_t, + unw_word_t *, void *); + + /* Optional call back to obtain the name of a elf file where the ip belongs + to. This callback is optional and may be set to NULL. */ + int (*get_elf_filename)(unw_addr_space_t, unw_word_t, char *, size_t, + unw_word_t *, void *); + + /* Optional call back to obtain the start and end ip of a procedure. + * procedure ip range is [start, end), the range is without end. + * This callback is optional and may be set to NULL. + */ + int (*get_proc_ip_range)(unw_addr_space_t, unw_word_t, unw_word_t *, + unw_word_t *, void *); + + /* Optional call back to return a mask to be used with pointer + * authentication on arm64. + * + * The on bits in the returned mask indicate which bits in a return address + * are part of a pointer authentication code. These are the bits in the + * return address to turn off so that the calling frame can be found + * for the unwinding to continue. + * + * The return value must be host-endian. e.g. if the target is big-endian + * and the host is little endian, the implementation of this function + * must byte swap. + * + * This callback is optional and may be set to NULL. In this case all + * the bits in the return address are used, as if no masking were done. + */ + unw_word_t (*ptrauth_insn_mask)(unw_addr_space_t, void *); + +} unw_accessors_t; + +typedef enum unw_save_loc_type +{ + UNW_SLT_NONE, /* register is not saved ("not an l-value") */ + UNW_SLT_MEMORY, /* register has been saved in memory */ + UNW_SLT_REG /* register has been saved in (another) register */ +} unw_save_loc_type_t; + +typedef struct unw_save_loc +{ + unw_save_loc_type_t type; + union + { + unw_word_t addr; /* valid if type==UNW_SLT_MEMORY */ + unw_regnum_t regnum; /* valid if type==UNW_SLT_REG */ + } u; + unw_tdep_save_loc_t extra; /* target-dependent additional information */ +} unw_save_loc_t; + +struct dl_phdr_info; +typedef int (*unw_iterate_phdr_callback_t)(struct dl_phdr_info *, size_t, + void *); +typedef int (*unw_iterate_phdr_func_t)(unw_iterate_phdr_callback_t, void *); + +/* These routines work both for local and remote unwinding. */ + +#define unw_local_addr_space UNW_OBJ(local_addr_space) +#define unw_create_addr_space UNW_OBJ(create_addr_space) +#define unw_destroy_addr_space UNW_OBJ(destroy_addr_space) +#define unw_get_accessors UNW_ARCH_OBJ(get_accessors) +#define unw_get_accessors_int UNW_ARCH_OBJ(get_accessors_int) +#define unw_init_local UNW_OBJ(init_local) +#define unw_init_local2 UNW_OBJ(init_local2) +#define unw_init_remote UNW_OBJ(init_remote) +#define unw_step UNW_OBJ(step) +#define unw_resume UNW_OBJ(resume) +#define unw_get_proc_info UNW_OBJ(get_proc_info) +#define unw_get_proc_info_by_ip UNW_OBJ(get_proc_info_by_ip) +#define unw_get_proc_info_in_range UNW_OBJ(get_proc_info_in_range) +#define unw_reg_states_iterate UNW_OBJ(reg_states_iterate) +#define unw_apply_reg_state UNW_OBJ(apply_reg_state) +#define unw_get_reg UNW_OBJ(get_reg) +#define unw_set_reg UNW_OBJ(set_reg) +#define unw_get_fpreg UNW_OBJ(get_fpreg) +#define unw_set_fpreg UNW_OBJ(set_fpreg) +#define unw_get_save_loc UNW_OBJ(get_save_loc) +#define unw_is_signal_frame UNW_OBJ(is_signal_frame) +#define unw_is_plt_entry UNW_OBJ(is_plt_entry) +#define unw_get_proc_name UNW_OBJ(get_proc_name) +#define unw_get_proc_name_by_ip UNW_OBJ(get_proc_name_by_ip) +#define unw_get_elf_filename UNW_OBJ(get_elf_filename) +#define unw_get_elf_filename_by_ip UNW_OBJ(get_elf_filename_by_ip) +#define unw_set_caching_policy UNW_OBJ(set_caching_policy) +#define unw_set_cache_size UNW_OBJ(set_cache_size) +#define unw_set_iterate_phdr_function UNW_OBJ(set_iterate_phdr_function) +#define unw_regname UNW_ARCH_OBJ(regname) +#define unw_flush_cache UNW_ARCH_OBJ(flush_cache) +#define unw_strerror UNW_ARCH_OBJ(strerror) + +extern unw_addr_space_t unw_create_addr_space(unw_accessors_t *, int); +extern void unw_destroy_addr_space(unw_addr_space_t); +extern unw_accessors_t *unw_get_accessors(unw_addr_space_t); +extern unw_accessors_t *unw_get_accessors_int(unw_addr_space_t); +extern void unw_flush_cache(unw_addr_space_t, unw_word_t, unw_word_t); +extern int unw_set_caching_policy(unw_addr_space_t, unw_caching_policy_t); +extern int unw_set_cache_size(unw_addr_space_t, size_t, int); +extern void unw_set_iterate_phdr_function(unw_addr_space_t, + unw_iterate_phdr_func_t); +extern const char *unw_regname(unw_regnum_t); + +extern int unw_init_local(unw_cursor_t *, unw_context_t *); +extern int unw_init_local2(unw_cursor_t *, unw_context_t *, int); +extern int unw_init_remote(unw_cursor_t *, unw_addr_space_t, void *); +extern int unw_step(unw_cursor_t *); +extern int unw_resume(unw_cursor_t *); +extern int unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *); +extern int unw_get_proc_info_by_ip(unw_addr_space_t, unw_word_t, + unw_proc_info_t *, void *); +extern int unw_get_proc_info_in_range(unw_word_t, unw_word_t, unw_word_t, + unw_word_t, unw_word_t, unw_word_t, + unw_addr_space_t, unw_word_t, + unw_proc_info_t *, int, void *); +extern int unw_reg_states_iterate(unw_cursor_t *, unw_reg_states_callback, + void *); +extern int unw_apply_reg_state(unw_cursor_t *, void *); +extern int unw_get_reg(unw_cursor_t *, int, unw_word_t *); +extern int unw_set_reg(unw_cursor_t *, int, unw_word_t); +extern int unw_get_fpreg(unw_cursor_t *, int, unw_fpreg_t *); +extern int unw_set_fpreg(unw_cursor_t *, int, unw_fpreg_t); +extern int unw_get_save_loc(unw_cursor_t *, int, unw_save_loc_t *); +extern int unw_is_signal_frame(unw_cursor_t *); +extern int unw_is_plt_entry(unw_cursor_t *); +extern int unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *); +extern int unw_get_proc_name_by_ip(unw_addr_space_t, unw_word_t, char *, size_t, + unw_word_t *, void *); +extern int unw_get_elf_filename(unw_cursor_t *, char *, size_t, unw_word_t *); +extern int unw_get_elf_filename_by_ip(unw_addr_space_t, unw_word_t, char *, + size_t, unw_word_t *, void *); +extern const char *unw_strerror(int); +extern int unw_backtrace(void **, int); +extern int unw_backtrace2(void **, int, unw_context_t *, int); + +extern unw_addr_space_t unw_local_addr_space; diff --git a/systrace/thirdparty/x86_64/libunwind/libunwind-dynamic.h b/systrace/thirdparty/x86_64/libunwind/libunwind-dynamic.h new file mode 100644 index 0000000..13caf16 --- /dev/null +++ b/systrace/thirdparty/x86_64/libunwind/libunwind-dynamic.h @@ -0,0 +1,201 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2002-2004 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +/* This file defines the runtime-support routines for dynamically +generated code. Even though it is implemented as part of libunwind, +it is logically separate from the interface to perform the actual +unwinding. In particular, this interface is always used in the +context of the unwind target, whereas the rest of the unwind API is +used in context of the process that is doing the unwind (which may be +a debugger running on another machine, for example). + +Note that the data-structures declared here server a dual purpose: +when a program registers a dynamically generated procedure, it uses +these structures directly. On the other hand, with remote-unwinding, +the data-structures are read from the remote process's memory and +translated into internalized versions. To facilitate remote-access, +the following rules should be followed in declaring these structures: + + (1) Declare a member as a pointer only if the the information the + member points to needs to be internalized as well (e.g., a + string representing a procedure name should be declared as + "const char *", but the instruction pointer should be declared + as unw_word_t). + + (2) Provide sufficient padding to ensure that no implicit padding + will be needed on any of the supported target architectures. For + the time being, padding data structures with the assumption that + sizeof (unw_word_t) == 8 should be sufficient. (Note: it's not + impossible to internalize structures with internal padding, but + it does make the process a bit harder). + + (3) Don't declare members that contain bitfields or floating-point + values. + + (4) Don't declare members with enumeration types. Declare them as + int32_t instead. */ + +typedef enum +{ + UNW_DYN_STOP = 0, /* end-of-unwind-info marker */ + UNW_DYN_SAVE_REG, /* save register to another register */ + UNW_DYN_SPILL_FP_REL, /* frame-pointer-relative register spill */ + UNW_DYN_SPILL_SP_REL, /* stack-pointer-relative register spill */ + UNW_DYN_ADD, /* add constant value to a register */ + UNW_DYN_POP_FRAMES, /* drop one or more stack frames */ + UNW_DYN_LABEL_STATE, /* name the current state */ + UNW_DYN_COPY_STATE, /* set the region's entry-state */ + UNW_DYN_ALIAS /* get unwind info from an alias */ +} unw_dyn_operation_t; + +typedef enum +{ + UNW_INFO_FORMAT_DYNAMIC, /* unw_dyn_proc_info_t */ + UNW_INFO_FORMAT_TABLE, /* unw_dyn_table_t */ + UNW_INFO_FORMAT_REMOTE_TABLE, /* unw_dyn_remote_table_t */ + UNW_INFO_FORMAT_ARM_EXIDX, /* ARM specific unwind info */ + UNW_INFO_FORMAT_IP_OFFSET /* Like UNW_INFO_FORMAT_REMOTE_TABLE, but + table entries are considered + relative to di->start_ip, rather + than di->segbase */ +} unw_dyn_info_format_t; + +typedef struct unw_dyn_op +{ + int8_t tag; /* what operation? */ + int8_t qp; /* qualifying predicate register */ + int16_t reg; /* what register */ + int32_t when; /* when does it take effect? */ + unw_word_t val; /* auxiliary value */ +} unw_dyn_op_t; + +typedef struct unw_dyn_region_info +{ + struct unw_dyn_region_info *next; /* linked list of regions */ + int32_t insn_count; /* region length (# of instructions) */ + uint32_t op_count; /* length of op-array */ + unw_dyn_op_t op[1]; /* variable-length op-array */ +} unw_dyn_region_info_t; + +typedef struct unw_dyn_proc_info +{ + unw_word_t name_ptr; /* address of human-readable procedure name */ + unw_word_t handler; /* address of personality routine */ + uint32_t flags; + int32_t pad0; + unw_dyn_region_info_t *regions; +} unw_dyn_proc_info_t; + +typedef struct unw_dyn_table_info +{ + unw_word_t name_ptr; /* addr. of table name (e.g., library name) */ + unw_word_t segbase; /* segment base */ + unw_word_t table_len; /* must be a multiple of sizeof(unw_word_t)! */ + unw_word_t *table_data; +} unw_dyn_table_info_t; + +typedef struct unw_dyn_remote_table_info +{ + unw_word_t name_ptr; /* addr. of table name (e.g., library name) */ + unw_word_t segbase; /* segment base */ + unw_word_t table_len; /* must be a multiple of sizeof(unw_word_t)! */ + unw_word_t table_data; +} unw_dyn_remote_table_info_t; + +typedef struct unw_dyn_info +{ + /* doubly-linked list of dyn-info structures: */ + struct unw_dyn_info *next; + struct unw_dyn_info *prev; + unw_word_t start_ip; /* first IP covered by this entry */ + unw_word_t end_ip; /* first IP NOT covered by this entry */ + unw_word_t gp; /* global-pointer in effect for this entry */ + int32_t format; /* real type: unw_dyn_info_format_t */ + int32_t pad; + unw_word_t load_offset; /* ELF load offset */ + union + { + unw_dyn_proc_info_t pi; + unw_dyn_table_info_t ti; + unw_dyn_remote_table_info_t rti; + } u; +} unw_dyn_info_t; + +typedef struct unw_dyn_info_list +{ + uint32_t version; + uint32_t generation; + unw_dyn_info_t *first; +} unw_dyn_info_list_t; + +/* Return the size (in bytes) of an unw_dyn_region_info_t structure that can + hold OP_COUNT ops. */ +#define _U_dyn_region_info_size(op_count) \ + ((char *)(((unw_dyn_region_info_t *)NULL)->op + (op_count)) - (char *)NULL) + +/* Register the unwind info for a single procedure. + This routine is NOT signal-safe. */ +extern void _U_dyn_register(unw_dyn_info_t *); + +/* Cancel the unwind info for a single procedure. + This routine is NOT signal-safe. */ +extern void _U_dyn_cancel(unw_dyn_info_t *); + +/* Convenience routines. */ + +#define _U_dyn_op(_tag, _qp, _when, _reg, _val) \ + ((unw_dyn_op_t){(_tag), (_qp), (_reg), (_when), (_val)}) + +#define _U_dyn_op_save_reg(op, qp, when, reg, dst) \ + (*(op) = _U_dyn_op(UNW_DYN_SAVE_REG, (qp), (when), (reg), (dst))) + +#define _U_dyn_op_spill_fp_rel(op, qp, when, reg, offset) \ + (*(op) = _U_dyn_op(UNW_DYN_SPILL_FP_REL, (qp), (when), (reg), (offset))) + +#define _U_dyn_op_spill_sp_rel(op, qp, when, reg, offset) \ + (*(op) = _U_dyn_op(UNW_DYN_SPILL_SP_REL, (qp), (when), (reg), (offset))) + +#define _U_dyn_op_add(op, qp, when, reg, value) \ + (*(op) = _U_dyn_op(UNW_DYN_ADD, (qp), (when), (reg), (value))) + +#define _U_dyn_op_pop_frames(op, qp, when, num_frames) \ + (*(op) = _U_dyn_op(UNW_DYN_POP_FRAMES, (qp), (when), 0, (num_frames))) + +#define _U_dyn_op_label_state(op, label) \ + (*(op) = _U_dyn_op(UNW_DYN_LABEL_STATE, _U_QP_TRUE, -1, 0, (label))) + +#define _U_dyn_op_copy_state(op, label) \ + (*(op) = _U_dyn_op(UNW_DYN_COPY_STATE, _U_QP_TRUE, -1, 0, (label))) + +#define _U_dyn_op_alias(op, qp, when, addr) \ + (*(op) = _U_dyn_op(UNW_DYN_ALIAS, (qp), (when), 0, (addr))) + +#define _U_dyn_op_stop(op) \ + (*(op) = _U_dyn_op(UNW_DYN_STOP, _U_QP_TRUE, -1, 0, 0)) + +/* The target-dependent qualifying predicate which is always TRUE. On + IA-64, that's p0 (0), on non-predicated architectures, the value is + ignored. */ +#define _U_QP_TRUE _U_TDEP_QP_TRUE diff --git a/systrace/thirdparty/x86_64/libunwind/libunwind-x86_64.h b/systrace/thirdparty/x86_64/libunwind/libunwind-x86_64.h new file mode 100644 index 0000000..e9fc817 --- /dev/null +++ b/systrace/thirdparty/x86_64/libunwind/libunwind-x86_64.h @@ -0,0 +1,146 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2002-2004 Hewlett-Packard Co + Contributed by David Mosberger-Tang + + Modified for x86_64 by Max Asbock + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef LIBUNWIND_H +#define LIBUNWIND_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" +{ +#endif + +#include +#include +#include +#include + +#ifndef UNW_EMPTY_STRUCT +#define UNW_EMPTY_STRUCT uint8_t unused; +#endif + +#define UNW_TARGET x86_64 +#define UNW_TARGET_X86_64 1 + +#define _U_TDEP_QP_TRUE 0 /* see libunwind-dynamic.h */ + +/* This needs to be big enough to accommodate "struct cursor", while + leaving some slack for future expansion. Changing this value will + require recompiling all users of this library. Stack allocation is + relatively cheap and unwind-state copying is relatively rare, so we + want to err on making it rather too big than too small. */ +#define UNW_TDEP_CURSOR_LEN 127 + + typedef uint64_t unw_word_t; + typedef int64_t unw_sword_t; + + typedef long double unw_tdep_fpreg_t; + +#define UNW_WORD_MAX UINT64_MAX + + typedef enum + { + UNW_X86_64_RAX, + UNW_X86_64_RDX, + UNW_X86_64_RCX, + UNW_X86_64_RBX, + UNW_X86_64_RSI, + UNW_X86_64_RDI, + UNW_X86_64_RBP, + UNW_X86_64_RSP, + UNW_X86_64_R8, + UNW_X86_64_R9, + UNW_X86_64_R10, + UNW_X86_64_R11, + UNW_X86_64_R12, + UNW_X86_64_R13, + UNW_X86_64_R14, + UNW_X86_64_R15, + UNW_X86_64_RIP, +#ifdef CONFIG_MSABI_SUPPORT + UNW_X86_64_XMM0, + UNW_X86_64_XMM1, + UNW_X86_64_XMM2, + UNW_X86_64_XMM3, + UNW_X86_64_XMM4, + UNW_X86_64_XMM5, + UNW_X86_64_XMM6, + UNW_X86_64_XMM7, + UNW_X86_64_XMM8, + UNW_X86_64_XMM9, + UNW_X86_64_XMM10, + UNW_X86_64_XMM11, + UNW_X86_64_XMM12, + UNW_X86_64_XMM13, + UNW_X86_64_XMM14, + UNW_X86_64_XMM15, + UNW_TDEP_LAST_REG = UNW_X86_64_XMM15, +#else + UNW_TDEP_LAST_REG = UNW_X86_64_RIP, +#endif + + /* XXX Add other regs here */ + + /* frame info (read-only) */ + UNW_X86_64_CFA, + + UNW_TDEP_IP = UNW_X86_64_RIP, + UNW_TDEP_SP = UNW_X86_64_RSP, + UNW_TDEP_BP = UNW_X86_64_RBP, + UNW_TDEP_EH = UNW_X86_64_RAX + } x86_64_regnum_t; + +#define UNW_TDEP_NUM_EH_REGS 2 /* XXX Not sure what this means */ + + typedef struct unw_tdep_save_loc + { + /* Additional target-dependent info on a save location. */ + UNW_EMPTY_STRUCT + } unw_tdep_save_loc_t; + + /* On x86_64, we can directly use ucontext_t as the unwind context. */ + typedef ucontext_t unw_tdep_context_t; + + typedef struct + { + /* no x86-64-specific auxiliary proc-info */ + UNW_EMPTY_STRUCT + } unw_tdep_proc_info_t; + +#include "libunwind-common.h" +#include "libunwind-dynamic.h" + +#define unw_tdep_getcontext UNW_ARCH_OBJ(getcontext) +#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg) + + extern int unw_tdep_getcontext(unw_tdep_context_t *); + extern int unw_tdep_is_fpreg(int); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* LIBUNWIND_H */ diff --git a/systrace/thirdparty/x86_64/libunwind/libunwind.h b/systrace/thirdparty/x86_64/libunwind/libunwind.h new file mode 100644 index 0000000..db092c7 --- /dev/null +++ b/systrace/thirdparty/x86_64/libunwind/libunwind.h @@ -0,0 +1,40 @@ +/* Provide a real file - not a symlink - as it would cause multiarch conflicts + when multiple different arch releases are installed simultaneously. */ + +#ifndef UNW_REMOTE_ONLY + +#if defined __aarch64__ +#include "libunwind-aarch64.h" +#elif defined __arm__ +#include "libunwind-arm.h" +#elif defined __hppa__ +#include "libunwind-hppa.h" +#elif defined __ia64__ +#include "libunwind-ia64.h" +#elif defined __mips__ +#include "libunwind-mips.h" +#elif defined __powerpc__ && !defined __powerpc64__ +#include "libunwind-ppc32.h" +#elif defined __powerpc64__ +#include "libunwind-ppc64.h" +#elif defined __sh__ +#include "libunwind-sh.h" +#elif defined __i386__ +#include "libunwind-x86.h" +#elif defined __x86_64__ +#include "libunwind-x86_64.h" +#elif defined __s390x__ +#include "libunwind-s390x.h" +#elif defined __riscv || defined __riscv__ +#include "libunwind-riscv.h" +#elif defined __loongarch64 +#include "libunwind-loongarch64.h" +#else +#error "Unsupported arch" +#endif + +#else /* UNW_REMOTE_ONLY */ + +#include "libunwind-x86_64.h" + +#endif /* UNW_REMOTE_ONLY */ diff --git a/systrace/thirdparty/x86_64/libunwind/unwind.h b/systrace/thirdparty/x86_64/libunwind/unwind.h new file mode 100644 index 0000000..69201dc --- /dev/null +++ b/systrace/thirdparty/x86_64/libunwind/unwind.h @@ -0,0 +1,158 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _UNWIND_H +#define _UNWIND_H + +/* For uint64_t */ +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Minimal interface as per C++ ABI draft standard: + + http://www.codesourcery.com/cxx-abi/abi-eh.html */ + + typedef enum + { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 + } _Unwind_Reason_Code; + + typedef int _Unwind_Action; + +#define _UA_SEARCH_PHASE 1 +#define _UA_CLEANUP_PHASE 2 +#define _UA_HANDLER_FRAME 4 +#define _UA_FORCE_UNWIND 8 + + struct _Unwind_Context; /* opaque data-structure */ + struct _Unwind_Exception; /* forward-declaration */ + + typedef void (*_Unwind_Exception_Cleanup_Fn)(_Unwind_Reason_Code, + struct _Unwind_Exception *); + + typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)(int, _Unwind_Action, + uint64_t, + struct _Unwind_Exception *, + struct _Unwind_Context *, + void *); + + /* The C++ ABI requires exception_class, private_1, and private_2 to + be of type uint64 and the entire structure to be + double-word-aligned. Please note that exception_class stays 64-bit + even on 32-bit machines for gcc compatibility. */ + struct _Unwind_Exception + { + alignas(8) uint64_t exception_class; + _Unwind_Exception_Cleanup_Fn exception_cleanup; + unsigned long private_1; + unsigned long private_2; + }; + + extern _Unwind_Reason_Code + _Unwind_RaiseException(struct _Unwind_Exception *); + extern _Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception *, + _Unwind_Stop_Fn, void *); + extern void _Unwind_Resume(struct _Unwind_Exception *); + extern void _Unwind_DeleteException(struct _Unwind_Exception *); + extern unsigned long _Unwind_GetGR(struct _Unwind_Context *, int); + extern void _Unwind_SetGR(struct _Unwind_Context *, int, unsigned long); + extern unsigned long _Unwind_GetIP(struct _Unwind_Context *); + extern unsigned long _Unwind_GetIPInfo(struct _Unwind_Context *, int *); + extern void _Unwind_SetIP(struct _Unwind_Context *, unsigned long); + extern unsigned long + _Unwind_GetLanguageSpecificData(struct _Unwind_Context *); + extern unsigned long _Unwind_GetRegionStart(struct _Unwind_Context *); + +#ifdef _GNU_SOURCE + + /* Callback for _Unwind_Backtrace(). The backtrace stops immediately + if the callback returns any value other than _URC_NO_REASON. */ + typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *, + void *); + +/* See http://gcc.gnu.org/ml/gcc-patches/2001-09/msg00082.html for why + _UA_END_OF_STACK exists. */ +#define _UA_END_OF_STACK 16 + + /* If the unwind was initiated due to a forced unwind, resume that + operation, else re-raise the exception. This is used by + __cxa_rethrow(). */ + extern _Unwind_Reason_Code + _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *); + + /* See http://gcc.gnu.org/ml/gcc-patches/2003-09/msg00154.html for why + _Unwind_GetBSP() exists. */ + extern unsigned long _Unwind_GetBSP(struct _Unwind_Context *); + + /* Return the "canonical frame address" for the given context. + This is used by NPTL... */ + extern unsigned long _Unwind_GetCFA(struct _Unwind_Context *); + + /* Return the base-address for data references. */ + extern unsigned long _Unwind_GetDataRelBase(struct _Unwind_Context *); + + /* Return the base-address for text references. */ + extern unsigned long _Unwind_GetTextRelBase(struct _Unwind_Context *); + + /* Call _Unwind_Trace_Fn once for each stack-frame, without doing any + cleanup. The first frame for which the callback is invoked is the + one for the caller of _Unwind_Backtrace(). _Unwind_Backtrace() + returns _URC_END_OF_STACK when the backtrace stopped due to + reaching the end of the call-chain or _URC_FATAL_PHASE1_ERROR if it + stops for any other reason. */ + extern _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *); + + /* Find the start-address of the procedure containing the specified IP + or NULL if it cannot be found (e.g., because the function has no + unwind info). Note: there is not necessarily a one-to-one + correspondence between source-level functions and procedures: some + functions don't have unwind-info and others are split into multiple + procedures. */ + extern void *_Unwind_FindEnclosingFunction(void *); + + /* See also Linux Standard Base Spec: + http://www.linuxbase.org/spec/refspecs/LSB_1.3.0/gLSB/gLSB/libgcc-s.html + */ + +#endif /* _GNU_SOURCE */ + +#ifdef __cplusplus +}; +#endif + +#endif /* _UNWIND_H */ diff --git a/systrace/thirdparty/x86_64/mspti/include/mspti.h b/systrace/thirdparty/x86_64/mspti/include/mspti.h new file mode 100644 index 0000000..e83c454 --- /dev/null +++ b/systrace/thirdparty/x86_64/mspti/include/mspti.h @@ -0,0 +1,19 @@ +/** + * @file mspti.h + * + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef MSPTI_H +#define MSPTI_H + +#include "mspti_activity.h" +#include "mspti_callback.h" +#include "mspti_cbid.h" +#include "mspti_result.h" + +#endif diff --git a/systrace/thirdparty/x86_64/mspti/include/mspti_activity.h b/systrace/thirdparty/x86_64/mspti/include/mspti_activity.h new file mode 100644 index 0000000..30f7159 --- /dev/null +++ b/systrace/thirdparty/x86_64/mspti/include/mspti_activity.h @@ -0,0 +1,424 @@ +/** + * @file mspti_activity.h + * + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef MSPTI_ACTIVITY_H +#define MSPTI_ACTIVITY_H + +#define ACTIVITY_STRUCT_ALIGNMENT 8 +#if defined(_WIN32) +#define START_PACKED_ALIGNMENT __pragma(pack(push, 1)) +#define PACKED_ALIGNMENT __declspec(align(ACTIVITY_STRUCT_ALIGNMENT)) +#define END_PACKED_ALIGNMENT __pragma(pack(pop)) +#elif defined(__GNUC__) +#define START_PACKED_ALIGNMENT +#define PACKED_ALIGNMENT \ + __attribute__((__packed__)) \ + __attribute__((aligned(ACTIVITY_STRUCT_ALIGNMENT))) +#define END_PACKED_ALIGNMENT +#else +#define START_PACKED_ALIGNMENT +#define PACKED_ALIGNMENT +#define END_PACKED_ALIGNMENT +#endif + +#include "mspti_result.h" +#include +#include + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#if defined(__GNUC__) && defined(MSPTI_LIB) +#pragma GCC visibility push(default) +#endif + + /** + * @brief The kinds of activity records. + * + * Each kind is associated with a + * activity record structure that holds the information associated + * with the kind. + */ + typedef enum + { + /** + * The activity record is invalid. + */ + MSPTI_ACTIVITY_KIND_INVALID = 0, + MSPTI_ACTIVITY_KIND_MARKER = 1, + MSPTI_ACTIVITY_KIND_KERNEL = 2, + MSPTI_ACTIVITY_KIND_API = 3, + MSPTI_ACTIVITY_KIND_COUNT, + MSPTI_ACTIVITY_KIND_FORCE_INT = 0x7fffffff + } msptiActivityKind; + + /** + * @brief The source kinds of mark data. + * + * Each mark activity record kind represents information about host or + * device + */ + typedef enum + { + MSPTI_ACTIVITY_SOURCE_KIND_HOST = 0, + MSPTI_ACTIVITY_SOURCE_KIND_DEVICE = 1 + } msptiActivitySourceKind; + + /** + * @brief Flags linked to activity records. + * + * These are the Flags that pertain to activity records. + * Flags can be combined by bitwise OR to + * associated multiple flags with an activity record. + */ + typedef enum + { + /** + * Signifies that the activity record lacks any flags. + */ + MSPTI_ACTIVITY_FLAG_NONE = 0, + /** + * Represents the activity as a pure host instantaneous marker. Works + * with MSPTI_ACTIVITY_KIND_MARKER. + */ + MSPTI_ACTIVITY_FLAG_MARKER_INSTANTANEOUS = 1 << 0, + /** + * Represents the activity as a pure host region start marker. Works + * with MSPTI_ACTIVITY_KIND_MARKER. + */ + MSPTI_ACTIVITY_FLAG_MARKER_START = 1 << 1, + /** + * Represents the activity as a pure host region end marker. Works with + * MSPTI_ACTIVITY_KIND_MARKER. + */ + MSPTI_ACTIVITY_FLAG_MARKER_END = 1 << 2, + /** + * Represents the activity as an instantaneous marker with device. Works + * with MSPTI_ACTIVITY_KIND_MARKER. + */ + MSPTI_ACTIVITY_FLAG_MARKER_INSTANTANEOUS_WITH_DEVICE = 1 << 3, + /** + * Represents the activity as a pure start marker with device. Works + * with MSPTI_ACTIVITY_KIND_MARKER. + */ + MSPTI_ACTIVITY_FLAG_MARKER_START_WITH_DEVICE = 1 << 4, + /** + * Represents the activity as a pure end marker with device. Works with + * MSPTI_ACTIVITY_KIND_MARKER. + */ + MSPTI_ACTIVITY_FLAG_MARKER_END_WITH_DEVICE = 1 << 5 + } msptiActivityFlag; + + START_PACKED_ALIGNMENT + + typedef struct PACKED_ALIGNMENT + { + msptiActivityKind kind; + } msptiActivity; + + typedef union PACKED_ALIGNMENT + { + /** + * A thread object requires that we identify both the process and + * thread ID. + */ + struct + { + uint32_t processId; + uint32_t threadId; + } pt; + /** + * A stream object requires that we identify device and stream ID. + */ + struct + { + uint32_t deviceId; + uint32_t streamId; + } ds; + } msptiObjectId; + + /** + * @brief This activity record serves as a marker, representing a specific + * moment in time. + * + * The marker is characterized by a distinctive name and a unique identifier + */ + typedef struct PACKED_ALIGNMENT + { + /** + * The activity record kind, always be MSPTI_ACTIVITY_KIND_MARKER. + */ + msptiActivityKind kind; + + /** + * The flags associated with the marker. + * @see msptiActivityFlag + */ + msptiActivityFlag flag; + + /** + * The source kinds of mark data. + * @see msptiActivitySourceKind + */ + msptiActivitySourceKind sourceKind; + + /** + * The timestamp for the marker, in ns. A value of 0 indicates that + * timestamp information could not be collected for the marker. + */ + uint64_t timestamp; + + /** + * The marker ID. + */ + uint64_t id; + + /** + * The identifier for the activity object associated with this + * marker. 'objectKind' indicates which ID is valid for this record. + */ + msptiObjectId objectId; + + /** + * The marker name for an instantaneous or start marker. + * This will be NULL for an end marker. + */ + const char *name; + + /** + * The name of the domain to which this marker belongs to. + * This will be NULL for default domain. + */ + const char *domain; + } msptiActivityMarker; + + typedef struct PACKED_ALIGNMENT + { + /** + * The activity record kind, must be MSPTI_ACTIVITY_KIND_API. + */ + msptiActivityKind kind; + + /** + * The start timestamp for the api, in ns. + */ + uint64_t start; + + /** + * The end timestamp for the api, in ns. + */ + uint64_t end; + + /** + * A thread object requires that we identify both the process and + * thread ID. + */ + struct + { + uint32_t processId; + uint32_t threadId; + } pt; + + /** + * The correlation ID of the kernel. + */ + uint64_t correlationId; + + /** + * The api name. + */ + const char *name; + } msptiActivityApi; + + typedef struct PACKED_ALIGNMENT + { + /** + * The activity record kind, must be MSPTI_ACTIVITY_KIND_KERNEL. + */ + msptiActivityKind kind; + + /** + * The start timestamp for the kernel, in ns. + */ + uint64_t start; + + /** + * The end timestamp for the kernel, in ns. + */ + uint64_t end; + + /** + * A stream object requires that we identify device and stream ID. + */ + struct + { + uint32_t deviceId; + uint32_t streamId; + } ds; + + /** + * The correlation ID of the kernel. + */ + uint64_t correlationId; + + /** + * The kernel type. + */ + const char *type; + + /** + * The kernel name. + */ + const char *name; + } msptiActivityKernel; + + END_PACKED_ALIGNMENT + + /** + * @brief Function type for callback used by MSPTI to request an empty + * buffer for storing activity records. + * + * This callback function signals the MSPTI client that an activity + * buffer is needed by MSPTI. The activity buffer is used by MSPTI to + * store activity records. The callback function can decline the + * request by setting **buffer to NULL. In this case MSPTI may drop + * activity records. + * + * @param buffer Returns the new buffer. If set to NULL then no buffer + * is returned. + * @param size Returns the size of the returned buffer. + * @param maxNumRecords Returns the maximum number of records that + * should be placed in the buffer. If 0 then the buffer is filled with + * as many records as possible. If > 0 the buffer is filled with at + * most that many records before it is returned. + */ + typedef void (*msptiBuffersCallbackRequestFunc)(uint8_t **buffer, + size_t *size, + size_t *maxNumRecords); + + /** + * @brief Function type for callback used by MSPTI to return a buffer + * of activity records. + * + * This callback function returns to the MSPTI client a buffer + * containing activity records. The buffer contains @p validSize + * bytes of activity records which should be read using + * msptiActivityGetNextRecord. After this call MSPTI + * relinquished ownership of the buffer and will not use it + * anymore. The client may return the buffer to MSPTI using the + * msptiBuffersCallbackRequestFunc callback. + * + * @param buffer The activity record buffer. + * @param size The total size of the buffer in bytes as set in + * MSPTI_BuffersCallbackRequestFunc. + * @param validSize The number of valid bytes in the buffer. + */ + typedef void (*msptiBuffersCallbackCompleteFunc)(uint8_t *buffer, + size_t size, + size_t validSize); + + /** + * @brief Registers callback functions with MSPTI for activity buffer + * handling. + * + * This function registers two callback functions to be used in asynchronous + * buffer handling. If registered, activity record buffers are handled using + * asynchronous requested/completed callbacks from MSPTI. + * + * @param funcBufferRequested callback which is invoked when an empty + * buffer is requested by MSPTI + * @param funcBufferCompleted callback which is invoked when a buffer + * containing activity records is available from MSPTI + * + * @retval MSPTI_SUCCESS + * @retval MSPTI_ERROR_INVALID_PARAMETER if either + * funcBufferRequested or funcBufferCompleted is NULL + */ + msptiResult msptiActivityRegisterCallbacks( + msptiBuffersCallbackRequestFunc funcBufferRequested, + msptiBuffersCallbackCompleteFunc funcBufferCompleted); + + /** + * @brief Enable collection of a specific kind of activity record. + * + * Enable collection of a specific kind of activity record. Multiple + * kinds can be enabled by calling this function multiple times. + * By default, the collection of all activity types is inactive. + * + * @param kind The kind of activity record to collect + * + * @retval MSPTI_SUCCESS + */ + msptiResult msptiActivityEnable(msptiActivityKind kind); + + /** + * @brief Disable collection of a specific kind of activity record. + * + * Disable collection of a specific kind of activity record. Multiple + * kinds can be disabled by calling this function multiple times. + * By default, the collection of all activity types is inactive. + * + * @param kind The kind of activity record to stop collecting + * + * @retval MSPTI_SUCCESS + */ + msptiResult msptiActivityDisable(msptiActivityKind kind); + + /** + * @brief Iterate over the activity records in a buffer. + * + * This is a function to iterate over the activity records in buffer. + * + * @param buffer The buffer containing activity records + * @param validBufferSizeBytes The number of valid bytes in the buffer. + * @param record Inputs the previous record returned by + * msptiActivityGetNextRecord and returns the next activity record + * from the buffer. If input value is NULL, returns the first activity + * record in the buffer. + * + * @retval MSPTI_SUCCESS + * @retval MSPTI_ERROR_MAX_LIMIT_REACHED if no more records in the buffer + * @retval MSPTI_ERROR_INVALID_PARAMETER if buffer is NULL. + */ + msptiResult msptiActivityGetNextRecord(uint8_t *buffer, + size_t validBufferSizeBytes, + msptiActivity **record); + + /** + * @brief Request to deliver activity records via the buffer completion + * callback. + * + * This function returns the activity records associated with all + * contexts/streams (and the global buffers not associated with any stream) + * to the MSPTI client using the callback registered in + * msptiActivityRegisterCallbacks. It return all activity buffers that + * contain completed activity records, even if these buffers are not + * completely filled. + * + * Before calling this function, the buffer handling callback api must be + * activated by calling msptiActivityRegisterCallbacks. + * + * @param flag Reserved for internal use. + * + * @retval MSPTI_SUCCESS + */ + msptiResult msptiActivityFlushAll(uint32_t flag); + +#if defined(__GNUC__) && defined(MSPTI_LIB) +#pragma GCC visibility pop +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/systrace/thirdparty/x86_64/mspti/include/mspti_callback.h b/systrace/thirdparty/x86_64/mspti/include/mspti_callback.h new file mode 100644 index 0000000..2e6f7ee --- /dev/null +++ b/systrace/thirdparty/x86_64/mspti/include/mspti_callback.h @@ -0,0 +1,258 @@ +/** + * @file mspti_callback.h + * + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef MSPTI_CALLBACK_H +#define MSPTI_CALLBACK_H + +#include "mspti_cbid.h" +#include "mspti_result.h" +#include + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#if defined(__GNUC__) && defined(MSPTI_LIB) +#pragma GCC visibility push(default) +#endif + + /** + * @brief Callback domains. + * + * Callback domains. Each domain represents callback points for a + * group of related API functions or CANN driver activity. + */ + typedef enum + { + /** + * Invalid domain. + */ + MSPTI_CB_DOMAIN_INVALID = 0, + /** + * Domain containing callback points for all runtime API functions. + */ + MSPTI_CB_DOMAIN_RUNTIME = 1, + MSPTI_CB_DOMAIN_HCCL = 2, + MSPTI_CB_DOMAIN_SIZE, + MSPTI_CB_DOMAIN_FORCE_INT = 0x7fffffff + } msptiCallbackDomain; + + typedef uint32_t msptiCallbackId; + + /** + * @brief Specifies the point in an API call that a callback is issued. + * + * Specifies the point in an API call that a callback is issued. This + * value is communicated to the callback function by @ref + * msptiCallbackData::callbackSite. + */ + typedef enum + { + /** + * The callback is at the entry of the API call. + */ + MSPTI_API_ENTER = 0, + /** + * The callback is at the exit of the API call. + */ + MSPTI_API_EXIT = 1, + MSPTI_API_CBSITE_FORCE_INT = 0x7fffffff + } msptiApiCallbackSite; + + typedef struct + { + /** + * Point in the runtime or driver function from where the callback + * was issued. + */ + msptiApiCallbackSite callbackSite; + + /** + * Name of the runtime or driver API function which issued the + * callback. + */ + const char *functionName; + + /** + * Params of the runtime or driver API function which issued the + * callback. + */ + const void *functionParams; + + /** + * Pointer to the return value of the runtime or driver API + * call. + */ + const void *functionReturnValue; + + /** + * Name of the symbol operated on by the runtime or driver API + * function which issued the callback. This entry is valid only for + * driver and runtime launch callbacks, where it returns the name of + * the kernel. + */ + const char *symbolName; + + /** + * The activity record correlation ID for this callback. For a + * driver domain callback (i.e. @p domain + * MSPTI_CB_DOMAIN_DRIVER_API) this ID will equal the correlation ID + * in the MSPTI_ActivityAPI record corresponding to the CANN driver + * function call. For a runtime domain callback (i.e. @p domain + * MSPTI_CB_DOMAIN_RUNTIME_API) this ID will equal the correlation + * ID in the MSPTI_ActivityAPI record corresponding to the CANN + * runtime function call. Within the callback, this ID can be + * recorded to correlate user data with the activity record. + */ + uint64_t correlationId; + + /** + * Undefined. Reserved for internal use. + */ + uint64_t reserved1; + + /** + * Undefined. Reserved for internal use. + */ + uint64_t reserved2; + + /** + * Pointer to data shared between the entry and exit callbacks of + * a given runtime or drive API function invocation. This field + * can be used to pass 64-bit values from the entry callback to + * the corresponding exit callback. + */ + uint64_t *correlationData; + } msptiCallbackData; + + /** + * @brief Function type for a callback. + * + * Function type for a callback. The type of the data passed to the + * callback in @p cbdata depends on the @p domain. If @p domain is + * MSPTI_CB_DOMAIN_RUNTIME the type + * of @p cbdata will be msptiCallbackData. + * + * @param userdata User data supplied at subscription of the callback + * @param domain The domain of the callback + * @param cbid The ID of the callback + * @param cbdata Data passed to the callback. + */ + typedef void (*msptiCallbackFunc)(void *userdata, + msptiCallbackDomain domain, + msptiCallbackId cbid, + const msptiCallbackData *cbdata); + + struct msptiSubscriber_st; + + /** + * @brief A callback subscriber. + */ + typedef struct msptiSubscriber_st *msptiSubscriberHandle; + + /** + * @brief Initialize a callback subscriber with a callback function + * and user data. + * + * Initializes a callback subscriber with a callback function and + * (optionally) a pointer to user data. The returned subscriber handle + * can be used to enable and disable the callback for specific domains + * and callback IDs. + * @note Only a single subscriber can be registered at a time. To ensure + * that no other MSPTI client interrupts the profiling session, it's the + * responsibility of all the MSPTI clients to call this function before + * starting the profling session. + * @note This function does not enable any callbacks. + * @note @b Thread-safety: this function is thread safe. + * + * @param subscriber handle to initialize subscriber + * @param callback The callback function + * @param userdata A pointer to user data. This data will be passed to + * the callback function via the @p userdata paramater. + * + * @retval MSPTI_SUCCESS on success + * @retval MSPTI_ERROR_INNER if unable to initialize MSPTI + * @retval MSPTI_ERROR_MULTIPLE_SUBSCRIBERS_NOT_SUPPORTED if there is + * already a MSPTI subscriber + * @retval MSPTI_ERROR_INVALID_PARAMETER if @p subscriber is NULL + */ + msptiResult msptiSubscribe(msptiSubscriberHandle *subscriber, + msptiCallbackFunc callback, void *userdata); + + /** + * @brief Unregister a callback subscriber. + * + * Removes a callback subscriber so that no future callbacks will be + * issued to that subscriber. + * + * @param subscriber Handle to the initialize subscriber + * + * @retval MSPTI_SUCCESS on success + * @retval MSPTI_ERROR_INVALID_PARAMETER if @p subscriber is NULL or not + * initialized + */ + msptiResult msptiUnsubscribe(msptiSubscriberHandle subscriber); + + /** + * @brief Enable or disabled callbacks for a specific domain and + * callback ID. + * + * Enable or disabled callbacks for a subscriber for a specific domain + * and callback ID. + * + * @note @b Thread-safety: a subscriber must serialize access to + * msptiEnableCallback, msptiEnableDomain. + * + * @param enable New enable state for the callback. Zero disables the + * callback, non-zero enables the callback. + * @param subscriber Handle to callback subscription + * @param domain The domain of the callback + * @param cbid The ID of the callback + * + * @retval MSPTI_SUCCESS on success + * @retval MSPTI_ERROR_INVALID_PARAMETER if @p subscriber, @p domain or @p + * cbid is invalid. + */ + msptiResult msptiEnableCallback(uint32_t enable, + msptiSubscriberHandle subscriber, + msptiCallbackDomain domain, + msptiCallbackId cbid); + + /** + * @brief Enable or disabled callbacks for a specific domain + * + * Enable or disabled callbacks for a subscriber for a specific domain + * + * @note @b Thread-safety: a subscriber must serialize access to + * msptiEnableCallback, msptiEnableDomain. + * + * @param enable New enable state for the callback. Zero disables the + * callback, non-zero enables the callback. + * @param subscriber Handle to callback subscription + * @param domain The domain of the callback + * + * @retval MSPTI_SUCCESS on success + * @retval MSPTI_ERROR_INVALID_PARAMETER if @p subscriber, @p domain is + * invalid. + */ + msptiResult msptiEnableDomain(uint32_t enable, + msptiSubscriberHandle subscriber, + msptiCallbackDomain domain); + +#if defined(__GNUC__) && defined(MSPTI_LIB) +#pragma GCC visibility pop +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/systrace/thirdparty/x86_64/mspti/include/mspti_cbid.h b/systrace/thirdparty/x86_64/mspti/include/mspti_cbid.h new file mode 100644 index 0000000..540ad39 --- /dev/null +++ b/systrace/thirdparty/x86_64/mspti/include/mspti_cbid.h @@ -0,0 +1,83 @@ +/** + * @file mspti_cbid.h + * + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef MSPTI_CBID_H +#define MSPTI_CBID_H + +/** + * @brief Definitions of indices for Runtime API functions, unique across entire + * API + */ +typedef enum +{ + MSPTI_CBID_RUNTIME_INVALID = 0, + MSPTI_CBID_RUNTIME_DEVICE_SET = 1, + MSPTI_CBID_RUNTIME_DEVICE_RESET = 2, + MSPTI_CBID_RUNTIME_DEVICE_SET_EX = 3, + MSPTI_CBID_RUNTIME_CONTEXT_CREATED_EX = 4, + MSPTI_CBID_RUNTIME_CONTEXT_CREATED = 5, + MSPTI_CBID_RUNTIME_CONTEXT_DESTROY = 6, + MSPTI_CBID_RUNTIME_STREAM_CREATED = 7, + MSPTI_CBID_RUNTIME_STREAM_DESTROY = 8, + MSPTI_CBID_RUNTIME_STREAM_SYNCHRONIZED = 9, + MSPTI_CBID_RUNTIME_LAUNCH = 10, + MSPTI_CBID_RUNTIME_CPU_LAUNCH = 11, + MSPTI_CBID_RUNTIME_AICPU_LAUNCH = 12, + MSPTI_CBID_RUNTIME_AIV_LAUNCH = 13, + MSPTI_CBID_RUNTIME_FFTS_LAUNCH = 14, + MSPTI_CBID_RUNTIME_MALLOC = 15, + MSPTI_CBID_RUNTIME_FREE = 16, + MSPTI_CBID_RUNTIME_MALLOC_HOST = 17, + MSPTI_CBID_RUNTIME_FREE_HOST = 18, + MSPTI_CBID_RUNTIME_MALLOC_CACHED = 19, + MSPTI_CBID_RUNTIME_FLUSH_CACHE = 20, + MSPTI_CBID_RUNTIME_INVALID_CACHE = 21, + MSPTI_CBID_RUNTIME_MEMCPY = 22, + MSPTI_CBID_RUNTIME_MEMCPY_HOST = 23, + MSPTI_CBID_RUNTIME_MEMCPY_ASYNC = 24, + MSPTI_CBID_RUNTIME_MEM_CPY2D = 25, + MSPTI_CBID_RUNTIME_MEM_CPY2D_ASYNC = 26, + MSPTI_CBID_RUNTIME_MEM_SET = 27, + MSPTI_CBID_RUNTIME_MEM_SET_ASYNC = 28, + MSPTI_CBID_RUNTIME_MEM_GET_INFO = 29, + MSPTI_CBID_RUNTIME_RESERVE_MEM_ADDRESS = 30, + MSPTI_CBID_RUNTIME_RELEASE_MEM_ADDRESS = 31, + MSPTI_CBID_RUNTIME_MALLOC_PHYSICAL = 32, + MSPTI_CBID_RUNTIME_FREE_PHYSICAL = 33, + MSPTI_CBID_RUNTIME_MEM_EXPORT_TO_SHAREABLE_HANDLE = 34, + MSPTI_CBID_RUNTIME_MEM_IMPORT_FROM_SHAREABLE_HANDLE = 35, + MSPTI_CBID_RUNTIME_MEM_SET_PID_TO_SHAREABLE_HANDLE = 36, + MSPTI_CBID_RUNTIME_SIZE, + MSPTI_CBID_RUNTIME_FORCE_INT = 0x7fffffff +} msptiCallbackIdRuntime; + +/** + * @brief Definitions of indices for hccl API functions + */ +typedef enum +{ + MSPTI_CBID_HCCL_INVALID = 0, + MSPTI_CBID_HCCL_ALLREDUCE = 1, + MSPTI_CBID_HCCL_BROADCAST = 2, + MSPTI_CBID_HCCL_ALLGATHER = 3, + MSPTI_CBID_HCCL_REDUCE_SCATTER = 4, + MSPTI_CBID_HCCL_REDUCE = 5, + MSPTI_CBID_HCCL_ALL_TO_ALL = 6, + MSPTI_CBID_HCCL_ALL_TO_ALLV = 7, + MSPTI_CBID_HCCL_BARRIER = 8, + MSPTI_CBID_HCCL_SCATTER = 9, + MSPTI_CBID_HCCL_SEND = 10, + MSPTI_CBID_HCCL_RECV = 11, + MSPTI_CBID_HCCL_SENDRECV = 12, + MSPTI_CBID_HCCL_SIZE, + MSPTI_CBID_HCCL_FORCE_INT = 0x7fffffff +} msptiCallbackIdHccl; + +#endif diff --git a/systrace/thirdparty/x86_64/mspti/include/mspti_result.h b/systrace/thirdparty/x86_64/mspti/include/mspti_result.h new file mode 100644 index 0000000..902647e --- /dev/null +++ b/systrace/thirdparty/x86_64/mspti/include/mspti_result.h @@ -0,0 +1,30 @@ +/** + * @file mspti_result.h + * + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef MSPTI_BASE_H +#define MSPTI_BASE_H + +/** + * @brief MSPTI result codes. + * + * Error and result codes returned by MSPTI functions. + */ +typedef enum +{ + MSPTI_SUCCESS = 0, + MSPTI_ERROR_INVALID_PARAMETER = 1, + MSPTI_ERROR_MULTIPLE_SUBSCRIBERS_NOT_SUPPORTED = 2, + MSPTI_ERROR_MAX_LIMIT_REACHED = 3, + MSPTI_ERROR_DEVICE_OFFLINE = 4, + MSPTI_ERROR_INNER = 999, + MSPTI_ERROR_FOECE_INT = 0x7fffffff +} msptiResult; + +#endif diff --git a/systrace/thirdparty/x86_64/mspti/lib64/libmspti.so b/systrace/thirdparty/x86_64/mspti/lib64/libmspti.so new file mode 100644 index 0000000000000000000000000000000000000000..79f2ec422f26585fd16f8b9ff95318447f8458fc GIT binary patch literal 522288 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⋙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


xa9_>0eOQDs^jn(wqcqeBY0j-BQ_kftIPv- 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 len(target) else window + return False + except FileNotFoundError: + print(f"文件 {file_path} 不存在") + return False + + def update_env_vars(self): + """更新环境变量""" + if not self.validate_json(self.args.trace): + return + try: + with open(self.args.trace, 'r') as f: + data = json.load(f) + for key, env_var in ENV_MAPPING.items(): + value = str(data.get(key, "false")).lower() + os.environ[env_var] = value + logging.info(f"Set {env_var}={value}") + except Exception as e: + logging.error(f"Env update failed: {str(e)}") + + def check_escape_trigger(self): + """检查逃生开关""" + if not self.validate_json(self.args.trace): + return + try: + with open(self.args.trace, 'r') as f: + data = json.load(f) + if data.get("escape_switch", False) is True: + self.kill_sysTrace() + except Exception as e: + logging.error(f"Escape check failed: {str(e)}") + + def scheduler_task(self): + """定时任务调度""" + every(DEFAULT_LOOP_TIME).seconds.do(self.update_env_vars) + every(DEFAULT_LOOP_TIME).seconds.do(self.check_escape_trigger) + every(DEFAULT_LOOP_TIME).seconds.do(self.check_systrace_stream, self.args.tracelog) + while True: + run_pending() + time.sleep(1) + + def run(self): + """主运行循环""" + threading.Thread(target=self.scheduler_task, daemon=True).start() + try: + while True: + time.sleep(3600) + except KeyboardInterrupt: + self.kill_sysTrace() + logging.info("Service stopped") + +if __name__ == "__main__": + monitor = SystemMonitor(DEFAULT_TRACE_FILE) + monitor.run() \ No newline at end of file -- Gitee