已合并
process_mem: 新模型单元测试补齐与配置文档更新 #1431
wangwenlong7创建于 11 天前
process_mem: 新模型单元测试补齐与配置文档更新 #1431
已合并
共 50 个文件变更+8988-6349
| @@ -3,8 +3,30 @@ ubse.plugin.name=process_mem | |||
| 3 | # Name of the SO file on which the process_mem module depends. Fixed value: libprocess_mem.so. | 3 | # Name of the SO file on which the process_mem module depends. Fixed value: libprocess_mem.so. |
| 4 | ubse.plugin.pkg=libprocess_mem.so | 4 | ubse.plugin.pkg=libprocess_mem.so |
| 5 | 5 | ||
| 6 | -[process_mem.pid] | 6 | +[process_mem] |
| 7 | -# NUMA distribution collection interval, in seconds. The value range is [1, 3600]. Any invalid value will be reset to 10. | 7 | +# 采集周期, 读 /proc 的间隔 (可配范围 1-3600s) |
| 8 | -collect.interval=10 | 8 | +collect_process_interval=5 |
| 9 | -# Timeout threshold for CREATING memory borrow debts, in seconds. If a borrow remains in CREATING state beyond this timeout, the PID is reset. Any invalid value will be reset to 30. | 9 | +# 默认 on, 跳过 root 进程 |
| 10 | -borrow.timeout=10 | 10 | +filter_root_process=true |
| 11 | +# 决策周期, 借用/归还的执行间隔 (可配范围 1-3600s) | ||
| 12 | +schedule_interval=5 | ||
| 13 | +# 节点空闲触发借用阈值 (f), 由容量规划推导 f = S − k×d×r, 可配范围 1-4096 GB, 越界回退默认 15, | ||
| 14 | +# 建议不超过本机最大内存, 且大于 emergency_free_threshold | ||
| 15 | +pressing_free_threshold=15 | ||
| 16 | +# 借用超时, 每 128MB 粒度的耗时 (ms), 可配范围 1-1800000 (30 分钟), 越界回退默认 1000 | ||
| 17 | +borrow.timeout=1000 | ||
| 18 | +# 是否必须同平面借用 (true=必须, 严格过滤; false=优先, 评分加权) | ||
| 19 | +borrow.must_same_plane=false | ||
| 20 | + | ||
| 21 | +# 主动归还 (可选, 均有默认值) | ||
| 22 | +[process_mem.return] | ||
| 23 | +# 连续快速检测周期数: 仅紧急 fast 轮询(200ms)期间记录节点 free, 每 n 次结算一次取最小值, | ||
| 24 | +# 窗口最小 free > pressing_free_threshold 时触发主动归还 (默认 300×200ms=60s 观察期), 可配范围 1-4096, 越界回退默认 300 | ||
| 25 | +observe_cycles=300 | ||
| 26 | + | ||
| 27 | +# OOM 快速检测 (可选, 均有默认值) | ||
| 28 | +[process_mem.oom] | ||
| 29 | +# 紧急状态轮询间隔 (ms), 可配范围 50-60000, 越界回退默认 200 | ||
| 30 | +collect_node_interval=200 | ||
| 31 | +# 紧急借用/转嫁阈值 (GB), 可配范围 1-4096, 越界回退默认 5, 建议小于 pressing_free_threshold | ||
| 32 | +emergency_free_threshold=5 | ||
| @@ -94,6 +94,12 @@ api.timeout=1800 | |||
| 94 | # obmm.memory.offline.timeout=100 | 94 | # obmm.memory.offline.timeout=100 |
| 95 | # When set to true, OS SEI degradation is enabled on the borrowing node after the first memory borrow. Any invalid value will be considered as false. | 95 | # When set to true, OS SEI degradation is enabled on the borrowing node after the first memory borrow. Any invalid value will be considered as false. |
| 96 | sei.enable=false | 96 | sei.enable=false |
| 97 | +# Phase 3 process_mem: Max lend capacity per node, in GB. The scheduler checks that | ||
| 98 | +# lender.totalLent + borrowAmount ≤ nodeMaxLend before selecting a lender. | ||
| 99 | +# 调度器配置 (reuse UBSE mem scheduler): 节点最大借出量 (GB), lender 借出总上限 | ||
| 100 | +# 可配范围 0-65535, 0 或不配置 = 不限制, 越界回退 0 (不限制) | ||
| 101 | +# 默认不限制(注释), 按需由部署方显式开启 | ||
| 102 | +# scheduler.node_max_lend_gb=0 | ||
| 97 | 103 | ||
| 98 | [ubse.urma] | 104 | [ubse.urma] |
| 99 | # Topology mode used when UBSE pushes topology to URMA UVS in CLOS networking. | 105 | # Topology mode used when UBSE pushes topology to URMA UVS in CLOS networking. |
| @@ -856,7 +856,7 @@ ubsectl display process-mem -t <type> | |||
| 856 | 856 | ||
| 857 | | 参数名 | 说明 | 取值 | | 857 | | 参数名 | 说明 | 取值 | |
| 858 | |------|------|------| | 858 | |------|------|------| |
| 859 | -| -t<br/>--type | 必选,查询的进程内存配置信息类型 | config | | 859 | +| -t<br/>--type | 必选,查询的进程内存配置信息类型 | config:按配置条目展示<br/>proc_detail:按当前纳管进程展开展示 | |
| 860 | 860 | ||
| 861 | **约束限制** | 861 | **约束限制** |
| 862 | 862 | ||
| @@ -865,24 +865,31 @@ ubsectl display process-mem -t <type> | |||
| 865 | 865 | ||
| 866 | **输出信息说明** | 866 | **输出信息说明** |
| 867 | 867 | ||
| 868 | -| 字段名 | 字段描述 | 字段取值 | | 868 | +| 字段名 | 字段描述 | 字段取值 | |
| 869 | -| -------------------- | ----------------------- | -------- | | 869 | +| ----------- | ------------------------ | -------------- | |
| 870 | -| pid | 进程ID | 整数 | | 870 | +| PID | 进程ID,配置条目未匹配到进程时为 N/A | 整数 / N/A | |
| 871 | -| evictThreshold | 迁出阈值(%) | 整数 | | 871 | +| Name | 进程名 | 字符串 | |
| 872 | -| targetEvictThreshold | 预期迁出比例(%) | 整数 | | 872 | +| Size | 进程期望最大内存大小 | 如 1G、512M、1.5G | |
| 873 | -| reclaimThreshold | 迁回阈值(%) | 整数 | | 873 | +| RemoteRatio | 远端内存占比上限 | 0.00-1.00 | |
| 874 | -| totalMemoryUsage | 进程期望总内存大小 | 整数 | | ||
| 875 | -| srcNuma | 本地NUMA节点ID(可选) | 整数 | | ||
| 876 | 874 | ||
| 877 | **示例** | 875 | **示例** |
| 878 | 876 | ||
| 879 | ```bash | 877 | ```bash |
| 878 | +# 查看配置条目 | ||
| 880 | $ ubsectl display process-mem -t config | 879 | $ ubsectl display process-mem -t config |
| 881 | --------------------------------------------------------------------------------------------------- | 880 | --------------------------------------------------------------------------------------------------- |
| 882 | -pid evictThreshold targetEvictThreshold reclaimThreshold totalMemoryUsage srcNuma | 881 | +PID Name Size RemoteRatio |
| 883 | --------------------------------------------------------------------------------------------------- | 882 | --------------------------------------------------------------------------------------------------- |
| 884 | -1234 80 50 30 1073741824 1 | 883 | +1234 N/A 1G 0.50 |
| 885 | -5678 80 50 30 1073741824 N/A | 884 | +N/A myapp 512M 0.30 |
| 885 | +--------------------------------------------------------------------------------------------------- | ||
| 886 | + | ||
| 887 | +# 查看按当前纳管进程展开的详情(name 配置会展开为实际进程) | ||
| 888 | +$ ubsectl display process-mem -t proc_detail | ||
| 889 | +--------------------------------------------------------------------------------------------------- | ||
| 890 | +PID Name Size RemoteRatio | ||
| 891 | +--------------------------------------------------------------------------------------------------- | ||
| 892 | +1234 myapp 1G 0.50 | ||
| 886 | --------------------------------------------------------------------------------------------------- | 893 | --------------------------------------------------------------------------------------------------- |
| 887 | ``` | 894 | ``` |
| 888 | 895 | ||
| @@ -890,50 +897,44 @@ pid evictThreshold targetEvictThreshold reclaimThreshold totalMemo | |||
| 890 | 897 | ||
| 891 | **描述** | 898 | **描述** |
| 892 | 899 | ||
| 893 | -修改指定进程的内存配置信息,包括迁出阈值、预期迁出比例、迁回阈值以及进程的期望内存大小。当进程存在子进程时,子进程会继承父进程的内存使用配置。 | 900 | +修改指定进程(或进程名匹配的所有进程)的内存配置信息,包括期望最大内存大小与远端内存占比上限。当进程存在子进程时,子进程会继承父进程的内存使用配置。 |
| 894 | 901 | ||
| 895 | **用法** | 902 | **用法** |
| 896 | 903 | ||
| 897 | ```shell | 904 | ```shell |
| 898 | -ubsectl change process-mem -p <pid> -e <evict-thresh> -t <target-evict-thresh> -r <reclaim-thresh> -s <size> [--src-numa <numa-id>] | 905 | +ubsectl change process-mem (-p <pid> | -n <name>) -s <size> -r <remote-ratio> |
| 899 | ``` | 906 | ``` |
| 900 | 907 | ||
| 901 | **输入参数说明** | 908 | **输入参数说明** |
| 902 | 909 | ||
| 903 | | 参数名 | 说明 | 取值 | | 910 | | 参数名 | 说明 | 取值 | |
| 904 | |------|------|------| | 911 | |------|------|------| |
| 905 | -| -p<br/>--pid | 必选,目标进程的PID | 整数,范围 1-4194304 | | 912 | +| -p<br/>--pid | 与 -n 二选一,目标进程的PID | 整数,范围 1-4194304 | |
| 906 | -| -e<br/>--evict-thresh | 必选,迁出阈值(%)。当进程总内存使用超过此比例时,触发迁出动作 | 整数,范围 1-100 | | 913 | +| -n<br/>--name | 与 -p 二选一,目标进程名(comm),匹配所有同名进程 | 字符串,最长 15 字符,仅含字母/数字/点/冒号/下划线/连字符 | |
| 907 | -| -t<br/>--target-evict-thresh | 必选,预期迁出比例(%)。迁出后远端内存占总内存的目标比例 | 整数,范围 1-100 | | 914 | +| -s<br/>--size | 必选,进程期望最大内存大小 | 格式为数字+单位(B/K/M/G,按1024换算),支持最多两位小数,范围 128M~32G,如 512M、1G、1.5G | |
| 908 | -| -r<br/>--reclaim-thresh | 必选,迁回阈值(%)。当进程总内存使用低于此比例时,将远端内存全部迁回并释放 | 整数,范围 1-100,需比--evict-thresh至少小5 | | 915 | +| -r<br/>--remote-ratio | 必选,远端内存占比上限 | 小数,范围 0.0-1.0 | |
| 909 | -| -s<br/>--size | 必选,进程期望总内存大小 | 格式为数字+单位(B/K/M/G),支持最多两位小数,范围 128M~32G,如 512M、1G、1.5G | | ||
| 910 | -| -sn<br/>--src-numa | 可选,本地NUMA节点ID。同平面socket会被选为借出socket | 整数 | | ||
| 911 | 916 | ||
| 912 | **约束限制** | 917 | **约束限制** |
| 913 | 918 | ||
| 914 | 1. ubsectl只能在root,ubse用户中运行 | 919 | 1. ubsectl只能在root,ubse用户中运行 |
| 915 | 2. 需要目标节点的 process_mem 插件正常启动,否则配置无法生效 | 920 | 2. 需要目标节点的 process_mem 插件正常启动,否则配置无法生效 |
| 916 | -3. evict-thresh必须比reclaim-thresh至少大5,避免震荡 | 921 | +3. -p 与 -n 互斥,必须二选一 |
| 917 | 4. 当进程已有子进程时,子进程会继承该配置 | 922 | 4. 当进程已有子进程时,子进程会继承该配置 |
| 918 | 923 | ||
| 919 | **输出信息说明** | 924 | **输出信息说明** |
| 920 | 925 | ||
| 921 | -Set successfully / 错误信息 | 926 | +Set process-mem config successfully / 错误信息 |
| 922 | 927 | ||
| 923 | **示例** | 928 | **示例** |
| 924 | 929 | ||
| 925 | ```bash | 930 | ```bash |
| 926 | -# 配置PID为1234的进程,迁出阈值80%,预期迁出比例50%,迁回阈值30%,期望内存1G | 931 | +# 配置PID为1234的进程,期望最大内存1G,远端占比50% |
| 927 | -$ ubsectl change process-mem -p 1234 -e 80 -t 50 -r 30 -s 1G | 932 | +$ ubsectl change process-mem -p 1234 -s 1G -r 0.5 |
| 928 | -Set successfully | 933 | +Set process-mem config successfully |
| 929 | 934 | ||
| 930 | -# 指定本地NUMA节点 | 935 | +# 按进程名配置(匹配当前节点所有同名进程) |
| 931 | -$ ubsectl change process-mem -p 1234 -e 80 -t 50 -r 30 -s 1G --src-numa 0 | 936 | +$ ubsectl change process-mem -n myapp -s 512M -r 0.3 |
| 932 | -Set successfully | 937 | +Set process-mem config successfully |
| 933 | - | ||
| 934 | -# 参数校验失败 | ||
| 935 | -$ ubsectl change process-mem -p 1234 -e 80 -t 50 -r 76 -s 1G | ||
| 936 | -evict-thresh must be at least 5 higher than reclaim-thresh to avoid oscillation | ||
| 937 | ``` | 938 | ``` |
| 938 | 939 | ||
| 939 | ### 移除进程内存配置 | 940 | ### 移除进程内存配置 |
| @@ -945,19 +946,21 @@ evict-thresh must be at least 5 higher than reclaim-thresh to avoid oscillation | |||
| 945 | **用法** | 946 | **用法** |
| 946 | 947 | ||
| 947 | ```shell | 948 | ```shell |
| 948 | -ubsectl remove process-mem -p <pid> | 949 | +ubsectl remove process-mem (-p <pid> | -n <name>) |
| 949 | ``` | 950 | ``` |
| 950 | 951 | ||
| 951 | **输入参数说明** | 952 | **输入参数说明** |
| 952 | 953 | ||
| 953 | | 参数名 | 说明 | 取值 | | 954 | | 参数名 | 说明 | 取值 | |
| 954 | |------|------|------| | 955 | |------|------|------| |
| 955 | -| -p<br/>--pid | 必选,目标进程的PID | 整数,范围 1-4194304 | | 956 | +| -p<br/>--pid | 与 -n 互斥,必选其一,目标进程的PID | 整数,范围 1-4194304 | |
| 957 | +| -n<br/>--name | 与 -p 互斥,必选其一,目标进程名 | 字符串,匹配当前节点所有同名进程 | | ||
| 956 | 958 | ||
| 957 | **约束限制** | 959 | **约束限制** |
| 958 | 960 | ||
| 959 | 1. ubsectl只能在root,ubse用户中运行 | 961 | 1. ubsectl只能在root,ubse用户中运行 |
| 960 | 2. 需要目标节点的 process_mem 插件正常启动,否则配置移除操作无法执行 | 962 | 2. 需要目标节点的 process_mem 插件正常启动,否则配置移除操作无法执行 |
| 963 | +3. -p 与 -n 互斥,必须二选一 | ||
| 961 | 964 | ||
| 962 | **输出信息说明** | 965 | **输出信息说明** |
| 963 | 966 | ||
| @@ -966,8 +969,13 @@ Unset successfully | |||
| 966 | **示例** | 969 | **示例** |
| 967 | 970 | ||
| 968 | ```bash | 971 | ```bash |
| 972 | +# 移除PID为1234的进程配置 | ||
| 969 | $ ubsectl remove process-mem -p 1234 | 973 | $ ubsectl remove process-mem -p 1234 |
| 970 | Unset successfully | 974 | Unset successfully |
| 975 | + | ||
| 976 | +# 按进程名移除配置(匹配当前节点所有同名进程) | ||
| 977 | +$ ubsectl remove process-mem -n myapp | ||
| 978 | +Unset successfully | ||
| 971 | ``` | 979 | ``` |
| 972 | 980 | ||
| 973 | ### 导入共享内存 | 981 | ### 导入共享内存 |
| @@ -120,6 +120,7 @@ section取值:[ubse.memory] | |||
| 120 | | 12 | scheduler.mode | 内存调度模式,控制 socket NUMA 选择策略。 | 默认值:free-priority<br>取值范围:free-priority / reliability-priority / performance-priority<br>如果取值超过范围,则取默认值free-priority。 | - **free-priority**:优先选择剩余内存最多的节点。<br>- **reliability-priority**:优先复用已有借用关系并均衡各节点的借出数。<br>- **performance-priority**:均衡各节点带宽利用率,兼顾时延、内存利用率和可靠性。<br>- 当不配置时,检查已弃用的 `lender.balance`(第 6 项),兼容方式如下:<br> lender.balance=true 时取值 reliability-priority,lender.balance=false 取值 free-priority。 | | 120 | | 12 | scheduler.mode | 内存调度模式,控制 socket NUMA 选择策略。 | 默认值:free-priority<br>取值范围:free-priority / reliability-priority / performance-priority<br>如果取值超过范围,则取默认值free-priority。 | - **free-priority**:优先选择剩余内存最多的节点。<br>- **reliability-priority**:优先复用已有借用关系并均衡各节点的借出数。<br>- **performance-priority**:均衡各节点带宽利用率,兼顾时延、内存利用率和可靠性。<br>- 当不配置时,检查已弃用的 `lender.balance`(第 6 项),兼容方式如下:<br> lender.balance=true 时取值 reliability-priority,lender.balance=false 取值 free-priority。 | |
| 121 | | 13 | bandwidth.tolerance | performance-priority 模式下带宽均衡的容忍度阈值。 | 默认值:2 × block.size(通常 256 MB)<br>单位:MB<br>如果取值小于block.size,则取默认值。 | 候选 socket 的当前借出量低于平均值至少该阈值时得分归零(最优),介于平均值与该阈值之间时得分线性插值在 (0,1] 范围。 | | 121 | | 13 | bandwidth.tolerance | performance-priority 模式下带宽均衡的容忍度阈值。 | 默认值:2 × block.size(通常 256 MB)<br>单位:MB<br>如果取值小于block.size,则取默认值。 | 候选 socket 的当前借出量低于平均值至少该阈值时得分归零(最优),介于平均值与该阈值之间时得分线性插值在 (0,1] 范围。 | |
| 122 | | 14 | sei.enable | 控制内存借用时是否启用OS的SEI降级功能。 | 默认值:false<br>取值范围:[true,false]<br>如果取值超过范围或非法,则取默认值false。 | - 仅在借入节点生效。<br>- 依赖 /etc/sudoers.d/ubse-sei 授予 ubse 用户 sysctl 提权权限。<br>- sei.enable=true 时,首次借用内存后自动执行 sysctl -w kernel.arm64_sync_sei=1 开启OS的SEI降级功能;末次归还后自动关闭OS的SEI降级功能。<br>- sei.enable=false时不会触发OS的SEI降级功能,UB断链导致的内存错误可能触发 OS Panic。 | | 122 | | 14 | sei.enable | 控制内存借用时是否启用OS的SEI降级功能。 | 默认值:false<br>取值范围:[true,false]<br>如果取值超过范围或非法,则取默认值false。 | - 仅在借入节点生效。<br>- 依赖 /etc/sudoers.d/ubse-sei 授予 ubse 用户 sysctl 提权权限。<br>- sei.enable=true 时,首次借用内存后自动执行 sysctl -w kernel.arm64_sync_sei=1 开启OS的SEI降级功能;末次归还后自动关闭OS的SEI降级功能。<br>- sei.enable=false时不会触发OS的SEI降级功能,UB断链导致的内存错误可能触发 OS Panic。 | |
| 123 | +| 15 | scheduler.node_max_lend_gb | 调度器节点最大借出量,即节点作为 lender 的借出总量上限。 | 默认值:用#注释(不限制)<br>单位:GB<br>取值范围:[0, 65535]<br>配置为 0 或不配置时表示不限制,取值超过范围则回退为 0(不限制)。 | - 调度器在选择 lender 前校验 `lender.totalLent + 本次借出量 ≤ node_max_lend_gb`,超出上限的节点不会被选中。<br>- 所有节点的配置需保持一致。<br>- 默认不限制,按需由部署方显式开启。 | | ||
| 123 | 124 | ||
| 124 | ## URMA配置说明 | 125 | ## URMA配置说明 |
| 125 | 126 | ||
| @@ -20,6 +20,22 @@ _ubse_mem_current_type() { | |||
| 20 | fi | 20 | fi |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | +# process-mem: 补全运行中进程 PID(/proc 扫描) | ||
| 24 | +_ubse_proc_pids() { | ||
| 25 | + compgen -W "$(ls /proc 2>/dev/null | grep -E '^[0-9]+$' | sort -n)" -- "$1" | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +# process-mem: 补全运行中进程名(/proc/<pid>/comm,去重) | ||
| 29 | +_ubse_proc_names() { | ||
| 30 | + local names="" | ||
| 31 | + for name in /proc/[0-9]*/comm; do | ||
| 32 | + local comm | ||
| 33 | + read -r comm < "$name" 2>/dev/null | ||
| 34 | + [ -n "$comm" ] && names="$names $comm" | ||
| 35 | + done | ||
| 36 | + compgen -W "$(echo $names | tr ' ' '\n' | sort -u | tr '\n' ' ')" -- "$1" | ||
| 37 | +} | ||
| 38 | + | ||
| 23 | function _ubse_commond_completion() { | 39 | function _ubse_commond_completion() { |
| 24 | COMPREPLY=() | 40 | COMPREPLY=() |
| 25 | 41 | ||
| @@ -28,7 +44,7 @@ function _ubse_commond_completion() { | |||
| 28 | local prev=${COMP_WORDS[COMP_CWORD-2]} | 44 | local prev=${COMP_WORDS[COMP_CWORD-2]} |
| 29 | 45 | ||
| 30 | commands='create display import delete check change remove detach attach' | 46 | commands='create display import delete check change remove detach attach' |
| 31 | - display_types='topo memory cluster cert node urma urma-qos process-mem' | 47 | + display_types='topo memory cluster node urma urma-qos process-mem' |
| 32 | create_types='memory urma-qos' | 48 | create_types='memory urma-qos' |
| 33 | delete_types='memory urma-qos' | 49 | delete_types='memory urma-qos' |
| 34 | check_types='memory' | 50 | check_types='memory' |
| @@ -54,7 +70,7 @@ commands='create display import delete check change remove detach attach' | |||
| 54 | return 0 | 70 | return 0 |
| 55 | ;; | 71 | ;; |
| 56 | 'change'|'remove') | 72 | 'change'|'remove') |
| 57 | - COMPREPLY=( $(compgen -W 'cert memory process-mem' -- ${cur}) ) | 73 | + COMPREPLY=( $(compgen -W 'cert process-mem' -- ${cur}) ) |
| 58 | return 0 | 74 | return 0 |
| 59 | ;; | 75 | ;; |
| 60 | 'check') | 76 | 'check') |
| @@ -135,8 +151,8 @@ commands='create display import delete check change remove detach attach' | |||
| 135 | COMPREPLY=( $(compgen -W '--ca-crl-file' -- ${cur}) ) | 151 | COMPREPLY=( $(compgen -W '--ca-crl-file' -- ${cur}) ) |
| 136 | return 0 | 152 | return 0 |
| 137 | ;; | 153 | ;; |
| 138 | - 'memory'|'process-mem') | 154 | + 'process-mem') |
| 139 | - COMPREPLY=( $(compgen -W '--pid --evict-thresh --target-evict-thresh --reclaim-thresh --size --src-numa' -- ${cur}) ) | 155 | + COMPREPLY=( $(compgen -W '--pid --name --size --remote-ratio' -- ${cur}) ) |
| 140 | return 0 | 156 | return 0 |
| 141 | ;; | 157 | ;; |
| 142 | '*') | 158 | '*') |
| @@ -147,8 +163,8 @@ commands='create display import delete check change remove detach attach' | |||
| 147 | 163 | ||
| 148 | 'remove') | 164 | 'remove') |
| 149 | case ${COMP_WORDS[2]} in | 165 | case ${COMP_WORDS[2]} in |
| 150 | - 'memory'|'process-mem') | 166 | + 'process-mem') |
| 151 | - COMPREPLY=( $(compgen -W '--pid' -- ${cur}) ) | 167 | + COMPREPLY=( $(compgen -W '--pid --name' -- ${cur}) ) |
| 152 | return 0 | 168 | return 0 |
| 153 | ;; | 169 | ;; |
| 154 | '*') | 170 | '*') |
| @@ -274,8 +290,8 @@ commands='create display import delete check change remove detach attach' | |||
| 274 | COMPREPLY=( $(compgen -W '-l' -- ${cur}) ) | 290 | COMPREPLY=( $(compgen -W '-l' -- ${cur}) ) |
| 275 | return 0 | 291 | return 0 |
| 276 | ;; | 292 | ;; |
| 277 | - 'memory'|'process-mem') | 293 | + 'process-mem') |
| 278 | - COMPREPLY=( $(compgen -W '-p -e -t -r -s -sn' -- ${cur}) ) | 294 | + COMPREPLY=( $(compgen -W '-p -n -s -r' -- ${cur}) ) |
| 279 | return 0 | 295 | return 0 |
| 280 | ;; | 296 | ;; |
| 281 | '*') | 297 | '*') |
| @@ -286,8 +302,8 @@ commands='create display import delete check change remove detach attach' | |||
| 286 | 302 | ||
| 287 | 'remove') | 303 | 'remove') |
| 288 | case ${COMP_WORDS[2]} in | 304 | case ${COMP_WORDS[2]} in |
| 289 | - 'memory'|'process-mem') | 305 | + 'process-mem') |
| 290 | - COMPREPLY=( $(compgen -W '-p' -- ${cur}) ) | 306 | + COMPREPLY=( $(compgen -W '-p -n' -- ${cur}) ) |
| 291 | return 0 | 307 | return 0 |
| 292 | ;; | 308 | ;; |
| 293 | '*') | 309 | '*') |
| @@ -420,6 +436,33 @@ if [[ ${COMP_WORDS[0]} == *ubsectl ]] && \ | |||
| 420 | return 0 | 436 | return 0 |
| 421 | fi | 437 | fi |
| 422 | 438 | ||
| 439 | + # process-mem: change/remove 的 -p/--pid 补全运行中 PID | ||
| 440 | + if [[ ${COMP_WORDS[0]} == *ubsectl ]] && \ | ||
| 441 | + [[ ${COMP_WORDS[1]} == change || ${COMP_WORDS[1]} == remove ]] && \ | ||
| 442 | + [[ ${COMP_WORDS[2]} == process-mem ]] && \ | ||
| 443 | + [[ "${cmd}" == '-p' || "${cmd}" == '--pid' ]]; then | ||
| 444 | + COMPREPLY=( $(_ubse_proc_pids "$cur") ) | ||
| 445 | + return 0 | ||
| 446 | + fi | ||
| 447 | + | ||
| 448 | + # process-mem: change/remove 的 -n/--name 补全运行中进程名 | ||
| 449 | + if [[ ${COMP_WORDS[0]} == *ubsectl ]] && \ | ||
| 450 | + [[ ${COMP_WORDS[1]} == change || ${COMP_WORDS[1]} == remove ]] && \ | ||
| 451 | + [[ ${COMP_WORDS[2]} == process-mem ]] && \ | ||
| 452 | + [[ "${cmd}" == '-n' || "${cmd}" == '--name' ]]; then | ||
| 453 | + COMPREPLY=( $(_ubse_proc_names "$cur") ) | ||
| 454 | + return 0 | ||
| 455 | + fi | ||
| 456 | + | ||
| 457 | + # process-mem: change 的 -r/--remote-ratio 补全常用比值(对应 -p 0 1 枚举模式) | ||
| 458 | + if [[ ${COMP_WORDS[0]} == *ubsectl ]] && \ | ||
| 459 | + [[ ${COMP_WORDS[1]} == change ]] && \ | ||
| 460 | + [[ ${COMP_WORDS[2]} == process-mem ]] && \ | ||
| 461 | + [[ "${cmd}" == '-r' || "${cmd}" == '--remote-ratio' ]]; then | ||
| 462 | + COMPREPLY=( $(compgen -W '0.3 0.5 0.8 1.0' -- ${cur}) ) | ||
| 463 | + return 0 | ||
| 464 | + fi | ||
| 465 | + | ||
| 423 | } | 466 | } |
| 424 | 467 | ||
| 425 | complete -F _ubse_commond_completion ubsectl | 468 | complete -F _ubse_commond_completion ubsectl |
| @@ -4,6 +4,7 @@ set(PROCESS_MEM_SOURCES | |||
| 4 | process_mem_pid_info_manager.cpp | 4 | process_mem_pid_info_manager.cpp |
| 5 | process_mem_pid_collect.cpp | 5 | process_mem_pid_collect.cpp |
| 6 | process_mem_pid_config_manager.cpp | 6 | process_mem_pid_config_manager.cpp |
| 7 | + process_mem_pid_decision.cpp | ||
| 7 | process_mem_plugin.cpp | 8 | process_mem_plugin.cpp |
| 8 | ) | 9 | ) |
| 9 | add_library(process_mem SHARED ${PROCESS_MEM_SOURCES}) | 10 | add_library(process_mem SHARED ${PROCESS_MEM_SOURCES}) |
| @@ -11,7 +11,9 @@ | |||
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | -#include <set> | 14 | +#include <algorithm> |
| 15 | + | ||
| 16 | + | ||
| 15 | 17 | ||
| 16 | 18 | ||
| 17 | 19 | ||
| @@ -28,51 +30,14 @@ | |||
| 28 | 30 | ||
| 29 | 31 | ||
| 30 | 32 | ||
| 33 | + | ||
| 31 | 34 | ||
| 35 | + | ||
| 32 | 36 | ||
| 33 | namespace process_mem::pid::bridge { | 37 | namespace process_mem::pid::bridge { |
| 34 | UBSE_DEFINE_THIS_MODULE("process_mem"); | 38 | UBSE_DEFINE_THIS_MODULE("process_mem"); |
| 35 | -const std::string PROCESS_MEM_PERMISSION = ""; | 39 | +// 具名权限对象, 便于在 [auth.role.default] 中按角色授权; 未授权时非内置用户 fail-closed |
| 36 | - | 40 | +const std::string PROCESS_MEM_PERMISSION = "process_mem"; |
| 37 | -uint32_t ProcessMemPidBridge::MemoryBorrow(def::ProcessMemPidInfo& pidInfo, | ||
| 38 | - const ubse::mem::controller::UbseMemBorrower& borrower, | ||
| 39 | - const MemoryBorrowRequest& request, | ||
| 40 | - ubse::mem::controller::UbseMemNumaDesc& borrowInfo) | ||
| 41 | -{ | ||
| 42 | - ubse::mem::controller::UbseMemNumaCreateOpt opt; | ||
| 43 | - constexpr size_t highWatermark = 100; | ||
| 44 | - opt.highWatermark = highWatermark; | ||
| 45 | - opt.size = request.size; | ||
| 46 | - if (memcpy_s(opt.usrInfo, ubse::mem::controller::UBSE_MAX_USR_INFO_LEN, request.usrInfo, | ||
| 47 | - ubse::mem::controller::UBSE_MAX_USR_INFO_LEN) != EOK) { | ||
| 48 | - UBSE_LOG_ERROR << "memcpy_s usrInfo to opt failed"; | ||
| 49 | - return UBSE_ERROR; | ||
| 50 | - } | ||
| 51 | - if (pidInfo.memBorrowInfo.exportSlotId == -1) { | ||
| 52 | - auto errCode = UbseMemNumaCreate(request.name, borrower, opt, borrowInfo); | ||
| 53 | - if (errCode != UBSE_OK) { | ||
| 54 | - UBSE_LOG_ERROR << "UbseMemNumaCreate failed"; | ||
| 55 | - return errCode; | ||
| 56 | - } | ||
| 57 | - } else { | ||
| 58 | - ubse::mem::controller::UbseMemNumaCandidateOpt candidateOpt{}; | ||
| 59 | - candidateOpt.highWatermark = highWatermark; | ||
| 60 | - candidateOpt.size = request.size; | ||
| 61 | - if (memcpy_s(candidateOpt.usrInfo, ubse::mem::controller::UBSE_MAX_USR_INFO_LEN, request.usrInfo, | ||
| 62 | - ubse::mem::controller::UBSE_MAX_USR_INFO_LEN) != EOK) { | ||
| 63 | - UBSE_LOG_ERROR << "memcpy_s usrInfo to candidateOpt failed"; | ||
| 64 | - return UBSE_ERROR; | ||
| 65 | - } | ||
| 66 | - candidateOpt.slotIds.push_back(std::to_string(pidInfo.memBorrowInfo.exportSlotId)); | ||
| 67 | - auto errCode = UbseMemNumaCreateWithCandidate(request.name, borrower, candidateOpt, borrowInfo); | ||
| 68 | - if (errCode != UBSE_OK) { | ||
| 69 | - UBSE_LOG_ERROR << "UbseMemNumaCreateWithCandidate failed"; | ||
| 70 | - return errCode; | ||
| 71 | - } | ||
| 72 | - } | ||
| 73 | - UBSE_LOG_INFO << borrowInfo.name << "UbseMemory Borrow " << borrowInfo.size << " Success"; | ||
| 74 | - return UBSE_OK; | ||
| 75 | -} | ||
| 76 | 41 | ||
| 77 | uint32_t ProcessMemPidBridge::MemoryReturn(const std::string& name) | 42 | uint32_t ProcessMemPidBridge::MemoryReturn(const std::string& name) |
| 78 | { | 43 | { |
| @@ -90,49 +55,8 @@ uint32_t ProcessMemPidBridge::MemoryReturn(const std::string& name) | |||
| 90 | return UBSE_OK; | 55 | return UBSE_OK; |
| 91 | } | 56 | } |
| 92 | 57 | ||
| 93 | -void GetLentInfo(const ubse::mem::controller::UbseNumaMemoryDebtInfo& info, | ||
| 94 | - const ubse::mem::controller::UbseMemNumaDesc& desc, uint32_t& socketId, uint64_t& numaId, bool& flag) | ||
| 95 | -{ | ||
| 96 | - if (info.name == desc.name) { | ||
| 97 | - if (info.borrowNodeId == std::to_string(desc.importNode.slotId)) { | ||
| 98 | - auto socketList = info.lentSocketIdList; | ||
| 99 | - auto lentNumaIdList = info.lentNumaIdList; | ||
| 100 | - if (!info.lentSocketIdList.empty() || !info.lentNumaIdList.empty()) { | ||
| 101 | - flag = true; | ||
| 102 | - socketId = info.lentSocketIdList[0]; | ||
| 103 | - numaId = info.lentNumaIdList[0]; | ||
| 104 | - } | ||
| 105 | - } | ||
| 106 | - } | ||
| 107 | -} | ||
| 108 | - | ||
| 109 | -uint32_t ProcessMemPidBridge::GetRemoteNumaSocketInfo(const ubse::mem::controller::UbseMemNumaDesc& desc, | ||
| 110 | - uint32_t& socketId, uint64_t& numaId) | ||
| 111 | -{ | ||
| 112 | - std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo> debtInfo{}; | ||
| 113 | - auto ret = UbseGetNumaMemDebtInfoWithNode(std::to_string(desc.exportNode.slotId), debtInfo); | ||
| 114 | - constexpr int retryTime = 3; | ||
| 115 | - constexpr int retryIntervalSec = 2; | ||
| 116 | - int remainRetry = retryTime; | ||
| 117 | - while (ret != UBSE_OK && ret != UBSE_MEMCONTROLLER_ERROR_PAR_SUCCESS && remainRetry > 0) { | ||
| 118 | - sleep(retryIntervalSec); | ||
| 119 | - ret = UbseGetNumaMemDebtInfoWithNode(std::to_string(desc.exportNode.slotId), debtInfo); | ||
| 120 | - --remainRetry; | ||
| 121 | - } | ||
| 122 | - auto flag = false; | ||
| 123 | - for (const auto& info : debtInfo) { | ||
| 124 | - GetLentInfo(info, desc, socketId, numaId, flag); | ||
| 125 | - } | ||
| 126 | - | ||
| 127 | - if (flag) { | ||
| 128 | - return UBSE_OK; | ||
| 129 | - } | ||
| 130 | - return UBSE_ERROR; | ||
| 131 | -} | ||
| 132 | - | ||
| 133 | namespace { | 58 | namespace { |
| 134 | - | 59 | +ubse::com::UbseComEndpoint GetProcessMemReturnEndpoint(uint16_t opCode, const std::string& nodeId) |
| 135 | -ubse::com::UbseComEndpoint GetProcessMemFaultComEndpoint(uint16_t opCode, const std::string& nodeId) | ||
| 136 | { | 60 | { |
| 137 | return ubse::com::UbseComEndpoint{ | 61 | return ubse::com::UbseComEndpoint{ |
| 138 | .moduleId = static_cast<uint16_t>(ubse::com::UbseModuleCode::UBSE_MEM_FAULT), | 62 | .moduleId = static_cast<uint16_t>(ubse::com::UbseModuleCode::UBSE_MEM_FAULT), |
| @@ -140,151 +64,65 @@ ubse::com::UbseComEndpoint GetProcessMemFaultComEndpoint(uint16_t opCode, const | |||
| 140 | .address = nodeId, | 64 | .address = nodeId, |
| 141 | }; | 65 | }; |
| 142 | } | 66 | } |
| 143 | - | ||
| 144 | -uint32_t SendProcessMemNodeFaultToNode(const std::string& nodeId, const std::string& faultNodeId) | ||
| 145 | -{ | ||
| 146 | - UBSE_LOG_INFO << "[PROCESS_MEM] Sending node fault notify to nodeId=" << nodeId << ", faultNodeId=" << faultNodeId; | ||
| 147 | - | ||
| 148 | - ubse::serial::UbseSerialization output; | ||
| 149 | - output << faultNodeId; | ||
| 150 | - if (!output.Check()) { | ||
| 151 | - UBSE_LOG_ERROR << "[PROCESS_MEM] Failed to serialize node fault notify message."; | ||
| 152 | - return UBSE_ERROR_SERIALIZE_FAILED; | ||
| 153 | - } | ||
| 154 | - | ||
| 155 | - uint32_t remoteResult = UBSE_OK; | ||
| 156 | - auto respHandler = [&remoteResult](void*, const UbseByteBuffer& respData, uint32_t statusCode) -> void { | ||
| 157 | - if (statusCode != UBSE_OK) { | ||
| 158 | - UBSE_LOG_ERROR << "[PROCESS_MEM] Node fault notify RPC failed, statusCode=" << statusCode; | ||
| 159 | - remoteResult = statusCode; | ||
| 160 | - return; | ||
| 161 | - } | ||
| 162 | - ubse::serial::UbseDeSerialization input(respData.data, respData.len); | ||
| 163 | - input >> remoteResult; | ||
| 164 | - if (!input.Check()) { | ||
| 165 | - UBSE_LOG_ERROR << "[PROCESS_MEM] Failed to deserialize node fault notify response."; | ||
| 166 | - remoteResult = UBSE_ERROR; | ||
| 167 | - return; | ||
| 168 | - } | ||
| 169 | - UBSE_LOG_INFO << "[PROCESS_MEM] Node fault notify response, remoteResult=" << remoteResult; | ||
| 170 | - }; | ||
| 171 | - | ||
| 172 | - auto endpoint = GetProcessMemFaultComEndpoint( | ||
| 173 | - static_cast<uint16_t>(ubse::com::UbseMemFaultOpCode::UBSE_PROCESS_MEM_NODE_FAULT_NOTIFY), nodeId); | ||
| 174 | - UbseByteBuffer request{.data = output.GetBuffer(), .len = output.GetLength(), .freeFunc = nullptr}; | ||
| 175 | - auto ret = ubse::com::UbseRpcSend(endpoint, request, nullptr, respHandler); | ||
| 176 | - if (ret != UBSE_OK) { | ||
| 177 | - UBSE_LOG_ERROR << "[PROCESS_MEM] Failed to send node fault notify to " << nodeId << ", ret=" << ret; | ||
| 178 | - return ret; | ||
| 179 | - } | ||
| 180 | - return remoteResult; | ||
| 181 | -} | ||
| 182 | - | ||
| 183 | } // namespace | 67 | } // namespace |
| 184 | 68 | ||
| 185 | -uint32_t ProcessMemPidBridge::FaultHandler(ubse::ras::ALARM_FAULT_TYPE alarmFaultEvent, std::string faultInfo) | 69 | +uint32_t ProcessMemPidBridge::SendReturnRequestToNode(const std::string& nodeId, |
| 70 | + const std::vector<def::ReturnRequestItem>& items) | ||
| 186 | { | 71 | { |
| 187 | - const auto& lentNodeId = faultInfo; | 72 | + ubse::serial::UbseSerialization output; |
| 188 | - UBSE_LOG_INFO << "FaultHandler: event=" << alarmFaultEvent << ", lentNodeId=" << lentNodeId; | 73 | + output << ubse::serial::array_len_insert(items.size()); |
| 189 | - | 74 | + for (const auto& item : items) { |
| 190 | - // 1. 本地处理(如果Master自身也是借入节点,处理本地的PID) | 75 | + output << item.name << item.size; |
| 191 | - auto ret = manager::ProcessMemPidInfoManager::GetInstance().HandleNodeFaultEvent(lentNodeId); | 76 | + } |
| 192 | - if (ret != UBSE_OK) { | 77 | + if (!output.Check()) { |
| 193 | - UBSE_LOG_ERROR << "FaultHandler: HandleNodeFaultEvent failed, ret=" << ret; | 78 | + UBSE_LOG_ERROR << "SendReturnRequestToNode: serialization failed, nodeId=" << nodeId; |
| 194 | - return ret; | 79 | + return UBSE_ERROR; |
| 195 | } | 80 | } |
| 196 | 81 | ||
| 197 | - // 2. 通知其他借入节点处理故障 | 82 | + auto endpoint = GetProcessMemReturnEndpoint( |
| 198 | - ret = NotifyBorrowNodesOnFault(lentNodeId); | 83 | + static_cast<uint16_t>(ubse::com::UbseMemFaultOpCode::UBSE_PROCESS_MEM_RETURN_REQUEST), nodeId); |
| 84 | + UbseByteBuffer request{.data = output.GetBuffer(), .len = output.GetLength(), .freeFunc = nullptr}; | ||
| 85 | + auto ret = ubse::com::UbseRpcSend(endpoint, request, nullptr, [](void*, const UbseByteBuffer&, uint32_t) {}); | ||
| 199 | if (ret != UBSE_OK) { | 86 | if (ret != UBSE_OK) { |
| 200 | - UBSE_LOG_ERROR << "FaultHandler: NotifyBorrowNodesOnFault failed, ret=" << ret; | 87 | + UBSE_LOG_ERROR << "SendReturnRequestToNode: rpc send failed to " << nodeId << ", ret=" << ret; |
| 201 | - return ret; | ||
| 202 | } | 88 | } |
| 203 | - | 89 | + return ret; |
| 204 | - return UBSE_OK; | ||
| 205 | } | 90 | } |
| 206 | 91 | ||
| 207 | -uint32_t ProcessMemPidBridge::NotifyBorrowNodesOnFault(const std::string& lentNodeId) | 92 | +void ProcessMemPidBridge::ProcessMemReturnRequestHandler(const UbseByteBuffer& req, UbseByteBuffer& resp) |
| 208 | { | 93 | { |
| 209 | - // 查询该故障节点相关的全量账本信息,找出所有借入节点 | 94 | + ubse::serial::UbseDeSerialization deserializer{req.data, req.len}; |
| 210 | - std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo> debtInfos{}; | 95 | + ubse::serial::common_len itemCount = 0; |
| 211 | - constexpr int retryTime = 3; | 96 | + deserializer >> ubse::serial::array_len_capture(itemCount); |
| 212 | - constexpr int retryIntervalSec = 2; | 97 | + if (!deserializer.Check()) { |
| 213 | - int remainRetry = retryTime; | 98 | + UBSE_LOG_ERROR << "ProcessMemReturnRequestHandler: deserialize failed"; |
| 214 | - auto ret = UbseGetNumaMemDebtInfoWithNode(lentNodeId, debtInfos); | ||
| 215 | - while (ret != UBSE_OK && ret != UBSE_MEMCONTROLLER_ERROR_PAR_SUCCESS && remainRetry > 0) { | ||
| 216 | - UBSE_LOG_WARN << "[PROCESS_MEM] NotifyBorrowNodesOnFault: query debts retry, ret=" << ret | ||
| 217 | - << ", remainRetry=" << remainRetry; | ||
| 218 | - sleep(retryIntervalSec); | ||
| 219 | - debtInfos.clear(); | ||
| 220 | - ret = UbseGetNumaMemDebtInfoWithNode(lentNodeId, debtInfos); | ||
| 221 | - --remainRetry; | ||
| 222 | - } | ||
| 223 | - if (ret != UBSE_OK && ret != UBSE_MEMCONTROLLER_ERROR_PAR_SUCCESS) { | ||
| 224 | - UBSE_LOG_WARN << "[PROCESS_MEM] NotifyBorrowNodesOnFault: query debts for node " << lentNodeId | ||
| 225 | - << " failed after retry, ret=" << ret; | ||
| 226 | - return ret; | ||
| 227 | - } | ||
| 228 | - UBSE_LOG_INFO << "[PROCESS_MEM] NotifyBorrowNodesOnFault: query debts done, ret=" << ret | ||
| 229 | - << ", total=" << debtInfos.size(); | ||
| 230 | - | ||
| 231 | - // 获取当前节点ID,避免通知自身(自身已在FaultHandler中处理) | ||
| 232 | - auto currentNodeId = ubse::nodeController::UbseNodeController::GetInstance().GetCurrentNodeId(); | ||
| 233 | - | ||
| 234 | - // 收集所有与该故障节点相关的借入节点(排除自身) | ||
| 235 | - std::set<std::string> targetNodeIds; | ||
| 236 | - for (const auto& debtInfo : debtInfos) { | ||
| 237 | - if (debtInfo.lentNodeId != lentNodeId) { | ||
| 238 | - continue; | ||
| 239 | - } | ||
| 240 | - if (!debtInfo.borrowNodeId.empty() && debtInfo.borrowNodeId != currentNodeId) { | ||
| 241 | - targetNodeIds.insert(debtInfo.borrowNodeId); | ||
| 242 | - } | ||
| 243 | - } | ||
| 244 | - | ||
| 245 | - if (targetNodeIds.empty()) { | ||
| 246 | - UBSE_LOG_INFO << "[PROCESS_MEM] No remote borrow nodes to notify for lentNodeId=" << lentNodeId; | ||
| 247 | - return UBSE_OK; | ||
| 248 | - } | ||
| 249 | - | ||
| 250 | - // 向每个借入节点发送故障通知 | ||
| 251 | - uint32_t firstError = UBSE_OK; | ||
| 252 | - for (const auto& nodeId : targetNodeIds) { | ||
| 253 | - auto sendRet = SendProcessMemNodeFaultToNode(nodeId, lentNodeId); | ||
| 254 | - if (sendRet != UBSE_OK) { | ||
| 255 | - UBSE_LOG_ERROR << "[PROCESS_MEM] Failed to notify borrow node " << nodeId << " about fault node " | ||
| 256 | - << lentNodeId; | ||
| 257 | - if (firstError == UBSE_OK) { | ||
| 258 | - firstError = sendRet; | ||
| 259 | - } | ||
| 260 | - } | ||
| 261 | - } | ||
| 262 | - return firstError; | ||
| 263 | -} | ||
| 264 | - | ||
| 265 | -void ProcessMemPidBridge::ProcessMemNodeFaultNotifyHandler(const UbseByteBuffer& req, UbseByteBuffer& resp) | ||
| 266 | -{ | ||
| 267 | - UBSE_LOG_INFO << "[PROCESS_MEM] Received node fault notify from master."; | ||
| 268 | - | ||
| 269 | - ubse::serial::UbseDeSerialization input(req.data, req.len); | ||
| 270 | - std::string faultNodeId; | ||
| 271 | - input >> faultNodeId; | ||
| 272 | - if (!input.Check()) { | ||
| 273 | - UBSE_LOG_ERROR << "[PROCESS_MEM] Failed to deserialize node fault notify message."; | ||
| 274 | return; | 99 | return; |
| 275 | } | 100 | } |
| 276 | - | 101 | + if (itemCount == 0) { |
| 277 | - UBSE_LOG_INFO << "[PROCESS_MEM] Processing node fault notify, faultNodeId=" << faultNodeId; | 102 | + UBSE_LOG_WARN << "ProcessMemReturnRequestHandler: empty item list, ignore"; |
| 278 | - auto ret = manager::ProcessMemPidInfoManager::GetInstance().HandleNodeFaultEvent(faultNodeId); | 103 | + return; |
| 279 | - UBSE_LOG_INFO << "[PROCESS_MEM] Node fault notify processing done, ret=" << ret; | 104 | + } |
| 280 | - | 105 | + constexpr ubse::serial::common_len MAX_RETURN_ITEMS = 4096; |
| 281 | - ubse::serial::UbseSerialization output; | 106 | + if (itemCount > MAX_RETURN_ITEMS) { |
| 282 | - output << ret; | 107 | + UBSE_LOG_ERROR << "ProcessMemReturnRequestHandler: invalid itemCount=" << itemCount |
| 283 | - resp.data = output.GetBuffer(true); | 108 | + << ", max=" << MAX_RETURN_ITEMS; |
| 284 | - resp.len = output.GetLength(); | 109 | + return; |
| 285 | - resp.freeFunc = [](uint8_t* data) { | 110 | + } |
| 286 | - delete[] data; | 111 | + std::vector<def::ReturnRequestItem> items; |
| 287 | - }; | 112 | + items.reserve(static_cast<size_t>(itemCount)); |
| 113 | + for (ubse::serial::common_len i = 0; i < itemCount; ++i) { | ||
| 114 | + def::ReturnRequestItem item; | ||
| 115 | + deserializer >> item.name >> item.size; | ||
| 116 | + if (!deserializer.Check()) { | ||
| 117 | + UBSE_LOG_ERROR << "ProcessMemReturnRequestHandler: deserialize failed at item " << i; | ||
| 118 | + return; | ||
| 119 | + } | ||
| 120 | + items.push_back(std::move(item)); | ||
| 121 | + } | ||
| 122 | + auto ret = decision::ProcessMemPidDecision::GetInstance().HandleReturnRequest(items); | ||
| 123 | + if (ret != UBSE_OK) { | ||
| 124 | + UBSE_LOG_ERROR << "ProcessMemReturnRequestHandler: HandleReturnRequest failed, ret=" << ret; | ||
| 125 | + } | ||
| 288 | } | 126 | } |
| 289 | 127 | ||
| 290 | uint32_t SendPidSetResponse(int successCode, const std::string& errorMsg, uint64_t requestId) | 128 | uint32_t SendPidSetResponse(int successCode, const std::string& errorMsg, uint64_t requestId) |
| @@ -305,112 +143,198 @@ uint32_t SendPidSetResponse(int successCode, const std::string& errorMsg, uint64 | |||
| 305 | return ret; | 143 | return ret; |
| 306 | } | 144 | } |
| 307 | 145 | ||
| 308 | -uint32_t ValidateSrcNumaIdOnCurrentNode(const def::ProcessMemPidConfigInfo& configInfo) | 146 | +namespace { |
| 309 | -{ | 147 | +bool DeserializeProcMemConfigRequest(const api::server::UbseIpcMessage& request, |
| 310 | - if (!configInfo.srcNumaId.has_value()) { | 148 | + def::ProcessMemNewConfigInfo& outConfig, const char* callerName) |
| 311 | - return UBSE_OK; | ||
| 312 | - } | ||
| 313 | - auto curNodeInfo = ubse::nodeController::UbseNodeController::GetInstance().GetCurNode(); | ||
| 314 | - for (const auto& [numaLoc, _] : curNodeInfo.numaInfos) { | ||
| 315 | - if (numaLoc.numaId == configInfo.srcNumaId.value()) { | ||
| 316 | - return UBSE_OK; | ||
| 317 | - } | ||
| 318 | - } | ||
| 319 | - UBSE_LOG_ERROR << "srcNumaId " << configInfo.srcNumaId.value() << " not found on current node"; | ||
| 320 | - return UBSE_ERR_NOT_EXIST; | ||
| 321 | -} | ||
| 322 | - | ||
| 323 | -uint32_t SendSetPidMapErrorResponse(uint32_t ret, uint64_t requestId) | ||
| 324 | -{ | ||
| 325 | - const char* msg = nullptr; | ||
| 326 | - if (ret == UBSE_ERR_NOT_EXIST) { | ||
| 327 | - msg = "PID does not exist on current node"; | ||
| 328 | - } else if (ret == UBSE_ERR_INVALID_ARG) { | ||
| 329 | - msg = "PID start time mismatch, process may have been restarted"; | ||
| 330 | - } else if (ret == UBSE_ERR_RESOURCE_BUSY) { | ||
| 331 | - msg = "Cannot modify srcNumaId while borrow debts exist"; | ||
| 332 | - } else { | ||
| 333 | - msg = "Set PID info failed"; | ||
| 334 | - } | ||
| 335 | - UBSE_LOG_ERROR << "SetPidInfoMap failed, " << ubse::log::FormatRetCode(ret); | ||
| 336 | - return SendPidSetResponse(0, msg, requestId); | ||
| 337 | -} | ||
| 338 | - | ||
| 339 | -uint32_t SetPidInfo(const api::server::UbseIpcMessage& request, const api::server::UbseRequestContext& context) | ||
| 340 | { | 149 | { |
| 341 | ubse::serial::UbseDeSerialization deserializer{request.buffer, request.length}; | 150 | ubse::serial::UbseDeSerialization deserializer{request.buffer, request.length}; |
| 342 | - def::ProcessMemPidInfo pidInfo{}; | 151 | + auto ret = outConfig.Deserialize(deserializer); |
| 343 | - auto ret = pidInfo.configInfo.DeserializeConfigInfo(deserializer); | 152 | + if (ret != UBSE_OK || !deserializer.Check()) { |
| 153 | + UBSE_LOG_ERROR << callerName << ": deserialize failed"; | ||
| 154 | + return false; | ||
| 155 | + } | ||
| 156 | + return true; | ||
| 157 | +} | ||
| 158 | +} // namespace | ||
| 159 | + | ||
| 160 | +uint32_t SetProcMemConfig(const api::server::UbseIpcMessage& request, const api::server::UbseRequestContext& context) | ||
| 161 | +{ | ||
| 162 | + def::ProcessMemNewConfigInfo newConfig{}; | ||
| 163 | + if (!DeserializeProcMemConfigRequest(request, newConfig, "SetProcMemConfig")) { | ||
| 164 | + return SendPidSetResponse(0, "Invalid request", context.requestId); | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + if (newConfig.identifier.empty()) { | ||
| 168 | + UBSE_LOG_ERROR << "SetProcMemConfig: identifier is empty"; | ||
| 169 | + return SendPidSetResponse(0, "Identifier is empty", context.requestId); | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + auto ret = manager::ProcessMemPidInfoManager::GetInstance().SetProcMemConfig(newConfig); | ||
| 173 | + if (ret == UBSE_ERR_NOT_EXIST) { | ||
| 174 | + if (newConfig.isPid) { | ||
| 175 | + return SendPidSetResponse(0, "PID does not exist on current node", context.requestId); | ||
| 176 | + } | ||
| 177 | + return SendPidSetResponse(0, "No running process matches name: " + newConfig.identifier, context.requestId); | ||
| 178 | + } | ||
| 179 | + if (ret == UBSE_ERR_INVALID_ARG) { | ||
| 180 | + return SendPidSetResponse(0, "Invalid process-mem config (size/ratio/name out of range)", context.requestId); | ||
| 181 | + } | ||
| 344 | if (ret != UBSE_OK) { | 182 | if (ret != UBSE_OK) { |
| 345 | - UBSE_LOG_ERROR << "DeSerialPidDefInfo failed, " << ubse::log::FormatRetCode(ret); | 183 | + UBSE_LOG_ERROR << "SetProcMemConfig failed for " << newConfig.identifier |
| 346 | - return ret; | 184 | + << ", ret=" << ubse::log::FormatRetCode(ret); |
| 347 | - } | 185 | + return SendPidSetResponse(0, "Failed to set process-mem config", context.requestId); |
| 348 | - auto retValidate = ValidateSrcNumaIdOnCurrentNode(pidInfo.configInfo); | ||
| 349 | - if (retValidate != UBSE_OK) { | ||
| 350 | - return SendPidSetResponse( | ||
| 351 | - 0, "srcNumaId " + std::to_string(pidInfo.configInfo.srcNumaId.value()) + " not found on current node", | ||
| 352 | - context.requestId); | ||
| 353 | - } | ||
| 354 | - pidInfo.startTime = manager::ProcessMemPidConfigManager::GetExactStartTime(pidInfo.configInfo.pid); | ||
| 355 | - if (pidInfo.startTime == 0) { | ||
| 356 | - UBSE_LOG_ERROR << "PID " << pidInfo.configInfo.pid << " does not exist"; | ||
| 357 | - return SendPidSetResponse(0, "PID does not exist", context.requestId); | ||
| 358 | - } | ||
| 359 | - ret = manager::ProcessMemPidInfoManager::GetInstance().SetPidInfoMap(pidInfo); | ||
| 360 | - if (ret != UBSE_OK) { | ||
| 361 | - return SendSetPidMapErrorResponse(ret, context.requestId); | ||
| 362 | } | 186 | } |
| 187 | + | ||
| 188 | + UBSE_LOG_INFO << "SetProcMemConfig: " << (newConfig.isPid ? "pid=" : "name=") << newConfig.identifier | ||
| 189 | + << ", maxMemory=" << newConfig.maxMemory << ", remoteRatio=" << newConfig.remoteRatio; | ||
| 363 | return SendPidSetResponse(1, "", context.requestId); | 190 | return SendPidSetResponse(1, "", context.requestId); |
| 364 | } | 191 | } |
| 365 | 192 | ||
| 366 | -uint32_t PidInfoPrint(const api::server::UbseIpcMessage& request, const api::server::UbseRequestContext& context) | 193 | +uint32_t RemoveProcMemConfig(const api::server::UbseIpcMessage& request, const api::server::UbseRequestContext& context) |
| 367 | { | 194 | { |
| 368 | - ubse::serial::UbseDeSerialization out{request.buffer, request.length}; | 195 | + def::ProcessMemNewConfigInfo removeTarget{}; |
| 369 | - std::vector<def::ProcessMemPidInfo> queryInfo{}; | 196 | + if (!DeserializeProcMemConfigRequest(request, removeTarget, "RemoveProcMemConfig")) { |
| 370 | - manager::ProcessMemPidInfoManager::GetInstance().GetAllPidInfo(queryInfo); | 197 | + return SendPidSetResponse(0, "Invalid request", context.requestId); |
| 371 | - UBSE_LOG_INFO << "info size is " << queryInfo.size(); | 198 | + } |
| 372 | - api::server::UbseIpcMessage response; | 199 | + |
| 373 | - ubse::serial::UbseSerialization serializer; | 200 | + auto ret = manager::ProcessMemPidInfoManager::GetInstance().RemoveProcMemConfig(removeTarget.isPid, |
| 374 | - serializer << (ubse::serial::right_v<size_t>(queryInfo.size())); | 201 | + removeTarget.identifier); |
| 375 | - for (const auto& info : queryInfo) { | 202 | + if (ret != UBSE_OK) { |
| 376 | - auto ret = info.configInfo.SerializeConfigInfo(serializer); | 203 | + UBSE_LOG_ERROR << "RemoveProcMemConfig failed for " << (removeTarget.isPid ? "pid=" : "name=") |
| 377 | - if (ret != UBSE_OK) { | 204 | + << removeTarget.identifier << ", ret=" << ubse::log::FormatRetCode(ret); |
| 378 | - UBSE_LOG_ERROR << "SerialPidDefInfo failed, " << ubse::log::FormatRetCode(ret); | 205 | + if (ret == UBSE_ERR_NOT_EXIST) { |
| 379 | - auto ret = SendResponse(UBSE_OK, context.requestId, response); | 206 | + if (removeTarget.isPid) { |
| 380 | - if (ret != UBSE_OK) { | 207 | + return SendPidSetResponse(0, "pid " + removeTarget.identifier + " is not managed by process_mem", |
| 381 | - UBSE_LOG_ERROR << "Send response failed, " << ubse::log::FormatRetCode(ret); | 208 | + context.requestId); |
| 382 | - return ret; | ||
| 383 | } | 209 | } |
| 210 | + return SendPidSetResponse(0, "name '" + removeTarget.identifier + "' is not configured", context.requestId); | ||
| 211 | + } | ||
| 212 | + return SendPidSetResponse(0, "Failed to remove process-mem config", context.requestId); | ||
| 213 | + } | ||
| 214 | + | ||
| 215 | + UBSE_LOG_INFO << "RemoveProcMemConfig: " << (removeTarget.isPid ? "pid=" : "name=") << removeTarget.identifier | ||
| 216 | + << " removed successfully"; | ||
| 217 | + return SendPidSetResponse(1, "", context.requestId); | ||
| 218 | +} | ||
| 219 | + | ||
| 220 | +namespace { | ||
| 221 | + | ||
| 222 | +void SortConfigEntries(std::vector<def::ProcessMemDisplayEntry>& entries) | ||
| 223 | +{ | ||
| 224 | + std::sort(entries.begin(), entries.end(), [](const auto& a, const auto& b) { | ||
| 225 | + if ((a.pid > 0) != (b.pid > 0)) { | ||
| 226 | + return a.pid > 0; | ||
| 227 | + } | ||
| 228 | + if (a.pid > 0) { | ||
| 229 | + return a.pid < b.pid; | ||
| 230 | + } | ||
| 231 | + return a.name < b.name; | ||
| 232 | + }); | ||
| 233 | +} | ||
| 234 | + | ||
| 235 | +void SortPidEntries(std::vector<def::ProcessMemDisplayEntry>& entries) | ||
| 236 | +{ | ||
| 237 | + std::sort(entries.begin(), entries.end(), [](const auto& a, const auto& b) { return a.pid < b.pid; }); | ||
| 238 | +} | ||
| 239 | + | ||
| 240 | +uint32_t SendDisplayEntries(const api::server::UbseIpcMessage& request, const api::server::UbseRequestContext& context, | ||
| 241 | + const std::vector<def::ProcessMemDisplayEntry>& entries) | ||
| 242 | +{ | ||
| 243 | + api::server::UbseIpcMessage response{nullptr, 0}; | ||
| 244 | + ubse::serial::UbseSerialization serializer; | ||
| 245 | + size_t entryCount = entries.size(); | ||
| 246 | + serializer << entryCount; | ||
| 247 | + for (const auto& entry : entries) { | ||
| 248 | + auto serRet = entry.Serialize(serializer); | ||
| 249 | + if (serRet != UBSE_OK) { | ||
| 250 | + UBSE_LOG_ERROR << "DisplayProcMem: entry serialize failed for pid=" << entry.pid; | ||
| 251 | + return serRet; | ||
| 384 | } | 252 | } |
| 385 | } | 253 | } |
| 386 | 254 | ||
| 387 | response.buffer = serializer.GetBuffer(); | 255 | response.buffer = serializer.GetBuffer(); |
| 388 | if (!response.buffer) { | 256 | if (!response.buffer) { |
| 389 | - UBSE_LOG_ERROR << "Serialization response failed."; | 257 | + UBSE_LOG_ERROR << "DisplayProcMem: serialization response failed."; |
| 390 | return UBSE_ERROR_NULLPTR; | 258 | return UBSE_ERROR_NULLPTR; |
| 391 | } | 259 | } |
| 392 | response.length = static_cast<uint32_t>(serializer.GetLength()); | 260 | response.length = static_cast<uint32_t>(serializer.GetLength()); |
| 393 | - auto ret = SendResponse(UBSE_OK, context.requestId, response); | 261 | + auto sendRet = SendResponse(UBSE_OK, context.requestId, response); |
| 394 | - if (ret != UBSE_OK) { | 262 | + if (sendRet != UBSE_OK) { |
| 395 | - UBSE_LOG_ERROR << "Send response failed, " << ubse::log::FormatRetCode(ret); | 263 | + UBSE_LOG_ERROR << "DisplayProcMem: Send response failed, " << ubse::log::FormatRetCode(sendRet); |
| 396 | } | 264 | } |
| 397 | - return ret; | 265 | + return sendRet; |
| 398 | } | 266 | } |
| 399 | 267 | ||
| 400 | -uint32_t UnSetPidInfoPrint(const api::server::UbseIpcMessage& request, const api::server::UbseRequestContext& context) | 268 | +} // namespace |
| 269 | + | ||
| 270 | +void BuildConfigEntries(std::vector<def::ProcessMemDisplayEntry>& entries, | ||
| 271 | + const std::vector<def::ProcessMemNewConfigInfo>& newConfigs) | ||
| 401 | { | 272 | { |
| 402 | - ubse::serial::UbseDeSerialization out{request.buffer, request.length}; | 273 | + for (const auto& cfg : newConfigs) { |
| 403 | - pid_t pid{}; | 274 | + if (cfg.isPid) { |
| 404 | - out >> pid; | 275 | + auto pidOpt = def::ParsePidFromIdentifier(cfg.identifier); |
| 405 | - auto ret = manager::ProcessMemPidInfoManager::GetInstance().UnsetPidInfo(pid); | 276 | + if (!pidOpt.has_value()) { |
| 406 | - if (ret == UBSE_ERR_NOT_EXIST) { | 277 | + UBSE_LOG_WARN << "BuildConfigEntries: skipping PID config with unparseable identifier=" |
| 407 | - UBSE_LOG_ERROR << "UnsetPidInfo failed, " << ubse::log::FormatRetCode(ret); | 278 | + << cfg.identifier; |
| 408 | - return SendPidSetResponse(0, "PID is not configured", context.requestId); | 279 | + continue; |
| 409 | - } else if (ret != UBSE_OK) { | 280 | + } |
| 410 | - UBSE_LOG_ERROR << "UnsetPidInfo failed, " << ubse::log::FormatRetCode(ret); | 281 | + def::ProcessMemDisplayEntry entry; |
| 411 | - return SendPidSetResponse(0, "Failed to unset PID info", context.requestId); | 282 | + entry.pid = static_cast<int32_t>(pidOpt.value()); |
| 283 | + entry.name = "N/A"; | ||
| 284 | + entry.maxMemory = cfg.maxMemory; | ||
| 285 | + entry.remoteRatio = cfg.remoteRatio; | ||
| 286 | + entries.push_back(entry); | ||
| 287 | + } else { | ||
| 288 | + def::ProcessMemDisplayEntry entry; | ||
| 289 | + entry.pid = 0; | ||
| 290 | + entry.name = cfg.identifier; | ||
| 291 | + entry.maxMemory = cfg.maxMemory; | ||
| 292 | + entry.remoteRatio = cfg.remoteRatio; | ||
| 293 | + entries.push_back(entry); | ||
| 294 | + } | ||
| 412 | } | 295 | } |
| 413 | - return SendPidSetResponse(1, "", context.requestId); | 296 | + SortConfigEntries(entries); |
| 297 | +} | ||
| 298 | + | ||
| 299 | +void BuildProcDetailEntries(std::vector<def::ProcessMemDisplayEntry>& entries, | ||
| 300 | + const std::map<pid_t, def::ManagedPidEntry>& managedSnapshot) | ||
| 301 | +{ | ||
| 302 | + // 仅回显 process_mem 真正托管(匹配)到的进程, 取托管缓存而非配置/实时 /proc 扫描; | ||
| 303 | + // pid 配置命中时 name 强制 N/A (与旧行为一致), 否则显示实际匹配的配置名 | ||
| 304 | + for (const auto& [pid, entry] : managedSnapshot) { | ||
| 305 | + def::ProcessMemDisplayEntry display; | ||
| 306 | + display.pid = static_cast<int32_t>(pid); | ||
| 307 | + bool hasPidConfig = (entry.sources & static_cast<uint8_t>(def::ConfigSource::PID_CONFIG)) != 0; | ||
| 308 | + display.name = hasPidConfig ? "N/A" : (entry.nameConfigName.empty() ? "N/A" : entry.nameConfigName); | ||
| 309 | + display.maxMemory = entry.maxMemory; | ||
| 310 | + display.remoteRatio = entry.remoteRatio; | ||
| 311 | + entries.push_back(display); | ||
| 312 | + } | ||
| 313 | + SortPidEntries(entries); | ||
| 314 | +} | ||
| 315 | + | ||
| 316 | +uint32_t DisplayProcMemConfig(const api::server::UbseIpcMessage& request, | ||
| 317 | + const api::server::UbseRequestContext& context) | ||
| 318 | +{ | ||
| 319 | + std::vector<def::ProcessMemNewConfigInfo> newConfigs; | ||
| 320 | + manager::ProcessMemPidInfoManager::GetInstance().GetAllProcMemConfigs(newConfigs); | ||
| 321 | + | ||
| 322 | + std::vector<def::ProcessMemDisplayEntry> entries; | ||
| 323 | + BuildConfigEntries(entries, newConfigs); | ||
| 324 | + | ||
| 325 | + UBSE_LOG_INFO << "DisplayProcMemConfig: returning " << entries.size() << " config entries"; | ||
| 326 | + return SendDisplayEntries(request, context, entries); | ||
| 327 | +} | ||
| 328 | + | ||
| 329 | +uint32_t DisplayProcMemDetail(const api::server::UbseIpcMessage& request, | ||
| 330 | + const api::server::UbseRequestContext& context) | ||
| 331 | +{ | ||
| 332 | + std::vector<def::ProcessMemDisplayEntry> entries; | ||
| 333 | + auto managedSnapshot = manager::ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 334 | + BuildProcDetailEntries(entries, managedSnapshot); | ||
| 335 | + | ||
| 336 | + UBSE_LOG_INFO << "DisplayProcMemDetail: returning " << entries.size() << " managed entries"; | ||
| 337 | + return SendDisplayEntries(request, context, entries); | ||
| 414 | } | 338 | } |
| 415 | 339 | ||
| 416 | namespace { | 340 | namespace { |
| @@ -427,86 +351,85 @@ bool LoadMemPoolingLibrary() | |||
| 427 | reinterpret_cast<int (*)(const uint16_t, const std::vector<pid_t>&, int)>(dlsym(handle, "UBSRMRSRemove")); | 351 | reinterpret_cast<int (*)(const uint16_t, const std::vector<pid_t>&, int)>(dlsym(handle, "UBSRMRSRemove")); |
| 428 | ProcessMemPidBridge::rmrsFreeWithMigrate = | 352 | ProcessMemPidBridge::rmrsFreeWithMigrate = |
| 429 | reinterpret_cast<uint32_t (*)(const std::string)>(dlsym(handle, "UBSRMRSMemFreeWithMigrate")); | 353 | reinterpret_cast<uint32_t (*)(const std::string)>(dlsym(handle, "UBSRMRSMemFreeWithMigrate")); |
| 354 | + ProcessMemPidBridge::rmrsRemoteToRemote = | ||
| 355 | + reinterpret_cast<int (*)(const mempooling::smap::MigrateEscapeMsg&)>(dlsym(handle, "UBSRMRSRemoteNumaMigrate")); | ||
| 356 | + ProcessMemPidBridge::rmrsProcessConfigQuery = | ||
| 357 | + reinterpret_cast<int (*)(int, mempooling::smap::ProcessPayload*, int, int*)>( | ||
| 358 | + dlsym(handle, "UBSRMRSProcessConfigQuery")); | ||
| 430 | if (!ProcessMemPidBridge::rmrsMigrateOut || !ProcessMemPidBridge::rmrsRemove) { | 359 | if (!ProcessMemPidBridge::rmrsMigrateOut || !ProcessMemPidBridge::rmrsRemove) { |
| 431 | dlclose(handle); | 360 | dlclose(handle); |
| 432 | ProcessMemPidBridge::memPoolingHandle = nullptr; | 361 | ProcessMemPidBridge::memPoolingHandle = nullptr; |
| 433 | return false; | 362 | return false; |
| 434 | } | 363 | } |
| 364 | + if (!ProcessMemPidBridge::rmrsRemoteToRemote) { | ||
| 365 | + UBSE_LOG_WARN << "UBSRMRSRemoteNumaMigrate not exported by libmempooling.so, " | ||
| 366 | + "remote-to-remote return will be skipped"; | ||
| 367 | + } | ||
| 435 | return true; | 368 | return true; |
| 436 | } | 369 | } |
| 437 | 370 | ||
| 438 | -void RegisterLocalStateHandler() | ||
| 439 | -{ | ||
| 440 | - // 注册前先检查当前节点状态,避免状态已变更但回调未注册 | ||
| 441 | - auto curNode = ubse::nodeController::UbseNodeController::GetInstance().GetCurNode(); | ||
| 442 | - if (curNode.localState == ubse::nodeController::UbseNodeLocalState::UBSE_NODE_READY) { | ||
| 443 | - process_mem::manager::ProcessMemPidInfoManager::GetInstance().RecoverAllDebtInfoData(); | ||
| 444 | - } | ||
| 445 | - | ||
| 446 | - auto localStateHandler = [](const ubse::nodeController::UbseNodeInfo& node) -> uint32_t { | ||
| 447 | - if (process_mem::manager::ProcessMemPidInfoManager::GetInstance().IsRecoverCompleted()) { | ||
| 448 | - return UBSE_OK; | ||
| 449 | - } | ||
| 450 | - if (node.localState == ubse::nodeController::UbseNodeLocalState::UBSE_NODE_READY) { | ||
| 451 | - process_mem::manager::ProcessMemPidInfoManager::GetInstance().RecoverAllDebtInfoData(); | ||
| 452 | - } else { | ||
| 453 | - UBSE_LOG_INFO << "localStateHandler: state=" << static_cast<int>(node.localState) | ||
| 454 | - << ", not READY yet, skip recovery"; | ||
| 455 | - } | ||
| 456 | - return UBSE_OK; | ||
| 457 | - }; | ||
| 458 | - ubse::nodeController::UbseNodeController::GetInstance().RegLocalStateNotifyHandler(localStateHandler); | ||
| 459 | -} | ||
| 460 | - | ||
| 461 | -void RegisterFaultHandlers() | ||
| 462 | -{ | ||
| 463 | - ubse::ras::AlarmHandler panicHandler; | ||
| 464 | - panicHandler.alarmFaultEvent = ubse::ras::ALARM_PANIC_EVENT; | ||
| 465 | - panicHandler.name = "ProcessMemPanic"; | ||
| 466 | - panicHandler.handler = ProcessMemPidBridge::FaultHandler; | ||
| 467 | - panicHandler.priority = ubse::ras::AlarmHandlerPriority::HIGH; | ||
| 468 | - ubse::ras::RegisterAlarmFaultHandler(panicHandler); | ||
| 469 | - | ||
| 470 | - ubse::ras::AlarmHandler rebootHandler; | ||
| 471 | - rebootHandler.alarmFaultEvent = ubse::ras::ALARM_KERNEL_REBOOT_EVENT; | ||
| 472 | - rebootHandler.name = "ProcessMemKernelReboot"; | ||
| 473 | - rebootHandler.handler = ProcessMemPidBridge::FaultHandler; | ||
| 474 | - rebootHandler.priority = ubse::ras::AlarmHandlerPriority::HIGH; | ||
| 475 | - ubse::ras::RegisterAlarmFaultHandler(rebootHandler); | ||
| 476 | -} | ||
| 477 | } // namespace | 371 | } // namespace |
| 478 | 372 | ||
| 373 | +uint32_t ProcessMemPidBridge::RegisterConfigIpcHandlers() | ||
| 374 | +{ | ||
| 375 | + auto ret = | ||
| 376 | + api::server::RegisterIpcHandler(UBSE_MEM, UBSE_MEM_CLI_PROC_MEM_SET, SetProcMemConfig, PROCESS_MEM_PERMISSION); | ||
| 377 | + if (ret != UBSE_OK) { | ||
| 378 | + UBSE_LOG_ERROR << "Register SetProcMemConfig IPC Handler failed, " << ubse::log::FormatRetCode(ret); | ||
| 379 | + return ret; | ||
| 380 | + } | ||
| 381 | + ret = api::server::RegisterIpcHandler(UBSE_MEM, UBSE_MEM_CLI_PROC_MEM_REMOVE, RemoveProcMemConfig, | ||
| 382 | + PROCESS_MEM_PERMISSION); | ||
| 383 | + if (ret != UBSE_OK) { | ||
| 384 | + UBSE_LOG_ERROR << "Register RemoveProcMemConfig IPC Handler failed, " << ubse::log::FormatRetCode(ret); | ||
| 385 | + return ret; | ||
| 386 | + } | ||
| 387 | + ret = api::server::RegisterIpcHandler(UBSE_MEM, UBSE_MEM_CLI_PROC_MEM_DISPLAY_CONFIG, DisplayProcMemConfig, | ||
| 388 | + PROCESS_MEM_PERMISSION); | ||
| 389 | + if (ret != UBSE_OK) { | ||
| 390 | + UBSE_LOG_ERROR << "Register DisplayProcMemConfig IPC Handler failed, " << ubse::log::FormatRetCode(ret); | ||
| 391 | + return ret; | ||
| 392 | + } | ||
| 393 | + ret = api::server::RegisterIpcHandler(UBSE_MEM, UBSE_MEM_CLI_PROC_MEM_DISPLAY_DETAIL, DisplayProcMemDetail, | ||
| 394 | + PROCESS_MEM_PERMISSION); | ||
| 395 | + if (ret != UBSE_OK) { | ||
| 396 | + UBSE_LOG_ERROR << "Register DisplayProcMemDetail IPC Handler failed, " << ubse::log::FormatRetCode(ret); | ||
| 397 | + return ret; | ||
| 398 | + } | ||
| 399 | + return UBSE_OK; | ||
| 400 | +} | ||
| 401 | + | ||
| 479 | uint32_t ProcessMemPidBridge::Init() | 402 | uint32_t ProcessMemPidBridge::Init() |
| 480 | { | 403 | { |
| 404 | + auto ret = RegisterConfigIpcHandlers(); | ||
| 405 | + if (ret != UBSE_OK) { | ||
| 406 | + return ret; | ||
| 407 | + } | ||
| 408 | + | ||
| 409 | + auto returnEndpoint = GetProcessMemReturnEndpoint( | ||
| 410 | + static_cast<uint16_t>(ubse::com::UbseMemFaultOpCode::UBSE_PROCESS_MEM_RETURN_REQUEST), ""); | ||
| 411 | + auto rpcRet = ubse::com::UbseRegRpcService(returnEndpoint, ProcessMemPidBridge::ProcessMemReturnRequestHandler); | ||
| 412 | + if (rpcRet != UBSE_OK) { | ||
| 413 | + UBSE_LOG_ERROR << "Register ProcessMemReturnRequest RPC handler failed, " << ubse::log::FormatRetCode(rpcRet); | ||
| 414 | + return rpcRet; | ||
| 415 | + } | ||
| 416 | + | ||
| 481 | if (!LoadMemPoolingLibrary()) { | 417 | if (!LoadMemPoolingLibrary()) { |
| 482 | - return UBSE_ERROR; | 418 | + UBSE_LOG_WARN << "Failed to load libmempooling.so, memory borrowing/migration will be unavailable"; |
| 419 | + process_mem::manager::ProcessMemPidInfoManager::GetInstance().RefreshProcMemConfigCache(); | ||
| 420 | + return UBSE_OK; | ||
| 483 | } | 421 | } |
| 484 | 422 | ||
| 485 | std::vector<__u32> dacReadSearchCap = {CAP_DAC_READ_SEARCH}; | 423 | std::vector<__u32> dacReadSearchCap = {CAP_DAC_READ_SEARCH}; |
| 486 | ubse::security::UbseSecurityModule::ModifyEffectiveCapabilities(dacReadSearchCap, true); | 424 | ubse::security::UbseSecurityModule::ModifyEffectiveCapabilities(dacReadSearchCap, true); |
| 487 | 425 | ||
| 488 | - auto ret = | ||
| 489 | - api::server::RegisterIpcHandler(UBSE_MEM, UBSE_MEM_CLI_PID_SET_THRESHOLD, SetPidInfo, PROCESS_MEM_PERMISSION); | ||
| 490 | - ret |= api::server::RegisterIpcHandler(UBSE_MEM, UBSE_MEM_CLI_PRINT_PID_INFO, PidInfoPrint, PROCESS_MEM_PERMISSION); | ||
| 491 | - ret |= api::server::RegisterIpcHandler(UBSE_MEM, UBSE_MEM_CLI_PID_UNSET, UnSetPidInfoPrint, PROCESS_MEM_PERMISSION); | ||
| 492 | - if (ret != UBSE_OK) { | ||
| 493 | - UBSE_LOG_ERROR << "Register IPC Handler failed, " << ubse::log::FormatRetCode(ret); | ||
| 494 | - return ret; | ||
| 495 | - } | ||
| 496 | - | ||
| 497 | - // 注册process_mem节点故障通知RPC处理器(所有节点均需注册,用于接收Master的通知) | ||
| 498 | - auto rpcEndpoint = GetProcessMemFaultComEndpoint( | ||
| 499 | - static_cast<uint16_t>(ubse::com::UbseMemFaultOpCode::UBSE_PROCESS_MEM_NODE_FAULT_NOTIFY), ""); | ||
| 500 | - auto rpcRet = ubse::com::UbseRegRpcService(rpcEndpoint, ProcessMemPidBridge::ProcessMemNodeFaultNotifyHandler); | ||
| 501 | - if (rpcRet != UBSE_OK) { | ||
| 502 | - UBSE_LOG_ERROR << "[PROCESS_MEM] Register node fault notify RPC handler failed, " | ||
| 503 | - << ubse::log::FormatRetCode(rpcRet); | ||
| 504 | - return rpcRet; | ||
| 505 | - } | ||
| 506 | - | ||
| 507 | - RegisterFaultHandlers(); | ||
| 508 | process_mem::manager::ProcessMemPidInfoManager::GetInstance().Init(); | 426 | process_mem::manager::ProcessMemPidInfoManager::GetInstance().Init(); |
| 509 | - RegisterLocalStateHandler(); | 427 | + |
| 428 | + auto decisionRet = process_mem::decision::ProcessMemPidDecision::GetInstance().Init(); | ||
| 429 | + if (decisionRet != UBSE_OK) { | ||
| 430 | + UBSE_LOG_ERROR << "ProcessMemPidDecision Init failed, " << ubse::log::FormatRetCode(decisionRet); | ||
| 431 | + } | ||
| 432 | + | ||
| 510 | return UBSE_OK; | 433 | return UBSE_OK; |
| 511 | } | 434 | } |
| 512 | 435 | ||
| @@ -515,6 +438,7 @@ uint32_t ProcessMemPidBridge::UnInit() | |||
| 515 | if (memPoolingHandle != nullptr) { | 438 | if (memPoolingHandle != nullptr) { |
| 516 | dlclose(memPoolingHandle); | 439 | dlclose(memPoolingHandle); |
| 517 | } | 440 | } |
| 441 | + process_mem::decision::ProcessMemPidDecision::GetInstance().UnInit(); | ||
| 518 | process_mem::manager::ProcessMemPidInfoManager::GetInstance().UnInit(); | 442 | process_mem::manager::ProcessMemPidInfoManager::GetInstance().UnInit(); |
| 519 | return UBSE_OK; | 443 | return UBSE_OK; |
| 520 | } | 444 | } |
| @@ -16,7 +16,6 @@ | |||
| 16 | 16 | ||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | - | ||
| 20 | 19 | ||
| 21 | 20 | ||
| 22 | 21 | ||
| @@ -26,35 +25,29 @@ constexpr const char* MEMPOOLING_PATH = "/usr/lib64/libmempooling.so"; | |||
| 26 | using MigrateOut = std::function<int(const std::vector<mempooling::smap::MigrateOutPayload>&, int)>; | 25 | using MigrateOut = std::function<int(const std::vector<mempooling::smap::MigrateOutPayload>&, int)>; |
| 27 | using Remove = std::function<int(const uint16_t, const std::vector<pid_t>&, int)>; | 26 | using Remove = std::function<int(const uint16_t, const std::vector<pid_t>&, int)>; |
| 28 | using NoMigrateBack = std::function<uint32_t(const std::string&)>; | 27 | using NoMigrateBack = std::function<uint32_t(const std::string&)>; |
| 29 | - | 28 | +using RemoteToRemote = std::function<int(const mempooling::smap::MigrateEscapeMsg&)>; |
| 30 | -struct MemoryBorrowRequest { | 29 | +using ProcessConfigQuery = std::function<int(int, mempooling::smap::ProcessPayload*, int, int*)>; |
| 31 | - std::string name; | ||
| 32 | - uint64_t size; | ||
| 33 | - uint8_t usrInfo[ubse::mem::controller::UBSE_MAX_USR_INFO_LEN]{}; | ||
| 34 | -}; | ||
| 35 | 30 | ||
| 36 | class ProcessMemPidBridge { | 31 | class ProcessMemPidBridge { |
| 37 | public: | 32 | public: |
| 38 | static uint32_t Init(); | 33 | static uint32_t Init(); |
| 39 | static uint32_t UnInit(); | 34 | static uint32_t UnInit(); |
| 40 | - static uint32_t MemoryBorrow(def::ProcessMemPidInfo& pidInfo, | 35 | + |
| 41 | - const ubse::mem::controller::UbseMemBorrower& borrower, | 36 | + static uint32_t RegisterConfigIpcHandlers(); |
| 42 | - const MemoryBorrowRequest& request, | ||
| 43 | - ubse::mem::controller::UbseMemNumaDesc& borrowInfo); | ||
| 44 | 37 | ||
| 45 | static uint32_t MemoryReturn(const std::string& name); | 38 | static uint32_t MemoryReturn(const std::string& name); |
| 46 | 39 | ||
| 47 | - static uint32_t GetRemoteNumaSocketInfo(const ubse::mem::controller::UbseMemNumaDesc& desc, uint32_t& socketId, | 40 | + static uint32_t SendReturnRequestToNode(const std::string& nodeId, |
| 48 | - uint64_t& numaId); | 41 | + const std::vector<def::ReturnRequestItem>& items); |
| 42 | + | ||
| 43 | + static void ProcessMemReturnRequestHandler(const UbseByteBuffer& req, UbseByteBuffer& resp); | ||
| 44 | + | ||
| 49 | inline static MigrateOut rmrsMigrateOut; | 45 | inline static MigrateOut rmrsMigrateOut; |
| 50 | inline static Remove rmrsRemove; | 46 | inline static Remove rmrsRemove; |
| 51 | inline static NoMigrateBack rmrsFreeWithMigrate; | 47 | inline static NoMigrateBack rmrsFreeWithMigrate; |
| 48 | + inline static RemoteToRemote rmrsRemoteToRemote; | ||
| 49 | + inline static ProcessConfigQuery rmrsProcessConfigQuery; | ||
| 52 | inline static void* memPoolingHandle = nullptr; | 50 | inline static void* memPoolingHandle = nullptr; |
| 53 | - static uint32_t FaultHandler(ubse::ras::ALARM_FAULT_TYPE alarmFaultEvent, std::string faultInfo); | ||
| 54 | - | ||
| 55 | - static void ProcessMemNodeFaultNotifyHandler(const UbseByteBuffer& req, UbseByteBuffer& resp); | ||
| 56 | - | ||
| 57 | - static uint32_t NotifyBorrowNodesOnFault(const std::string& lentNodeId); | ||
| 58 | }; | 51 | }; |
| 59 | } // namespace process_mem::pid::bridge | 52 | } // namespace process_mem::pid::bridge |
| 60 | 53 | ||
| @@ -12,8 +12,12 @@ | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | + | ||
| 15 | 16 | ||
| 17 | + | ||
| 18 | + | ||
| 16 | 19 | ||
| 20 | + | ||
| 17 | 21 | ||
| 18 | 22 | ||
| 19 | 23 | ||
| @@ -21,6 +25,7 @@ | |||
| 21 | 25 | ||
| 22 | 26 | ||
| 23 | 27 | ||
| 28 | + | ||
| 24 | 29 | ||
| 25 | 30 | ||
| 26 | namespace process_mem::collect { | 31 | namespace process_mem::collect { |
| @@ -29,7 +34,6 @@ UBSE_DEFINE_THIS_MODULE("process_mem"); | |||
| 29 | using namespace ubse::timer; | 34 | using namespace ubse::timer; |
| 30 | using namespace process_mem::manager; | 35 | using namespace process_mem::manager; |
| 31 | 36 | ||
| 32 | -const uint32_t PROCESS_MEM_COLLECT_INTERVAL = 10; | ||
| 33 | const size_t DEFAULT_PAGE_SIZE = 4096; | 37 | const size_t DEFAULT_PAGE_SIZE = 4096; |
| 34 | 38 | ||
| 35 | std::vector<pid_t> GetChildrenPidsFallback(pid_t parentPid) | 39 | std::vector<pid_t> GetChildrenPidsFallback(pid_t parentPid) |
| @@ -54,7 +58,6 @@ std::vector<pid_t> GetChildrenPidsFallback(pid_t parentPid) | |||
| 54 | 58 | ||
| 55 | std::vector<pid_t> GetChildrenPids(pid_t parentPid) | 59 | std::vector<pid_t> GetChildrenPids(pid_t parentPid) |
| 56 | { | 60 | { |
| 57 | - // 优先尝试 /proc/pid/children(高效) | ||
| 58 | std::string path = "/proc/" + std::to_string(parentPid) + "/task/" + std::to_string(parentPid) + "/children"; | 61 | std::string path = "/proc/" + std::to_string(parentPid) + "/task/" + std::to_string(parentPid) + "/children"; |
| 59 | std::ifstream file(path); | 62 | std::ifstream file(path); |
| 60 | if (file.is_open()) { | 63 | if (file.is_open()) { |
| @@ -69,117 +72,537 @@ std::vector<pid_t> GetChildrenPids(pid_t parentPid) | |||
| 69 | return children; | 72 | return children; |
| 70 | } | 73 | } |
| 71 | } | 74 | } |
| 72 | - // 回退到 pgrep | ||
| 73 | return GetChildrenPidsFallback(parentPid); | 75 | return GetChildrenPidsFallback(parentPid); |
| 74 | } | 76 | } |
| 75 | 77 | ||
| 76 | -uint32_t GetPidInfoByCollect(def::ProcessMemPidInfo& pidInfo, CollectInfoMap& pidCollectInfo) | ||
| 77 | -{ | ||
| 78 | - std::unordered_map<uint32_t, size_t> numaDistribution{}; | ||
| 79 | - auto ret = | ||
| 80 | - ProcessMemPidCollect::GetInstance().CollectProcessNumaMemDistribution(pidInfo.configInfo.pid, numaDistribution); | ||
| 81 | - // 读取失败认为进程不存在 | ||
| 82 | - if (ret != UBSE_OK) { | ||
| 83 | - UBSE_LOG_ERROR << "pid=" << std::to_string(pidInfo.configInfo.pid) << " collect failed=" << ret; | ||
| 84 | - pidInfo.processStatus = def::ProcessStatus::INACTIVE; | ||
| 85 | - return ret; | ||
| 86 | - } | ||
| 87 | - pidInfo.processStatus = def::ProcessStatus::IDLE; | ||
| 88 | - pidCollectInfo[pidInfo.configInfo.pid] = numaDistribution; | ||
| 89 | - return UBSE_OK; | ||
| 90 | -} | ||
| 91 | - | ||
| 92 | uint32_t ProcessMemPidCollect::Init() | 78 | uint32_t ProcessMemPidCollect::Init() |
| 93 | { | 79 | { |
| 94 | - const std::string section = "process_mem.pid"; | 80 | + const std::string section = "process_mem"; |
| 95 | - const std::string configKey = "collect.interval"; | 81 | + const std::string collectIntervalKey = "collect_process_interval"; |
| 96 | uint32_t collectInterval = 0; | 82 | uint32_t collectInterval = 0; |
| 97 | - ubse::config::UbseGetUInt(section, configKey, collectInterval); | 83 | + ubse::config::UbseGetUInt(section, collectIntervalKey, collectInterval); |
| 84 | + constexpr uint32_t defaultCollectInterval = 5; | ||
| 98 | constexpr uint32_t maxCollectInterval = 3600; | 85 | constexpr uint32_t maxCollectInterval = 3600; |
| 99 | - constexpr uint32_t defaultCollectInterval = 10; | ||
| 100 | if (collectInterval < 1 || collectInterval > maxCollectInterval) { | 86 | if (collectInterval < 1 || collectInterval > maxCollectInterval) { |
| 101 | collectInterval = defaultCollectInterval; | 87 | collectInterval = defaultCollectInterval; |
| 102 | } | 88 | } |
| 89 | + | ||
| 90 | + const std::string filterRootKey = "filter_root_process"; | ||
| 91 | + filterRootProcesses_ = true; | ||
| 92 | + ubse::config::UbseGetBool(section, filterRootKey, filterRootProcesses_); | ||
| 93 | + | ||
| 94 | + collectExecutor_ = ubse::task_executor::UbseTaskExecutor::Create("ProcMemCollect", 1, 16); | ||
| 95 | + if (collectExecutor_ == nullptr || !collectExecutor_->Start()) { | ||
| 96 | + UBSE_LOG_ERROR << "ProcMemCollect: collectExecutor start failed"; | ||
| 97 | + return UBSE_ERROR; | ||
| 98 | + } | ||
| 99 | + | ||
| 103 | UbseTimerHandlerRegister( | 100 | UbseTimerHandlerRegister( |
| 104 | "numaDistributionCollectTimer", [this]() -> uint32_t { return this->CycleCollectNumaInfo(); }, collectInterval); | 101 | "numaDistributionCollectTimer", [this]() -> uint32_t { return this->CycleCollectNumaInfo(); }, collectInterval); |
| 102 | + UBSE_LOG_INFO << "ProcessMemPidCollect Init: collect_interval=" << collectInterval | ||
| 103 | + << "s, filter_root=" << (filterRootProcesses_ ? "on" : "off"); | ||
| 105 | return UBSE_OK; | 104 | return UBSE_OK; |
| 106 | } | 105 | } |
| 107 | 106 | ||
| 108 | void ProcessMemPidCollect::UnInit() | 107 | void ProcessMemPidCollect::UnInit() |
| 109 | { | 108 | { |
| 110 | UbseTimerHandlerUnregister("numaDistributionCollectTimer"); | 109 | UbseTimerHandlerUnregister("numaDistributionCollectTimer"); |
| 110 | + if (collectExecutor_ != nullptr) { | ||
| 111 | + collectExecutor_->Wait(); | ||
| 112 | + collectExecutor_->Stop(); | ||
| 113 | + } | ||
| 111 | } | 114 | } |
| 112 | 115 | ||
| 113 | uint32_t ProcessMemPidCollect::CycleCollectNumaInfo() | 116 | uint32_t ProcessMemPidCollect::CycleCollectNumaInfo() |
| 114 | { | 117 | { |
| 115 | - CollectInfoMap pidCollectInfo{}; | 118 | + uint64_t roundNum = roundNumber_.fetch_add(1) + 1; |
| 116 | - std::vector<def::ProcessMemPidInfo> pidInfos{}; | 119 | + |
| 117 | - ProcessMemPidInfoManager::GetInstance().GetAllPidInfo(pidInfos); | 120 | + if (collectionRunning_.load(std::memory_order_acquire)) { |
| 118 | - for (auto pidInfo : pidInfos) { | 121 | + UBSE_LOG_INFO << "[process_mem] collect round=" << roundNum |
| 119 | - bool isFaultPid = (pidInfo.processStatus == def::ProcessStatus::FAULT); | 122 | + << " skip: backpressure (prev_round dur=" << prevRoundDurMs_.load() << "ms, not finished)"; |
| 120 | - if (GetPidInfoByCollect(pidInfo, pidCollectInfo) != UBSE_OK) { | 123 | + return UBSE_OK; |
| 121 | - ProcessMemPidInfoManager::GetInstance().UnsetPidInfo(pidInfo.configInfo.pid); | ||
| 122 | - continue; | ||
| 123 | - } | ||
| 124 | - CollectChildPids(pidInfo.configInfo.pid, pidInfo.configInfo, pidCollectInfo); | ||
| 125 | } | 124 | } |
| 126 | - decltype(collectHandlers) handlerCopy; | 125 | + |
| 127 | - { | 126 | + bool prevFinished = prevRoundFinished_.load(std::memory_order_acquire); |
| 128 | - std::shared_lock<std::shared_mutex> lock(collectHandlersMutex); | 127 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " timer fired, dispatching task (prev round dur=" |
| 129 | - handlerCopy = collectHandlers; | 128 | + << (prevFinished ? std::to_string(prevRoundDurMs_.load()) : "N/A") |
| 130 | - } | 129 | + << "ms, finished=" << (prevFinished ? "Y" : "N") << ")"; |
| 131 | - for (const auto& handler : handlerCopy) { | 130 | + |
| 132 | - handler.second(pidCollectInfo); | 131 | + collectionRunning_.store(true, std::memory_order_release); |
| 132 | + prevRoundFinished_.store(false, std::memory_order_release); | ||
| 133 | + | ||
| 134 | + if (!collectExecutor_->Execute([this, roundNum]() { DoCollectRound(roundNum); })) { | ||
| 135 | + UBSE_LOG_ERROR << "[process_mem] collect round=" << roundNum << " enqueue failed"; | ||
| 136 | + prevRoundFinished_.store(true, std::memory_order_release); | ||
| 137 | + collectionRunning_.store(false, std::memory_order_release); | ||
| 133 | } | 138 | } |
| 139 | + | ||
| 134 | return UBSE_OK; | 140 | return UBSE_OK; |
| 135 | } | 141 | } |
| 136 | 142 | ||
| 137 | -void ProcessMemPidCollect::CollectChildPids(pid_t parentPid, const def::ProcessMemPidConfigInfo& parentConfig, | 143 | +std::optional<std::set<pid_t>> ProcessMemPidCollect::ScanProcPids() |
| 138 | - CollectInfoMap& pidCollectInfo) | ||
| 139 | { | 144 | { |
| 140 | - auto childrenPids = GetChildrenPids(parentPid); | 145 | + std::set<pid_t> curPids; |
| 141 | - for (auto pid : childrenPids) { | 146 | + DIR* procDir = opendir("/proc"); |
| 142 | - def::ProcessMemPidInfo childPidInfo{}; | 147 | + if (procDir == nullptr) { |
| 143 | - childPidInfo.ppid = parentPid; | 148 | + UBSE_LOG_ERROR << "[process_mem] collect: opendir(/proc) failed, errno=" << errno; |
| 144 | - childPidInfo.configInfo = parentConfig; | 149 | + return std::nullopt; |
| 145 | - childPidInfo.configInfo.pid = pid; | 150 | + } |
| 146 | - childPidInfo.startTime = ProcessMemPidConfigManager::GetExactStartTime(pid); | ||
| 147 | 151 | ||
| 148 | - auto existingInfo = ProcessMemPidInfoManager::GetInstance().GetPidInfoMap(pid); | 152 | + struct dirent* entry; |
| 149 | - bool alreadyConfigured = | 153 | + while ((entry = readdir(procDir)) != nullptr) { |
| 150 | - (existingInfo.configInfo.pid != -1 && existingInfo.startTime == childPidInfo.startTime); | 154 | + if (entry->d_type != DT_DIR) { |
| 151 | - | ||
| 152 | - if (GetPidInfoByCollect(childPidInfo, pidCollectInfo) != UBSE_OK) { | ||
| 153 | - if (!alreadyConfigured) { | ||
| 154 | - ProcessMemPidInfoManager::GetInstance().UnsetPidInfo(pid); | ||
| 155 | - } | ||
| 156 | continue; | 155 | continue; |
| 157 | } | 156 | } |
| 158 | - | 157 | + const char* name = entry->d_name; |
| 159 | - if (!alreadyConfigured) { | 158 | + if (name[0] < '0' || name[0] > '9') { |
| 160 | - ProcessMemPidInfoManager::GetInstance().SetPidInfoMap(childPidInfo); | 159 | + continue; |
| 161 | } | 160 | } |
| 161 | + bool allDigits = true; | ||
| 162 | + for (const char* p = name; *p != '\0'; ++p) { | ||
| 163 | + if (*p < '0' || *p > '9') { | ||
| 164 | + allDigits = false; | ||
| 165 | + break; | ||
| 166 | + } | ||
| 167 | + } | ||
| 168 | + if (!allDigits) { | ||
| 169 | + continue; | ||
| 170 | + } | ||
| 171 | + pid_t pid = static_cast<pid_t>(strtol(name, nullptr, 10)); | ||
| 172 | + if (pid > 0) { | ||
| 173 | + curPids.insert(pid); | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | + closedir(procDir); | ||
| 177 | + return curPids; | ||
| 178 | +} | ||
| 179 | + | ||
| 180 | +static std::string ReadProcStatComm(pid_t pid) | ||
| 181 | +{ | ||
| 182 | + std::string path = "/proc/" + std::to_string(pid) + "/stat"; | ||
| 183 | + std::ifstream file(path); | ||
| 184 | + if (!file.is_open()) { | ||
| 185 | + return {}; | ||
| 186 | + } | ||
| 187 | + std::string line; | ||
| 188 | + if (!std::getline(file, line)) { | ||
| 189 | + return {}; | ||
| 190 | + } | ||
| 191 | + auto start = line.find('('); | ||
| 192 | + auto end = line.rfind(')'); // comm 可含 '(' 或 ')',必须取最后一个右括号 | ||
| 193 | + if (start == std::string::npos || end == std::string::npos || start >= end) { | ||
| 194 | + return {}; | ||
| 195 | + } | ||
| 196 | + return line.substr(start + 1, end - start - 1); | ||
| 197 | +} | ||
| 198 | + | ||
| 199 | +void HandleExitedPidEntry(ProcessMemPidInfoManager& infoMgr, pid_t pid, const def::ManagedPidEntry& entry, | ||
| 200 | + uint64_t roundNum) | ||
| 201 | +{ | ||
| 202 | + bool hasPidSource = (entry.sources & static_cast<uint8_t>(def::ConfigSource::PID_CONFIG)) != 0; | ||
| 203 | + bool hasNameSource = (entry.sources & static_cast<uint8_t>(def::ConfigSource::NAME_CONFIG)) != 0; | ||
| 204 | + std::string comm = entry.nameConfigName.empty() ? ReadProcStatComm(pid) : entry.nameConfigName; | ||
| 205 | + | ||
| 206 | + if (hasPidSource && hasNameSource) { | ||
| 207 | + infoMgr.RemoveProcMemConfig(true, std::to_string(pid)); | ||
| 208 | + infoMgr.RemovePidSourceFromManagedPid(pid); | ||
| 209 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " pid=" << pid << " comm=" << comm | ||
| 210 | + << " exit, removed pid-config from cache and storage" | ||
| 211 | + << " (kept name-config source)"; | ||
| 212 | + } else if (hasPidSource) { | ||
| 213 | + infoMgr.RemoveProcMemConfig(true, std::to_string(pid)); | ||
| 214 | + infoMgr.RemoveManagedPidEntry(pid); | ||
| 215 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " pid=" << pid << " comm=" << comm | ||
| 216 | + << " exit, removed from cache and storage (sources=pid-config)"; | ||
| 217 | + } else if (hasNameSource) { | ||
| 218 | + infoMgr.RemoveManagedPidEntry(pid); | ||
| 219 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " pid=" << pid << " comm=" << comm | ||
| 220 | + << " exit, removed from cache (sources=name-config)"; | ||
| 221 | + } else if (entry.isChild) { | ||
| 222 | + infoMgr.RemoveManagedPidEntry(pid); | ||
| 223 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " pid=" << pid << " comm=" << comm | ||
| 224 | + << " exit, removed from cache (sources=child)"; | ||
| 162 | } | 225 | } |
| 163 | } | 226 | } |
| 164 | 227 | ||
| 165 | -uint32_t ProcessMemPidCollect::RegisterCollectHandler(const std::string& name, | 228 | +bool ShouldSkipNewPid(const std::map<pid_t, def::ManagedPidEntry>& snapshot, bool filterRoot, pid_t pid, |
| 166 | - NumaMemDistributionCollectHandler handler) | 229 | + size_t& skippedChild, size_t& skippedRoot) |
| 167 | { | 230 | { |
| 168 | - std::unique_lock<std::shared_mutex> lock(collectHandlersMutex); | 231 | + if (filterRoot && GetProcUid(pid) == 0) { |
| 232 | + ++skippedRoot; | ||
| 233 | + return true; | ||
| 234 | + } | ||
| 235 | + auto existingIt = snapshot.find(pid); | ||
| 236 | + if (existingIt != snapshot.end() && existingIt->second.isChild) { | ||
| 237 | + ++skippedChild; | ||
| 238 | + return true; | ||
| 239 | + } | ||
| 240 | + return false; | ||
| 241 | +} | ||
| 242 | + | ||
| 243 | +bool BuildNameConfigMap(ProcessMemPidInfoManager& infoMgr, | ||
| 244 | + std::unordered_map<std::string, def::ProcessMemNewConfigInfo>& nameToConfig) | ||
| 245 | +{ | ||
| 246 | + std::vector<def::ProcessMemNewConfigInfo> allConfigs; | ||
| 247 | + infoMgr.GetAllProcMemConfigs(allConfigs); | ||
| 248 | + | ||
| 249 | + std::set<std::string> configNameSet; | ||
| 250 | + for (const auto& cfg : allConfigs) { | ||
| 251 | + if (!cfg.isPid) { | ||
| 252 | + configNameSet.insert(cfg.identifier); | ||
| 253 | + nameToConfig[cfg.identifier] = cfg; | ||
| 254 | + } | ||
| 255 | + } | ||
| 256 | + return !configNameSet.empty(); | ||
| 257 | +} | ||
| 258 | + | ||
| 259 | +bool MatchNewPidComm(ProcessMemPidInfoManager& infoMgr, pid_t pid, | ||
| 260 | + const std::unordered_map<std::string, def::ProcessMemNewConfigInfo>& nameToConfig, | ||
| 261 | + uint64_t roundNum) | ||
| 262 | +{ | ||
| 263 | + std::string comm = ReadProcStatComm(pid); | ||
| 264 | + if (comm.empty()) { | ||
| 265 | + return false; | ||
| 266 | + } | ||
| 267 | + auto cfgIt = nameToConfig.find(comm); | ||
| 268 | + if (cfgIt == nameToConfig.end()) { | ||
| 269 | + return false; | ||
| 270 | + } | ||
| 271 | + | ||
| 272 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " pid=" << pid << " comm=" << comm | ||
| 273 | + << " discovered: NAME match"; | ||
| 274 | + | ||
| 275 | + infoMgr.AddNameSourceToManagedPid(pid, comm, cfgIt->second.maxMemory, cfgIt->second.remoteRatio); | ||
| 276 | + return true; | ||
| 277 | +} | ||
| 278 | + | ||
| 279 | +bool ReadProcStatusVmRss(pid_t pid, uint64_t& vmRssKb, uint32_t& uid) | ||
| 280 | +{ | ||
| 281 | + std::string path = "/proc/" + std::to_string(pid) + "/status"; | ||
| 282 | + std::ifstream file(path); | ||
| 283 | + if (!file.is_open()) { | ||
| 284 | + return false; | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | + bool foundVmRss = false; | ||
| 288 | + bool foundUid = false; | ||
| 289 | + std::string line; | ||
| 290 | + while (std::getline(file, line)) { | ||
| 291 | + if (line.compare(0, 6, "VmRSS:") == 0) { | ||
| 292 | + std::istringstream iss(line.substr(6)); | ||
| 293 | + if (iss >> vmRssKb) { | ||
| 294 | + foundVmRss = true; | ||
| 295 | + } | ||
| 296 | + } | ||
| 297 | + if (line.compare(0, 4, "Uid:") == 0) { | ||
| 298 | + std::istringstream iss(line.substr(4)); | ||
| 299 | + iss >> uid; | ||
| 300 | + foundUid = true; | ||
| 301 | + } | ||
| 302 | + if (foundVmRss && foundUid) { | ||
| 303 | + break; | ||
| 304 | + } | ||
| 305 | + } | ||
| 306 | + return foundVmRss; | ||
| 307 | +} | ||
| 308 | + | ||
| 309 | +bool ComputePidDiff(const std::set<pid_t>& lastPidSet, const std::set<pid_t>& curPids, std::set<pid_t>& newPids, | ||
| 310 | + std::set<pid_t>& exitedPids) | ||
| 311 | +{ | ||
| 312 | + if (lastPidSet.empty()) { | ||
| 313 | + newPids = curPids; | ||
| 314 | + return true; | ||
| 315 | + } | ||
| 316 | + for (pid_t pid : curPids) { | ||
| 317 | + if (lastPidSet.find(pid) == lastPidSet.end()) { | ||
| 318 | + newPids.insert(pid); | ||
| 319 | + } | ||
| 320 | + } | ||
| 321 | + for (pid_t pid : lastPidSet) { | ||
| 322 | + if (curPids.find(pid) == curPids.end()) { | ||
| 323 | + exitedPids.insert(pid); | ||
| 324 | + } | ||
| 325 | + } | ||
| 326 | + return false; | ||
| 327 | +} | ||
| 328 | + | ||
| 329 | +size_t DispatchVmRssCallbacks(const std::unordered_map<std::string, VmRssCollectHandler>& handlers, | ||
| 330 | + const PidCollectInfoMap& results) | ||
| 331 | +{ | ||
| 332 | + size_t callbackEntries = 0; | ||
| 333 | + for (const auto& handler : handlers) { | ||
| 334 | + for (const auto& [pid, entry] : results.entries) { | ||
| 335 | + if (!entry.rootFiltered) { | ||
| 336 | + ++callbackEntries; | ||
| 337 | + } | ||
| 338 | + } | ||
| 339 | + handler.second(results); | ||
| 340 | + } | ||
| 341 | + return callbackEntries; | ||
| 342 | +} | ||
| 343 | + | ||
| 344 | +size_t LogRoundSummary(uint64_t roundNum, const PidCollectInfoMap& results, size_t managed, int64_t durMs, | ||
| 345 | + size_t prevManagedCount) | ||
| 346 | +{ | ||
| 347 | + size_t collected = 0; | ||
| 348 | + size_t skipRoot = 0; | ||
| 349 | + for (const auto& [pid, entry] : results.entries) { | ||
| 350 | + if (!entry.rootFiltered) { | ||
| 351 | + ++collected; | ||
| 352 | + } else { | ||
| 353 | + ++skipRoot; | ||
| 354 | + } | ||
| 355 | + } | ||
| 356 | + | ||
| 357 | + UBSE_LOG_INFO << "[process_mem] collect round=" << roundNum << " managed=" << managed << " collected=" << collected | ||
| 358 | + << " skip(root=" << skipRoot << ") dur=" << durMs << "ms"; | ||
| 359 | + | ||
| 360 | + if (prevManagedCount > 0) { | ||
| 361 | + int64_t delta = static_cast<int64_t>(managed) - static_cast<int64_t>(prevManagedCount); | ||
| 362 | + int64_t absDelta = (delta < 0) ? -delta : delta; | ||
| 363 | + double percentChange = (prevManagedCount > 0) ? | ||
| 364 | + (static_cast<double>(absDelta) / static_cast<double>(prevManagedCount)) * 100.0 : | ||
| 365 | + 0.0; | ||
| 366 | + if (percentChange > 30.0 || absDelta > 5) { | ||
| 367 | + UBSE_LOG_INFO << "[process_mem] collect managedPidCache size changed: " << prevManagedCount << " -> " | ||
| 368 | + << managed << " (delta=" << delta << ")"; | ||
| 369 | + } | ||
| 370 | + } | ||
| 371 | + return managed; | ||
| 372 | +} | ||
| 373 | + | ||
| 374 | +void ProcessMemPidCollect::HandleExitedPids(const std::set<pid_t>& exitedPids, uint64_t roundNum) | ||
| 375 | +{ | ||
| 376 | + if (exitedPids.empty()) { | ||
| 377 | + return; | ||
| 378 | + } | ||
| 379 | + | ||
| 380 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 381 | + std::vector<std::pair<pid_t, def::ManagedPidEntry>> toProcess; | ||
| 382 | + auto cache = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 383 | + for (pid_t pid : exitedPids) { | ||
| 384 | + auto it = cache.find(pid); | ||
| 385 | + if (it != cache.end()) { | ||
| 386 | + toProcess.push_back({pid, it->second}); | ||
| 387 | + } | ||
| 388 | + } | ||
| 389 | + | ||
| 390 | + for (const auto& [pid, entry] : toProcess) { | ||
| 391 | + decision::ProcessMemPidDecision::GetInstance().HandlePidExited(pid); | ||
| 392 | + HandleExitedPidEntry(infoMgr, pid, entry, roundNum); | ||
| 393 | + } | ||
| 394 | +} | ||
| 395 | + | ||
| 396 | +void ProcessMemPidCollect::HandleNewPidsByName(const std::set<pid_t>& newPids, uint64_t roundNum) | ||
| 397 | +{ | ||
| 398 | + if (newPids.empty()) { | ||
| 399 | + return; | ||
| 400 | + } | ||
| 401 | + | ||
| 402 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 403 | + | ||
| 404 | + std::unordered_map<std::string, def::ProcessMemNewConfigInfo> nameToConfig; | ||
| 405 | + if (!BuildNameConfigMap(infoMgr, nameToConfig)) { | ||
| 406 | + return; | ||
| 407 | + } | ||
| 408 | + | ||
| 409 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " step=name_match scan_new=" << newPids.size() | ||
| 410 | + << " name_count=" << nameToConfig.size(); | ||
| 411 | + | ||
| 412 | + size_t matched = 0; | ||
| 413 | + size_t skippedChild = 0; | ||
| 414 | + size_t skippedRoot = 0; | ||
| 415 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 416 | + for (pid_t pid : newPids) { | ||
| 417 | + if (ShouldSkipNewPid(snapshot, filterRootProcesses_, pid, skippedChild, skippedRoot)) { | ||
| 418 | + continue; | ||
| 419 | + } | ||
| 420 | + if (MatchNewPidComm(infoMgr, pid, nameToConfig, roundNum)) { | ||
| 421 | + ++matched; | ||
| 422 | + } | ||
| 423 | + } | ||
| 424 | + | ||
| 425 | + if (skippedChild > 0 || skippedRoot > 0) { | ||
| 426 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum | ||
| 427 | + << " step=name_match skipped_child=" << skippedChild << " skipped_root=" << skippedRoot; | ||
| 428 | + } | ||
| 429 | + | ||
| 430 | + auto cache = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 431 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " step=name_match matched=" << matched | ||
| 432 | + << " total_managed=" << cache.size(); | ||
| 433 | +} | ||
| 434 | + | ||
| 435 | +void ProcessMemPidCollect::CollectVmRss(PidCollectInfoMap& results, uint64_t roundNum) | ||
| 436 | +{ | ||
| 437 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 438 | + auto cache = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 439 | + | ||
| 440 | + size_t passCount = 0; | ||
| 441 | + size_t skipRootCount = 0; | ||
| 442 | + | ||
| 443 | + for (const auto& [pid, entry] : cache) { | ||
| 444 | + uint64_t vmRssKb = 0; | ||
| 445 | + uint32_t uid = 0; | ||
| 446 | + if (!ReadProcStatusVmRss(pid, vmRssKb, uid)) { | ||
| 447 | + continue; | ||
| 448 | + } | ||
| 449 | + | ||
| 450 | + std::string comm = entry.nameConfigName.empty() ? ReadProcStatComm(pid) : entry.nameConfigName; | ||
| 451 | + | ||
| 452 | + if (filterRootProcesses_ && uid == 0) { | ||
| 453 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " pid=" << pid << " comm=" << comm | ||
| 454 | + << " skip: root filtered (uid=" << uid << ")"; | ||
| 455 | + ++skipRootCount; | ||
| 456 | + results.entries[pid] = {vmRssKb, true}; | ||
| 457 | + continue; | ||
| 458 | + } | ||
| 459 | + | ||
| 460 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " pid=" << pid << " comm=" << comm | ||
| 461 | + << " rss=" << vmRssKb; | ||
| 462 | + | ||
| 463 | + ++passCount; | ||
| 464 | + results.entries[pid] = {vmRssKb, false}; | ||
| 465 | + } | ||
| 466 | + | ||
| 467 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " step=filter pass=" << passCount | ||
| 468 | + << " skip_root=" << skipRootCount; | ||
| 469 | +} | ||
| 470 | + | ||
| 471 | +void ProcessMemPidCollect::CollectChildProcesses(uint64_t roundNum, const std::set<pid_t>& curPids) | ||
| 472 | +{ | ||
| 473 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 474 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 475 | + | ||
| 476 | + size_t discovered = 0; | ||
| 477 | + size_t staleRemoved = 0; | ||
| 478 | + | ||
| 479 | + std::set<pid_t> parentPids; | ||
| 480 | + for (const auto& [pid, entry] : snapshot) { | ||
| 481 | + if (!entry.isChild) { | ||
| 482 | + parentPids.insert(pid); | ||
| 483 | + } | ||
| 484 | + } | ||
| 485 | + | ||
| 486 | + for (pid_t parentPid : parentPids) { | ||
| 487 | + auto children = GetChildrenPids(parentPid); | ||
| 488 | + auto parentIt = snapshot.find(parentPid); | ||
| 489 | + uint64_t maxMemory = (parentIt != snapshot.end()) ? parentIt->second.maxMemory : 0; | ||
| 490 | + double remoteRatio = (parentIt != snapshot.end()) ? parentIt->second.remoteRatio : 0.0; | ||
| 491 | + | ||
| 492 | + for (pid_t childPid : children) { | ||
| 493 | + if (filterRootProcesses_ && GetProcUid(childPid) == 0) { | ||
| 494 | + continue; | ||
| 495 | + } | ||
| 496 | + infoMgr.AddChildSourceToManagedPid(childPid, parentPid, maxMemory, remoteRatio); | ||
| 497 | + ++discovered; | ||
| 498 | + } | ||
| 499 | + } | ||
| 500 | + | ||
| 501 | + std::vector<pid_t> staleChildren; | ||
| 502 | + for (const auto& [pid, entry] : snapshot) { | ||
| 503 | + if (entry.isChild && curPids.find(pid) == curPids.end()) { | ||
| 504 | + staleChildren.push_back(pid); | ||
| 505 | + } | ||
| 506 | + } | ||
| 507 | + for (pid_t pid : staleChildren) { | ||
| 508 | + infoMgr.RemoveManagedPidEntry(pid); | ||
| 509 | + ++staleRemoved; | ||
| 510 | + } | ||
| 511 | + | ||
| 512 | + if (discovered > 0 || staleRemoved > 0) { | ||
| 513 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " step=children discovered=" << discovered | ||
| 514 | + << " stale_removed=" << staleRemoved << " parent_count=" << parentPids.size(); | ||
| 515 | + } | ||
| 516 | +} | ||
| 517 | + | ||
| 518 | +void ProcessMemPidCollect::DoCollectRound(uint64_t roundNum) | ||
| 519 | +{ | ||
| 520 | + auto roundStart = std::chrono::steady_clock::now(); | ||
| 521 | + | ||
| 522 | + auto scanRet = ScanProcPids(); | ||
| 523 | + if (!scanRet) { | ||
| 524 | + UBSE_LOG_ERROR << "[process_mem] collect round=" << roundNum << " /proc scan failed, skip round"; | ||
| 525 | + prevRoundFinished_.store(true, std::memory_order_release); | ||
| 526 | + collectionRunning_.store(false, std::memory_order_release); | ||
| 527 | + return; | ||
| 528 | + } | ||
| 529 | + std::set<pid_t> curPids = std::move(*scanRet); | ||
| 530 | + | ||
| 531 | + std::set<pid_t> newPids; | ||
| 532 | + std::set<pid_t> exitedPids; | ||
| 533 | + bool coldStart = ComputePidDiff(lastPidSet_, curPids, newPids, exitedPids); | ||
| 534 | + if (coldStart) { | ||
| 535 | + UBSE_LOG_INFO << "[process_mem] collect round=" << roundNum << " cold_start: lastPidSet empty, full scan of " | ||
| 536 | + << curPids.size() << " pids"; | ||
| 537 | + } | ||
| 538 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " step=diff cur_pids=" << curPids.size() | ||
| 539 | + << " new=" << newPids.size() << " exited=" << exitedPids.size() | ||
| 540 | + << (coldStart ? " (cold_start)" : ""); | ||
| 541 | + | ||
| 542 | + HandleExitedPids(exitedPids, roundNum); | ||
| 543 | + | ||
| 544 | + HandleNewPidsByName(newPids, roundNum); | ||
| 545 | + | ||
| 546 | + CollectChildProcesses(roundNum, curPids); | ||
| 547 | + | ||
| 548 | + PidCollectInfoMap results; | ||
| 549 | + CollectVmRssDispatch(results, roundNum); | ||
| 550 | + | ||
| 551 | + decltype(vmRssHandlers_) handlerCopy; | ||
| 552 | + std::shared_lock<std::shared_mutex> lock(vmRssHandlersMutex_); | ||
| 553 | + handlerCopy = vmRssHandlers_; | ||
| 554 | + lock.unlock(); | ||
| 555 | + size_t callbackEntries = DispatchVmRssCallbacks(handlerCopy, results); | ||
| 556 | + UBSE_LOG_DEBUG << "[process_mem] collect round=" << roundNum << " step=callback entries=" << callbackEntries; | ||
| 557 | + | ||
| 558 | + lastPidSet_ = std::move(curPids); | ||
| 559 | + | ||
| 560 | + prevRoundDurMs_.store( | ||
| 561 | + std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - roundStart).count(), | ||
| 562 | + std::memory_order_release); | ||
| 563 | + prevRoundFinished_.store(true, std::memory_order_release); | ||
| 564 | + collectionRunning_.store(false, std::memory_order_release); | ||
| 565 | + | ||
| 566 | + prevManagedCount_ = LogRoundSummary(roundNum, results, | ||
| 567 | + ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot().size(), | ||
| 568 | + prevRoundDurMs_.load(), prevManagedCount_); | ||
| 569 | +} | ||
| 570 | + | ||
| 571 | +void ProcessMemPidCollect::CollectVmRssDispatch(PidCollectInfoMap& results, uint64_t roundNum) | ||
| 572 | +{ | ||
| 573 | + | ||
| 574 | + if (collectVmRssOverride_) { | ||
| 575 | + collectVmRssOverride_(results, roundNum); | ||
| 576 | + return; | ||
| 577 | + } | ||
| 578 | + | ||
| 579 | + CollectVmRss(results, roundNum); | ||
| 580 | +} | ||
| 581 | + | ||
| 582 | +uint32_t ProcessMemPidCollect::RegisterVmRssCollectHandler(const std::string& name, VmRssCollectHandler handler) | ||
| 583 | +{ | ||
| 584 | + std::unique_lock<std::shared_mutex> lock(vmRssHandlersMutex_); | ||
| 169 | if (!handler) { | 585 | if (!handler) { |
| 170 | - UBSE_LOG_ERROR << "handler=" << name << " is nullptr"; | 586 | + UBSE_LOG_ERROR << "VmRss handler=" << name << " is nullptr"; |
| 171 | return UBSE_ERROR; | 587 | return UBSE_ERROR; |
| 172 | } | 588 | } |
| 173 | - collectHandlers[name] = handler; | 589 | + vmRssHandlers_[name] = handler; |
| 174 | return UBSE_OK; | 590 | return UBSE_OK; |
| 175 | } | 591 | } |
| 176 | 592 | ||
| 177 | -void ProcessMemPidCollect::UnRegisterCollectHandler(const std::string& name) | 593 | +void ProcessMemPidCollect::UnRegisterVmRssCollectHandler(const std::string& name) |
| 178 | { | 594 | { |
| 179 | - std::unique_lock<std::shared_mutex> lock(collectHandlersMutex); | 595 | + std::unique_lock<std::shared_mutex> lock(vmRssHandlersMutex_); |
| 180 | - collectHandlers.erase(name); | 596 | + vmRssHandlers_.erase(name); |
| 181 | } | 597 | } |
| 182 | 598 | ||
| 599 | + | ||
| 600 | +void ProcessMemPidCollect::SetCollectVmRssOverride(std::function<void(PidCollectInfoMap&, uint64_t)> fn) | ||
| 601 | +{ | ||
| 602 | + collectVmRssOverride_ = std::move(fn); | ||
| 603 | +} | ||
| 604 | + | ||
| 605 | + | ||
| 183 | size_t GetPageSize() | 606 | size_t GetPageSize() |
| 184 | { | 607 | { |
| 185 | long pageSize = sysconf(_SC_PAGESIZE); | 608 | long pageSize = sysconf(_SC_PAGESIZE); |
| @@ -204,7 +627,6 @@ bool GetNumaInfoFromToken(const std::string& token, const size_t pageSize, | |||
| 204 | size_t pages = std::stoull(pagesStr); | 627 | size_t pages = std::stoull(pagesStr); |
| 205 | numaMemDistribution[numa] += pages * pageSize; | 628 | numaMemDistribution[numa] += pages * pageSize; |
| 206 | } catch (const std::exception& e) { | 629 | } catch (const std::exception& e) { |
| 207 | - // 忽略非法格式 | ||
| 208 | return false; | 630 | return false; |
| 209 | } | 631 | } |
| 210 | } | 632 | } |
| @@ -239,7 +661,60 @@ uint32_t ProcessMemPidCollect::CollectProcessNumaMemDistribution( | |||
| 239 | } | 661 | } |
| 240 | file.close(); | 662 | file.close(); |
| 241 | 663 | ||
| 242 | - for (const auto& [key, value] : numaMemDistribution) {} | ||
| 243 | return UBSE_OK; | 664 | return UBSE_OK; |
| 244 | } | 665 | } |
| 666 | + | ||
| 667 | +int32_t GetProcUid(pid_t pid) | ||
| 668 | +{ | ||
| 669 | + std::ifstream file("/proc/" + std::to_string(pid) + "/status"); | ||
| 670 | + std::string line; | ||
| 671 | + while (std::getline(file, line)) { | ||
| 672 | + if (line.compare(0, 4, "Uid:") == 0) { | ||
| 673 | + std::istringstream iss(line.substr(4)); | ||
| 674 | + uint32_t uid = 0; | ||
| 675 | + iss >> uid; | ||
| 676 | + return static_cast<int32_t>(uid); | ||
| 677 | + } | ||
| 678 | + } | ||
| 679 | + return -1; | ||
| 680 | +} | ||
| 681 | + | ||
| 682 | +std::vector<PidNameEntry> FindPidsByName(const std::string& procName) | ||
| 683 | +{ | ||
| 684 | + std::vector<PidNameEntry> result; | ||
| 685 | + try { | ||
| 686 | + for (const auto& entry : std::filesystem::directory_iterator("/proc")) { | ||
| 687 | + if (!entry.is_directory()) { | ||
| 688 | + continue; | ||
| 689 | + } | ||
| 690 | + std::string pidStr = entry.path().filename().string(); | ||
| 691 | + if (pidStr.empty() || !std::all_of(pidStr.begin(), pidStr.end(), ::isdigit)) { | ||
| 692 | + continue; | ||
| 693 | + } | ||
| 694 | + std::ifstream commFile(entry.path().string() + "/comm"); | ||
| 695 | + std::string comm; | ||
| 696 | + if (!std::getline(commFile, comm) || comm != procName) { | ||
| 697 | + continue; | ||
| 698 | + } | ||
| 699 | + pid_t pid; | ||
| 700 | + try { | ||
| 701 | + pid = std::stoi(pidStr); | ||
| 702 | + } catch (const std::exception& e) { | ||
| 703 | + UBSE_LOG_DEBUG << "FindPidsByName: stoi failed for pidStr=" << pidStr << ": " << e.what(); | ||
| 704 | + continue; | ||
| 705 | + } | ||
| 706 | + long startTime = manager::ProcessMemPidConfigManager::GetExactStartTime(pid); | ||
| 707 | + if (startTime != 0) { | ||
| 708 | + result.push_back({pid, startTime}); | ||
| 709 | + } else { | ||
| 710 | + UBSE_LOG_DEBUG << "FindPidsByName: process died mid-scan, pid=" << pid << ", name=" << procName; | ||
| 711 | + } | ||
| 712 | + } | ||
| 713 | + } catch (const std::exception& e) { | ||
| 714 | + UBSE_LOG_ERROR << "FindPidsByName: /proc scan failed: " << e.what(); | ||
| 715 | + } | ||
| 716 | + UBSE_LOG_DEBUG << "FindPidsByName: scanned /proc entries, matched " << result.size() | ||
| 717 | + << " PIDs for name=" << procName; | ||
| 718 | + return result; | ||
| 719 | +} | ||
| 245 | } // namespace process_mem::collect | 720 | } // namespace process_mem::collect |
| @@ -14,19 +14,30 @@ | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | + | ||
| 17 | 18 | ||
| 18 | 19 | ||
| 20 | + | ||
| 21 | + | ||
| 19 | 22 | ||
| 20 | 23 | ||
| 21 | 24 | ||
| 22 | 25 | ||
| 26 | + | ||
| 23 | 27 | ||
| 24 | 28 | ||
| 25 | namespace process_mem::collect { | 29 | namespace process_mem::collect { |
| 26 | 30 | ||
| 27 | -using CollectInfoMap = std::unordered_map<pid_t, std::unordered_map<uint32_t, size_t>>; | 31 | +struct PidCollectEntry { |
| 28 | -// std::unordered_map<pid, std::unordered_map<numa, memSize>>, 每个numa上内存分布单位为 byte. | 32 | + uint64_t vmRssKb{0}; |
| 29 | -using NumaMemDistributionCollectHandler = std::function<void(CollectInfoMap)>; | 33 | + bool rootFiltered{false}; |
| 34 | +}; | ||
| 35 | + | ||
| 36 | +struct PidCollectInfoMap { | ||
| 37 | + std::unordered_map<pid_t, PidCollectEntry> entries; | ||
| 38 | +}; | ||
| 39 | + | ||
| 40 | +using VmRssCollectHandler = std::function<void(const PidCollectInfoMap&)>; | ||
| 30 | 41 | ||
| 31 | class ProcessMemPidCollect { | 42 | class ProcessMemPidCollect { |
| 32 | public: | 43 | public: |
| @@ -42,20 +53,60 @@ public: | |||
| 42 | 53 | ||
| 43 | uint32_t CycleCollectNumaInfo(); | 54 | uint32_t CycleCollectNumaInfo(); |
| 44 | 55 | ||
| 45 | - uint32_t RegisterCollectHandler(const std::string& name, NumaMemDistributionCollectHandler handler); | 56 | + uint32_t RegisterVmRssCollectHandler(const std::string& name, VmRssCollectHandler handler); |
| 46 | 57 | ||
| 47 | - void UnRegisterCollectHandler(const std::string& name); | 58 | + void UnRegisterVmRssCollectHandler(const std::string& name); |
| 48 | 59 | ||
| 49 | uint32_t CollectProcessNumaMemDistribution(pid_t pid, std::unordered_map<uint32_t, size_t>& numaMemDistribution); | 60 | uint32_t CollectProcessNumaMemDistribution(pid_t pid, std::unordered_map<uint32_t, size_t>& numaMemDistribution); |
| 50 | 61 | ||
| 51 | - void CollectChildPids(pid_t parentPid, const def::ProcessMemPidConfigInfo& parentConfig, | 62 | +#ifdef UB_ENVIRONMENT |
| 52 | - CollectInfoMap& pidCollectInfo); | 63 | + void SetCollectVmRssOverride(std::function<void(PidCollectInfoMap&, uint64_t)> fn); |
| 64 | + | ||
| 53 | 65 | ||
| 54 | private: | 66 | private: |
| 55 | - std::unordered_map<std::string, NumaMemDistributionCollectHandler> collectHandlers{}; | 67 | + void DoCollectRound(uint64_t roundNum); |
| 56 | 68 | ||
| 57 | - std::shared_mutex collectHandlersMutex{}; | 69 | + std::optional<std::set<pid_t>> ScanProcPids(); |
| 70 | + | ||
| 71 | + void HandleExitedPids(const std::set<pid_t>& exitedPids, uint64_t roundNum); | ||
| 72 | + | ||
| 73 | + void HandleNewPidsByName(const std::set<pid_t>& newPids, uint64_t roundNum); | ||
| 74 | + | ||
| 75 | + void CollectVmRss(PidCollectInfoMap& results, uint64_t roundNum); | ||
| 76 | + | ||
| 77 | + void CollectVmRssDispatch(PidCollectInfoMap& results, uint64_t roundNum); | ||
| 78 | + | ||
| 79 | + void CollectChildProcesses(uint64_t roundNum, const std::set<pid_t>& curPids); | ||
| 80 | + | ||
| 81 | + std::set<pid_t> lastPidSet_{}; | ||
| 82 | + | ||
| 83 | + ubse::task_executor::UbseTaskExecutorPtr collectExecutor_{}; | ||
| 84 | + std::atomic<bool> collectionRunning_{false}; | ||
| 85 | + std::atomic<uint64_t> roundNumber_{0}; | ||
| 86 | + std::atomic<bool> prevRoundFinished_{true}; | ||
| 87 | + std::atomic<int64_t> prevRoundDurMs_{0}; | ||
| 88 | + | ||
| 89 | + bool filterRootProcesses_{true}; | ||
| 90 | + | ||
| 91 | + size_t prevManagedCount_{0}; | ||
| 92 | + | ||
| 93 | + std::unordered_map<std::string, VmRssCollectHandler> vmRssHandlers_{}; | ||
| 94 | + std::shared_mutex vmRssHandlersMutex_{}; | ||
| 95 | + | ||
| 96 | + | ||
| 97 | + std::function<void(PidCollectInfoMap&, uint64_t)> collectVmRssOverride_{}; | ||
| 98 | + | ||
| 58 | }; | 99 | }; |
| 100 | + | ||
| 101 | +struct PidNameEntry { | ||
| 102 | + pid_t pid; | ||
| 103 | + long startTime; | ||
| 104 | +}; | ||
| 105 | + | ||
| 106 | +std::vector<PidNameEntry> FindPidsByName(const std::string& procName); | ||
| 107 | + | ||
| 108 | +int32_t GetProcUid(pid_t pid); | ||
| 109 | + | ||
| 59 | } // namespace process_mem::collect | 110 | } // namespace process_mem::collect |
| 60 | 111 | ||
| 61 | -#endif // PROCESS_MEM_PID_COLLECT_H | 112 | +#endif |
| @@ -13,7 +13,6 @@ | |||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | - | ||
| 17 | 16 | ||
| 18 | 17 | ||
| 19 | 18 | ||
| @@ -22,76 +21,18 @@ | |||
| 22 | 21 | ||
| 23 | namespace process_mem::manager { | 22 | namespace process_mem::manager { |
| 24 | UBSE_DEFINE_THIS_MODULE("process_mem"); | 23 | UBSE_DEFINE_THIS_MODULE("process_mem"); |
| 25 | -static const std::string PID_CONFIG_KEY_PREFIX = "pidConfig_"; | ||
| 26 | -void ProcessMemPidConfigManager::PersistPidConfigInfo(const def::ProcessMemPidInfo& pidInfo) | ||
| 27 | -{ | ||
| 28 | - std::string key = std::to_string(pidInfo.configInfo.pid); | ||
| 29 | - ubse::serial::UbseSerialization serializer; | ||
| 30 | - auto ret = pidInfo.SerializePidInfo(serializer); | ||
| 31 | - if (ret != UBSE_OK) { | ||
| 32 | - UBSE_LOG_ERROR << "SerialPidDefInfo failed, " << ubse::log::FormatRetCode(ret); | ||
| 33 | - return; | ||
| 34 | - } | ||
| 35 | - UbseByteBuffer byteBuffer{}; | ||
| 36 | - byteBuffer.data = serializer.GetBuffer(); | ||
| 37 | - byteBuffer.len = serializer.GetLength(); | ||
| 38 | - ret = ubse::storage::UbseStoragePutData(PID_CONFIG_KEY_PREFIX, key, &byteBuffer); | ||
| 39 | - if (ret != UBSE_OK) { | ||
| 40 | - UBSE_LOG_ERROR << "UbseStoragePutData failed, " << ubse::log::FormatRetCode(ret); | ||
| 41 | - return; | ||
| 42 | - } | ||
| 43 | -} | ||
| 44 | - | ||
| 45 | -void ProcessMemPidConfigManager::GetAllPersistedPidConfigInfo(std::vector<def::ProcessMemPidInfo>& pidInfos) | ||
| 46 | -{ | ||
| 47 | - std::vector<std::string> fileNames{}; | ||
| 48 | - try { | ||
| 49 | - // 1. 遍历目录 | ||
| 50 | - for (const auto& entry : std::filesystem::directory_iterator(ubse::storage::DB_STORE_DIR)) { | ||
| 51 | - // 2. 过滤:只获取普通文件(排除文件夹) | ||
| 52 | - if (entry.is_regular_file()) { | ||
| 53 | - // 3. 获取文件名(不含路径) | ||
| 54 | - std::string fileName = entry.path().filename().string(); | ||
| 55 | - if (fileName.size() < PID_CONFIG_KEY_PREFIX.size()) { | ||
| 56 | - continue; | ||
| 57 | - } | ||
| 58 | - if (fileName.compare(0, PID_CONFIG_KEY_PREFIX.size(), PID_CONFIG_KEY_PREFIX) != 0) { | ||
| 59 | - continue; | ||
| 60 | - } | ||
| 61 | - fileNames.push_back(fileName); | ||
| 62 | - UBSE_LOG_INFO << "Get one pid file:" << fileName; | ||
| 63 | - } | ||
| 64 | - } | ||
| 65 | - } catch (const std::exception& e) { | ||
| 66 | - UBSE_LOG_ERROR << "Failed to read directory: " << ubse::storage::DB_STORE_DIR << ", error: " << e.what(); | ||
| 67 | - return; | ||
| 68 | - } | ||
| 69 | - | ||
| 70 | - // 遍历所有文件获取所有的pid | ||
| 71 | - auto ret = UBSE_OK; | ||
| 72 | - for (const auto& pidFileName : fileNames) { | ||
| 73 | - auto pidStr = pidFileName.substr(PID_CONFIG_KEY_PREFIX.size()); | ||
| 74 | - ret = ubse::storage::UbseStorageQueryData(PID_CONFIG_KEY_PREFIX, pidStr, &pidInfos, QueryPidConfigCallback); | ||
| 75 | - if (ret != UBSE_OK) { | ||
| 76 | - UBSE_LOG_ERROR << "Failed to query pid config for pid: " << pidStr; | ||
| 77 | - continue; | ||
| 78 | - } | ||
| 79 | - } | ||
| 80 | - return; | ||
| 81 | -} | ||
| 82 | - | ||
| 83 | uint64_t ProcessMemPidConfigManager::GetExactStartTime(pid_t pid) | 24 | uint64_t ProcessMemPidConfigManager::GetExactStartTime(pid_t pid) |
| 84 | { | 25 | { |
| 85 | std::string path = "/proc/" + std::to_string(pid) + "/stat"; | 26 | std::string path = "/proc/" + std::to_string(pid) + "/stat"; |
| 86 | FILE* file = fopen(path.c_str(), "r"); | 27 | FILE* file = fopen(path.c_str(), "r"); |
| 87 | if (!file) { | 28 | if (!file) { |
| 29 | + UBSE_LOG_WARN << "GetExactStartTime: fopen failed for pid=" << pid << ", path=" << path << ", errno=" << errno | ||
| 30 | + << " (" << std::strerror(errno) << ")"; | ||
| 88 | return 0; | 31 | return 0; |
| 89 | } | 32 | } |
| 90 | 33 | ||
| 91 | - char buf[1024]; | 34 | + unsigned long long starttime = 0; |
| 92 | - unsigned long long starttime = 0; // 使用 unsigned long long 接收 | ||
| 93 | 35 | ||
| 94 | - // 使用 %*[^)] 跳过带空格的进程名, 使用 %llu 匹配 64 位无符号整数 | ||
| 95 | if (fscanf_s(file, | 36 | if (fscanf_s(file, |
| 96 | "%*d %*[^)] %*c %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*lu %*lu %*d %*d %*d %*d %*d %*d %llu", | 37 | "%*d %*[^)] %*c %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*lu %*lu %*d %*d %*d %*d %*d %*d %llu", |
| 97 | &starttime) == 1) { | 38 | &starttime) == 1) { |
| @@ -101,59 +42,171 @@ uint64_t ProcessMemPidConfigManager::GetExactStartTime(pid_t pid) | |||
| 101 | return static_cast<uint64_t>(starttime); | 42 | return static_cast<uint64_t>(starttime); |
| 102 | } | 43 | } |
| 103 | 44 | ||
| 45 | + UBSE_LOG_WARN << "GetExactStartTime: fscanf_s failed to parse stat for pid=" << pid << ", path=" << path | ||
| 46 | + << ", errno=" << errno << " (" << std::strerror(errno) << ")"; | ||
| 104 | if (fclose(file) != 0) { | 47 | if (fclose(file) != 0) { |
| 105 | UBSE_LOG_WARN << "fclose_failed errno=" << errno << " error=" << std::strerror(errno); | 48 | UBSE_LOG_WARN << "fclose_failed errno=" << errno << " error=" << std::strerror(errno); |
| 106 | } | 49 | } |
| 107 | return 0; | 50 | return 0; |
| 108 | } | 51 | } |
| 109 | 52 | ||
| 110 | -void ProcessMemPidConfigManager::QueryPidConfigCallback(const std::string& keyPrefix, const std::string& key, | 53 | +namespace { |
| 111 | - const UbseByteBuffer& buff, void* ctx) | 54 | +const std::string& GetProcMemKeyPrefix(bool isPid) |
| 55 | +{ | ||
| 56 | + return isPid ? def::PROC_MEM_PID_KEY_PREFIX : def::PROC_MEM_NAME_KEY_PREFIX; | ||
| 57 | +} | ||
| 58 | +} // namespace | ||
| 59 | + | ||
| 60 | +uint32_t ProcessMemPidConfigManager::PersistProcMemConfig(const def::ProcessMemNewConfigInfo& config) | ||
| 61 | +{ | ||
| 62 | + ubse::serial::UbseSerialization serializer; | ||
| 63 | + auto ret = config.Serialize(serializer); | ||
| 64 | + if (ret != UBSE_OK) { | ||
| 65 | + UBSE_LOG_ERROR << "Serialize ProcMemConfig failed for identifier=" << config.identifier; | ||
| 66 | + return UBSE_ERROR; | ||
| 67 | + } | ||
| 68 | + UbseByteBuffer byteBuffer{}; | ||
| 69 | + byteBuffer.data = serializer.GetBuffer(); | ||
| 70 | + byteBuffer.len = serializer.GetLength(); | ||
| 71 | + const auto& prefix = GetProcMemKeyPrefix(config.isPid); | ||
| 72 | + ret = ubse::storage::UbseStoragePutData(prefix, config.identifier, &byteBuffer); | ||
| 73 | + if (ret != UBSE_OK) { | ||
| 74 | + UBSE_LOG_ERROR << "UbseStoragePutData ProcMemConfig failed for prefix=" << prefix | ||
| 75 | + << ", identifier=" << config.identifier << ", ret=" << ubse::log::FormatRetCode(ret); | ||
| 76 | + return UBSE_ERROR; | ||
| 77 | + } | ||
| 78 | + UBSE_LOG_INFO << "PersistProcMemConfig: persisted prefix=" << prefix << ", identifier=" << config.identifier | ||
| 79 | + << ", maxMemory=" << config.maxMemory << ", remoteRatio=" << config.remoteRatio; | ||
| 80 | + return UBSE_OK; | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +void ProcessMemPidConfigManager::GetAllPersistedProcMemConfigs(std::vector<def::ProcessMemNewConfigInfo>& configs) | ||
| 84 | +{ | ||
| 85 | + for (const auto& prefix : {def::PROC_MEM_PID_KEY_PREFIX, def::PROC_MEM_NAME_KEY_PREFIX}) { | ||
| 86 | + std::vector<std::string> keys; | ||
| 87 | + auto listRet = ubse::storage::UbseStorageListKeys(prefix, keys); | ||
| 88 | + if (listRet != UBSE_OK) { | ||
| 89 | + UBSE_LOG_WARN << "UbseStorageListKeys failed for prefix=" << prefix; | ||
| 90 | + continue; | ||
| 91 | + } | ||
| 92 | + for (const auto& key : keys) { | ||
| 93 | + std::vector<def::ProcessMemNewConfigInfo> tmpVec; | ||
| 94 | + auto ret = ubse::storage::UbseStorageQueryData(prefix, key, &tmpVec, QueryProcMemConfigCallback); | ||
| 95 | + if (ret == UBSE_OK && !tmpVec.empty()) { | ||
| 96 | + configs.push_back(tmpVec[0]); | ||
| 97 | + } else { | ||
| 98 | + UBSE_LOG_WARN << "UbseStorageQueryData failed for prefix=" << prefix << ", key=" << key | ||
| 99 | + << ", ret=" << ubse::log::FormatRetCode(ret); | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + } | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +uint32_t ProcessMemPidConfigManager::DeleteProcMemConfig(bool isPid, const std::string& identifier) | ||
| 106 | +{ | ||
| 107 | + const auto& prefix = GetProcMemKeyPrefix(isPid); | ||
| 108 | + auto ret = ubse::storage::UbseStorageDeleteData(prefix, identifier); | ||
| 109 | + if (ret != UBSE_OK) { | ||
| 110 | + UBSE_LOG_ERROR << "Failed to delete ProcMem config for prefix=" << prefix << ", identifier=" << identifier | ||
| 111 | + << ", ret=" << ubse::log::FormatRetCode(ret); | ||
| 112 | + return ret; | ||
| 113 | + } | ||
| 114 | + return UBSE_OK; | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +uint32_t ProcessMemPidConfigManager::PersistFossilConfig(pid_t pid, const def::FossilPidConfigInfo& fossil) | ||
| 118 | +{ | ||
| 119 | + ubse::serial::UbseSerialization serializer; | ||
| 120 | + auto ret = fossil.Serialize(serializer); | ||
| 121 | + if (ret != UBSE_OK) { | ||
| 122 | + UBSE_LOG_ERROR << "Serialize FossilConfig failed for pid=" << pid; | ||
| 123 | + return UBSE_ERROR; | ||
| 124 | + } | ||
| 125 | + UbseByteBuffer byteBuffer{}; | ||
| 126 | + byteBuffer.data = serializer.GetBuffer(); | ||
| 127 | + byteBuffer.len = serializer.GetLength(); | ||
| 128 | + const std::string key = std::to_string(pid); | ||
| 129 | + ret = ubse::storage::UbseStoragePutData(def::PROC_MEM_FOSSIL_KEY_PREFIX, key, &byteBuffer); | ||
| 130 | + if (ret != UBSE_OK) { | ||
| 131 | + UBSE_LOG_ERROR << "UbseStoragePutData FossilConfig failed for pid=" << pid | ||
| 132 | + << ", ret=" << ubse::log::FormatRetCode(ret); | ||
| 133 | + return UBSE_ERROR; | ||
| 134 | + } | ||
| 135 | + UBSE_LOG_INFO << "PersistFossilConfig: pid=" << pid << " name=" << fossil.name << " maxMemory=" << fossil.maxMemory | ||
| 136 | + << " remoteRatio=" << fossil.remoteRatio; | ||
| 137 | + return UBSE_OK; | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +uint32_t ProcessMemPidConfigManager::DeleteFossilConfig(pid_t pid) | ||
| 141 | +{ | ||
| 142 | + auto ret = ubse::storage::UbseStorageDeleteData(def::PROC_MEM_FOSSIL_KEY_PREFIX, std::to_string(pid)); | ||
| 143 | + if (ret != UBSE_OK) { | ||
| 144 | + UBSE_LOG_ERROR << "Failed to delete FossilConfig for pid=" << pid << ", ret=" << ubse::log::FormatRetCode(ret); | ||
| 145 | + return ret; | ||
| 146 | + } | ||
| 147 | + return UBSE_OK; | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +void ProcessMemPidConfigManager::GetAllFossilConfigs(std::vector<std::pair<pid_t, def::FossilPidConfigInfo>>& fossils) | ||
| 151 | +{ | ||
| 152 | + std::vector<std::string> keys; | ||
| 153 | + auto listRet = ubse::storage::UbseStorageListKeys(def::PROC_MEM_FOSSIL_KEY_PREFIX, keys); | ||
| 154 | + if (listRet != UBSE_OK) { | ||
| 155 | + UBSE_LOG_WARN << "UbseStorageListKeys failed for prefix=" << def::PROC_MEM_FOSSIL_KEY_PREFIX; | ||
| 156 | + return; | ||
| 157 | + } | ||
| 158 | + for (const auto& key : keys) { | ||
| 159 | + std::vector<def::FossilPidConfigInfo> tmpVec; | ||
| 160 | + auto ret = ubse::storage::UbseStorageQueryData(def::PROC_MEM_FOSSIL_KEY_PREFIX, key, &tmpVec, | ||
| 161 | + QueryFossilConfigCallback); | ||
| 162 | + if (ret == UBSE_OK && !tmpVec.empty()) { | ||
| 163 | + auto pidOpt = def::ParsePidFromIdentifier(key); | ||
| 164 | + if (pidOpt.has_value()) { | ||
| 165 | + fossils.emplace_back(pidOpt.value(), tmpVec[0]); | ||
| 166 | + } | ||
| 167 | + } else { | ||
| 168 | + UBSE_LOG_WARN << "UbseStorageQueryData failed for prefix=" << def::PROC_MEM_FOSSIL_KEY_PREFIX | ||
| 169 | + << ", key=" << key << ", ret=" << ubse::log::FormatRetCode(ret); | ||
| 170 | + } | ||
| 171 | + } | ||
| 172 | +} | ||
| 173 | + | ||
| 174 | +void ProcessMemPidConfigManager::QueryFossilConfigCallback(const std::string& keyPrefix, const std::string& key, | ||
| 175 | + const UbseByteBuffer& buff, void* ctx) | ||
| 112 | { | 176 | { |
| 113 | if (buff.data == nullptr || buff.len == 0) { | 177 | if (buff.data == nullptr || buff.len == 0) { |
| 114 | - UBSE_LOG_ERROR << "QueryPidConfigCallback failed, invalid buffer"; | 178 | + UBSE_LOG_ERROR << "QueryFossilConfigCallback failed, invalid buffer for keyPrefix=" << keyPrefix |
| 179 | + << " key=" << key; | ||
| 115 | return; | 180 | return; |
| 116 | } | 181 | } |
| 117 | - def::ProcessMemPidInfo pidInfo{}; | 182 | + def::FossilPidConfigInfo fossil{}; |
| 118 | ubse::serial::UbseDeSerialization deserializer(buff.data, buff.len); | 183 | ubse::serial::UbseDeSerialization deserializer(buff.data, buff.len); |
| 119 | - auto ret = pidInfo.DeserializePidInfo(deserializer); | 184 | + auto ret = fossil.Deserialize(deserializer); |
| 120 | if (ret != UBSE_OK) { | 185 | if (ret != UBSE_OK) { |
| 121 | - UBSE_LOG_ERROR << "DeSerialPidDefInfo failed, " << ubse::log::FormatRetCode(ret); | 186 | + UBSE_LOG_ERROR << "Deserialize FossilConfig failed for keyPrefix=" << keyPrefix << " key=" << key; |
| 122 | return; | 187 | return; |
| 123 | } | 188 | } |
| 124 | - auto pidInfos = static_cast<std::vector<def::ProcessMemPidInfo>*>(ctx); | 189 | + auto fossils = static_cast<std::vector<def::FossilPidConfigInfo>*>(ctx); |
| 125 | - pidInfos->push_back(pidInfo); | 190 | + fossils->push_back(fossil); |
| 126 | - return; | ||
| 127 | } | 191 | } |
| 128 | 192 | ||
| 129 | -bool ProcessMemPidConfigManager::CheckPidConfigInfo(const def::ProcessMemPidInfo& pidInfo) | 193 | +void ProcessMemPidConfigManager::QueryProcMemConfigCallback(const std::string& keyPrefix, const std::string& key, |
| 194 | + const UbseByteBuffer& buff, void* ctx) | ||
| 130 | { | 195 | { |
| 131 | - auto exactStartTime = GetExactStartTime(pidInfo.configInfo.pid); | 196 | + if (buff.data == nullptr || buff.len == 0) { |
| 132 | - if (exactStartTime == 0) { | 197 | + UBSE_LOG_ERROR << "QueryProcMemConfigCallback failed, invalid buffer for keyPrefix=" << keyPrefix |
| 133 | - UBSE_LOG_ERROR << "Failed to get exact start time for pid: " << pidInfo.configInfo.pid; | 198 | + << " key=" << key; |
| 134 | - return false; | 199 | + return; |
| 135 | } | 200 | } |
| 136 | - return exactStartTime == pidInfo.startTime; | 201 | + def::ProcessMemNewConfigInfo config{}; |
| 137 | -} | 202 | + ubse::serial::UbseDeSerialization deserializer(buff.data, buff.len); |
| 138 | - | 203 | + auto ret = config.Deserialize(deserializer); |
| 139 | -void ProcessMemPidConfigManager::DeletePidConfigInfo(pid_t pid) | ||
| 140 | -{ | ||
| 141 | - auto pidStr = std::to_string(pid); | ||
| 142 | - auto ret = ubse::storage::UbseStorageDeleteData(PID_CONFIG_KEY_PREFIX, pidStr); | ||
| 143 | if (ret != UBSE_OK) { | 204 | if (ret != UBSE_OK) { |
| 144 | - UBSE_LOG_ERROR << "Failed to delete pid config for pid: " << pidStr << ", " << ubse::log::FormatRetCode(ret); | 205 | + UBSE_LOG_ERROR << "Deserialize ProcMemConfig failed for keyPrefix=" << keyPrefix << " key=" << key; |
| 206 | + return; | ||
| 145 | } | 207 | } |
| 146 | - UBSE_LOG_INFO << "Delete pid config for pid: " << pidStr; | 208 | + auto configs = static_cast<std::vector<def::ProcessMemNewConfigInfo>*>(ctx); |
| 147 | - return; | 209 | + configs->push_back(config); |
| 148 | } | 210 | } |
| 149 | 211 | ||
| 150 | -bool ProcessMemPidConfigManager::IsPidInfoExist(pid_t pid, uint64_t startTime) | 212 | +} // namespace process_mem::manager |
| 151 | -{ | ||
| 152 | - auto exactStartTime = GetExactStartTime(pid); | ||
| 153 | - if (exactStartTime == 0) { | ||
| 154 | - return false; | ||
| 155 | - } | ||
| 156 | - return exactStartTime == startTime; | ||
| 157 | -} | ||
| 158 | - | ||
| 159 | -} // namespace process_mem::manager | ||
| @@ -16,20 +16,20 @@ | |||
| 16 | namespace process_mem::manager { | 16 | namespace process_mem::manager { |
| 17 | class ProcessMemPidConfigManager { | 17 | class ProcessMemPidConfigManager { |
| 18 | public: | 18 | public: |
| 19 | - static void PersistPidConfigInfo(const def::ProcessMemPidInfo& pidInfo); | ||
| 20 | - | ||
| 21 | - static void GetAllPersistedPidConfigInfo(std::vector<def::ProcessMemPidInfo>& pidConfigInfos); | ||
| 22 | - | ||
| 23 | - static bool CheckPidConfigInfo(const def::ProcessMemPidInfo& pidInfo); | ||
| 24 | - | ||
| 25 | static uint64_t GetExactStartTime(pid_t pid); | 19 | static uint64_t GetExactStartTime(pid_t pid); |
| 26 | 20 | ||
| 27 | - static void QueryPidConfigCallback(const std::string& keyPrefix, const std::string& key, const UbseByteBuffer& buff, | 21 | + static uint32_t PersistProcMemConfig(const def::ProcessMemNewConfigInfo& config); |
| 28 | - void* ctx); | 22 | + static void GetAllPersistedProcMemConfigs(std::vector<def::ProcessMemNewConfigInfo>& configs); |
| 23 | + static uint32_t DeleteProcMemConfig(bool isPid, const std::string& identifier); | ||
| 29 | 24 | ||
| 30 | - static void DeletePidConfigInfo(pid_t pid); | 25 | + static uint32_t PersistFossilConfig(pid_t pid, const def::FossilPidConfigInfo& fossil); |
| 26 | + static uint32_t DeleteFossilConfig(pid_t pid); | ||
| 27 | + static void GetAllFossilConfigs(std::vector<std::pair<pid_t, def::FossilPidConfigInfo>>& fossils); | ||
| 31 | 28 | ||
| 32 | - static bool IsPidInfoExist(pid_t pid, uint64_t startTime); | 29 | + static void QueryProcMemConfigCallback(const std::string& keyPrefix, const std::string& key, |
| 30 | + const UbseByteBuffer& buff, void* ctx); | ||
| 31 | + static void QueryFossilConfigCallback(const std::string& keyPrefix, const std::string& key, | ||
| 32 | + const UbseByteBuffer& buff, void* ctx); | ||
| 33 | }; | 33 | }; |
| 34 | } // namespace process_mem::manager | 34 | } // namespace process_mem::manager |
| 35 | -#endif | 35 | +#endif |
| @@ -0,0 +1,1916 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved. | ||
| 3 | + * ubs-engine is licensed under Mulan PSL v2. | ||
| 4 | + * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 5 | + * You may obtain a copy of Mulan PSL v2 at: | ||
| 6 | + * http://license.coscl.org.cn/MulanPSL2 | ||
| 7 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 8 | + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 9 | + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 10 | + * See the Mulan PSL v2 for more details. | ||
| 11 | + */ | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +namespace process_mem::decision { | ||
| 35 | +UBSE_DEFINE_THIS_MODULE("process_mem"); | ||
| 36 | + | ||
| 37 | +using namespace ubse::timer; | ||
| 38 | +using namespace process_mem::manager; | ||
| 39 | + | ||
| 40 | +namespace { | ||
| 41 | +constexpr uint64_t BYTES_PER_GB = 1073741824; | ||
| 42 | +constexpr uint64_t BYTES_PER_MB = 1048576; | ||
| 43 | +constexpr uint64_t MB_128 = 128; | ||
| 44 | +constexpr uint64_t RETURN_HEADROOM_BYTES = 5 * BYTES_PER_GB; | ||
| 45 | +constexpr uint64_t NORMAL_POLL_INTERVAL_MS = 1000; | ||
| 46 | +constexpr uint32_t kBorrowHighWatermark = 100; | ||
| 47 | +constexpr uint32_t kMaxReturnRetry = 100; | ||
| 48 | +constexpr int64_t kEmergencyBroadcastIntervalMs = 2000; | ||
| 49 | + | ||
| 50 | +const char* ReturnSceneToString(ReturnScene scene) | ||
| 51 | +{ | ||
| 52 | + switch (scene) { | ||
| 53 | + case ReturnScene::TIMEOUT: | ||
| 54 | + return "timeout"; | ||
| 55 | + case ReturnScene::ACTIVE: | ||
| 56 | + return "active"; | ||
| 57 | + case ReturnScene::EXITED: | ||
| 58 | + return "exited"; | ||
| 59 | + case ReturnScene::PASSIVE: | ||
| 60 | + return "passive"; | ||
| 61 | + } | ||
| 62 | + return "unknown"; | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +uint64_t GbToBytes(uint64_t gb) | ||
| 66 | +{ | ||
| 67 | + return gb * BYTES_PER_GB; | ||
| 68 | +} | ||
| 69 | +uint64_t BytesToGb(uint64_t bytes) | ||
| 70 | +{ | ||
| 71 | + return bytes / BYTES_PER_GB; | ||
| 72 | +} | ||
| 73 | +double BytesToGbDouble(uint64_t bytes) | ||
| 74 | +{ | ||
| 75 | + return static_cast<double>(bytes) / BYTES_PER_GB; | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +std::string GenerateBorrowName(pid_t pid) | ||
| 79 | +{ | ||
| 80 | + static std::atomic<uint32_t> nameSeq{0}; | ||
| 81 | + auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()) | ||
| 82 | + .count(); | ||
| 83 | + auto nodeId = ubse::nodeController::UbseNodeController::GetInstance().GetCurrentNodeId(); | ||
| 84 | + std::string name = | ||
| 85 | + nodeId + "-" + std::to_string(pid) + "-" + std::to_string(ms) + "-" + std::to_string(nameSeq.fetch_add(1)); | ||
| 86 | + constexpr size_t maxNameLen = 48; | ||
| 87 | + if (name.size() >= maxNameLen) { | ||
| 88 | + name.resize(maxNameLen - 1); | ||
| 89 | + } | ||
| 90 | + return name; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +enum class CandidateSkip | ||
| 94 | +{ | ||
| 95 | + NONE, | ||
| 96 | + CAN_MIGRATE | ||
| 97 | +}; | ||
| 98 | + | ||
| 99 | +def::ProcessStatus ComputeSlotStatus(const std::vector<def::BorrowSlot>& slots) | ||
| 100 | +{ | ||
| 101 | + bool hasActiveSlots = false; | ||
| 102 | + for (const auto& s : slots) { | ||
| 103 | + if (s.status == def::BorrowSlotStatus::BORROWING) { | ||
| 104 | + hasActiveSlots = true; | ||
| 105 | + break; | ||
| 106 | + } | ||
| 107 | + } | ||
| 108 | + return slots.empty() ? def::ProcessStatus::IDLE : | ||
| 109 | + (hasActiveSlots ? def::ProcessStatus::BORROWING : def::ProcessStatus::BORROWED); | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +std::vector<const ubse::mem::controller::UbseNumaMemoryDebtInfo*> MatchPidDebts( | ||
| 113 | + pid_t pid, const std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo>& debts) | ||
| 114 | +{ | ||
| 115 | + std::vector<const ubse::mem::controller::UbseNumaMemoryDebtInfo*> matched; | ||
| 116 | + for (const auto& debt : debts) { | ||
| 117 | + def::ProcessMemUsrInfo usrInfo{}; | ||
| 118 | + if (memcpy_s(&usrInfo, sizeof(usrInfo), debt.usrInfo, sizeof(usrInfo)) != EOK || | ||
| 119 | + usrInfo.pid != static_cast<int32_t>(pid)) { | ||
| 120 | + continue; | ||
| 121 | + } | ||
| 122 | + matched.push_back(&debt); | ||
| 123 | + } | ||
| 124 | + return matched; | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +bool IsBorrowBindable(pid_t pid, const def::ProcessMemUsrInfo& usrInfo, | ||
| 128 | + const std::map<pid_t, def::ManagedPidEntry>& snapshot) | ||
| 129 | +{ | ||
| 130 | + auto it = snapshot.find(pid); | ||
| 131 | + if (it == snapshot.end()) { | ||
| 132 | + return false; | ||
| 133 | + } | ||
| 134 | + auto curStartTime = ProcessMemPidConfigManager::GetExactStartTime(pid); | ||
| 135 | + return curStartTime != 0 && static_cast<uint64_t>(curStartTime) == static_cast<uint64_t>(usrInfo.startTime); | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | +void QuerySmapProcessConfigs(std::unordered_map<pid_t, std::unordered_map<int, uint64_t>>& smapMemKb, | ||
| 139 | + std::optional<std::set<int>>& failedNumas, size_t& queryFail) | ||
| 140 | +{ | ||
| 141 | + if (!pid::bridge::ProcessMemPidBridge::rmrsProcessConfigQuery) { | ||
| 142 | + UBSE_LOG_DEBUG << "[process_mem] recover config: UBSRMRSProcessConfigQuery unavailable"; | ||
| 143 | + failedNumas = std::nullopt; | ||
| 144 | + return; | ||
| 145 | + } | ||
| 146 | + constexpr int kCapacity = 512; | ||
| 147 | + std::vector<ubse::mem::controller::UbseNumaMemoryImportDebtInfo> debts; | ||
| 148 | + if (ubse::mem::controller::UbseGetNumaMemImportDebtInfoWithLocalNode(debts) != UBSE_OK) { | ||
| 149 | + UBSE_LOG_WARN << "[process_mem] recover config: get import debts failed"; | ||
| 150 | + failedNumas = std::nullopt; | ||
| 151 | + return; | ||
| 152 | + } | ||
| 153 | + std::set<int> remoteNumas; | ||
| 154 | + for (const auto& debt : debts) { | ||
| 155 | + remoteNumas.insert(static_cast<int>(debt.remoteNumaId)); | ||
| 156 | + } | ||
| 157 | + for (int nid : remoteNumas) { | ||
| 158 | + std::vector<mempooling::smap::ProcessPayload> payloads(static_cast<size_t>(kCapacity)); | ||
| 159 | + int realLen = 0; | ||
| 160 | + auto ret = pid::bridge::ProcessMemPidBridge::rmrsProcessConfigQuery(nid, payloads.data(), kCapacity, &realLen); | ||
| 161 | + if (ret != 0 || realLen <= 0) { | ||
| 162 | + if (ret != 0) { | ||
| 163 | + ++queryFail; | ||
| 164 | + if (!failedNumas.has_value()) { | ||
| 165 | + failedNumas = std::set<int>{}; | ||
| 166 | + } | ||
| 167 | + failedNumas->insert(nid); | ||
| 168 | + } | ||
| 169 | + continue; | ||
| 170 | + } | ||
| 171 | + for (int i = 0; i < realLen; ++i) { | ||
| 172 | + const auto& p = payloads[static_cast<size_t>(i)]; | ||
| 173 | + if (p.pid <= 0 || p.scanType != 1) { | ||
| 174 | + continue; | ||
| 175 | + } | ||
| 176 | + smapMemKb[p.pid][nid] += p.memSize; | ||
| 177 | + } | ||
| 178 | + } | ||
| 179 | +} | ||
| 180 | + | ||
| 181 | +void RearrangePidMigrated(def::BorrowState& borrow) | ||
| 182 | +{ | ||
| 183 | + std::map<int, std::vector<size_t>> numaGroups; | ||
| 184 | + for (size_t i = 0; i < borrow.slots.size(); ++i) { | ||
| 185 | + const auto& s = borrow.slots[i]; | ||
| 186 | + if (s.status != def::BorrowSlotStatus::COMPLETED || s.returnStatus == def::ReturnStatus::RETURNING) { | ||
| 187 | + continue; | ||
| 188 | + } | ||
| 189 | + numaGroups[s.remoteNumaId].push_back(i); | ||
| 190 | + } | ||
| 191 | + for (auto& [numaId, idxs] : numaGroups) { | ||
| 192 | + uint64_t expectedTotal = 0; | ||
| 193 | + for (size_t i : idxs) { | ||
| 194 | + expectedTotal += borrow.slots[i].migratedBytes; | ||
| 195 | + } | ||
| 196 | + std::sort(idxs.begin(), idxs.end(), | ||
| 197 | + [&borrow](size_t a, size_t b) { return borrow.slots[a].capacity > borrow.slots[b].capacity; }); | ||
| 198 | + uint64_t remaining = expectedTotal; | ||
| 199 | + for (size_t i : idxs) { | ||
| 200 | + uint64_t fill = std::min(borrow.slots[i].capacity, remaining); | ||
| 201 | + borrow.slots[i].migratedBytes = fill; | ||
| 202 | + remaining -= fill; | ||
| 203 | + } | ||
| 204 | + } | ||
| 205 | +} | ||
| 206 | +} // namespace | ||
| 207 | + | ||
| 208 | +std::string ProcessMemPidDecision::GetCurrentNodeId() | ||
| 209 | +{ | ||
| 210 | + return ubse::nodeController::UbseNodeController::GetInstance().GetCurrentNodeId(); | ||
| 211 | +} | ||
| 212 | + | ||
| 213 | +void ProcessMemPidDecision::LoadConfig() | ||
| 214 | +{ | ||
| 215 | + const std::string section = "process_mem"; | ||
| 216 | + | ||
| 217 | + const std::string intervalKey = "schedule_interval"; | ||
| 218 | + ubse::config::UbseGetUInt(section, intervalKey, processIntervalSec_); | ||
| 219 | + constexpr uint32_t defaultInterval = 5; | ||
| 220 | + constexpr uint32_t maxInterval = 3600; | ||
| 221 | + if (processIntervalSec_ < 1 || processIntervalSec_ > maxInterval) { | ||
| 222 | + processIntervalSec_ = defaultInterval; | ||
| 223 | + } | ||
| 224 | + | ||
| 225 | + const std::string thresholdKey = "pressing_free_threshold"; | ||
| 226 | + uint64_t thresholdGb = 15; | ||
| 227 | + ubse::config::UbseGetULong(section, thresholdKey, thresholdGb); | ||
| 228 | + constexpr uint64_t defaultThresholdGb = 15; | ||
| 229 | + constexpr uint64_t maxThresholdGb = 4096; | ||
| 230 | + if (thresholdGb < 1 || thresholdGb > maxThresholdGb) { | ||
| 231 | + thresholdGb = defaultThresholdGb; | ||
| 232 | + } | ||
| 233 | + freeMemoryThresholdBytes_ = GbToBytes(thresholdGb); | ||
| 234 | + | ||
| 235 | + const std::string timeoutKey = "borrow.timeout"; | ||
| 236 | + ubse::config::UbseGetUInt(section, timeoutKey, timeoutPer128MbMs_); | ||
| 237 | + constexpr uint32_t defaultTimeoutMs = 1000; | ||
| 238 | + constexpr uint32_t maxTimeoutMs = 1800000; | ||
| 239 | + if (timeoutPer128MbMs_ == 0 || timeoutPer128MbMs_ > maxTimeoutMs) { | ||
| 240 | + timeoutPer128MbMs_ = defaultTimeoutMs; | ||
| 241 | + } | ||
| 242 | + | ||
| 243 | + const std::string samePlaneKey = "borrow.must_same_plane"; | ||
| 244 | + bool mustSamePlane = false; | ||
| 245 | + ubse::config::UbseGetBool(section, samePlaneKey, mustSamePlane); | ||
| 246 | + // 配置语义: true=必须同平面(严格过滤); UbseMemBorrower.samePlanePrefer 语义相反: true=软优先 | ||
| 247 | + samePlanePrefer_ = !mustSamePlane; | ||
| 248 | +} | ||
| 249 | + | ||
| 250 | +void ProcessMemPidDecision::LoadOomReturnConfig() | ||
| 251 | +{ | ||
| 252 | + const std::string oomSection = "process_mem.oom"; | ||
| 253 | + uint32_t fastPollMs = 200; | ||
| 254 | + ubse::config::UbseGetUInt(oomSection, "collect_node_interval", fastPollMs); | ||
| 255 | + constexpr uint32_t defaultFastPollMs = 200; | ||
| 256 | + constexpr uint32_t minFastPollMs = 50; | ||
| 257 | + constexpr uint32_t maxFastPollMs = 60000; | ||
| 258 | + if (fastPollMs < minFastPollMs || fastPollMs > maxFastPollMs) { | ||
| 259 | + fastPollMs = defaultFastPollMs; | ||
| 260 | + } | ||
| 261 | + fastPollIntervalMs_ = fastPollMs; | ||
| 262 | + uint32_t emergencyGb = 5; | ||
| 263 | + ubse::config::UbseGetUInt(oomSection, "emergency_free_threshold", emergencyGb); | ||
| 264 | + constexpr uint32_t defaultEmergencyGb = 5; | ||
| 265 | + constexpr uint32_t maxEmergencyGb = 4096; | ||
| 266 | + if (emergencyGb < 1 || emergencyGb > maxEmergencyGb) { | ||
| 267 | + emergencyGb = defaultEmergencyGb; | ||
| 268 | + } | ||
| 269 | + emergencyThresholdBytes_ = GbToBytes(emergencyGb); | ||
| 270 | + | ||
| 271 | + const std::string returnSection = "process_mem.return"; | ||
| 272 | + uint32_t observeCycles = 300; | ||
| 273 | + ubse::config::UbseGetUInt(returnSection, "observe_cycles", observeCycles); | ||
| 274 | + constexpr uint32_t defaultObserveCycles = 300; | ||
| 275 | + constexpr uint32_t maxObserveCycles = 4096; | ||
| 276 | + if (observeCycles < 1 || observeCycles > maxObserveCycles) { | ||
| 277 | + observeCycles = defaultObserveCycles; | ||
| 278 | + } | ||
| 279 | + observeCycles_ = observeCycles; | ||
| 280 | +} | ||
| 281 | + | ||
| 282 | +bool ProcessMemPidDecision::StartExecutors() | ||
| 283 | +{ | ||
| 284 | + constexpr int borrowThreadNum = 8; | ||
| 285 | + constexpr int queSize = 256; | ||
| 286 | + borrowExecutor_ = ubse::task_executor::UbseTaskExecutor::Create("ProcMemDecision", borrowThreadNum, queSize); | ||
| 287 | + if (borrowExecutor_ == nullptr || !borrowExecutor_->Start()) { | ||
| 288 | + UBSE_LOG_ERROR << "[process_mem] decision: borrowExecutor start failed"; | ||
| 289 | + return false; | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | + constexpr int returnThreadNum = 4; | ||
| 293 | + returnExecutor_ = ubse::task_executor::UbseTaskExecutor::Create("ProcMemReturn", returnThreadNum, queSize); | ||
| 294 | + if (returnExecutor_ == nullptr || !returnExecutor_->Start()) { | ||
| 295 | + UBSE_LOG_ERROR << "[process_mem] decision: returnExecutor start failed"; | ||
| 296 | + return false; | ||
| 297 | + } | ||
| 298 | + return true; | ||
| 299 | +} | ||
| 300 | + | ||
| 301 | +uint32_t ProcessMemPidDecision::Init() | ||
| 302 | +{ | ||
| 303 | + LoadConfig(); | ||
| 304 | + LoadOomReturnConfig(); | ||
| 305 | + | ||
| 306 | + if (!StartExecutors()) { | ||
| 307 | + return UBSE_ERROR; | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + RecoverBorrowFromObmm(); | ||
| 311 | + ProcessMemPidInfoManager::GetInstance().CleanupStalePidConfigs(); | ||
| 312 | + | ||
| 313 | + if (UbseTimerHandlerRegister( | ||
| 314 | + "ProcessMemDecisionTimer", [this]() -> uint32_t { return this->OnDecisionTimer(); }, processIntervalSec_) != | ||
| 315 | + UBSE_OK) { | ||
| 316 | + return UBSE_ERROR; | ||
| 317 | + } | ||
| 318 | + | ||
| 319 | + { | ||
| 320 | + std::lock_guard<std::mutex> lock(oomMutex_); | ||
| 321 | + oomRunning_ = true; | ||
| 322 | + } | ||
| 323 | + oomThread_ = std::thread([this]() { OomLoop(); }); | ||
| 324 | + | ||
| 325 | + UBSE_LOG_INFO << "[process_mem] decision init: interval=" << processIntervalSec_ | ||
| 326 | + << "s, free_mem_threshold_gb=" << BytesToGb(freeMemoryThresholdBytes_) | ||
| 327 | + << ", timeout_per_128mb_ms=" << timeoutPer128MbMs_ << ", same_plane_prefer=" << samePlanePrefer_ | ||
| 328 | + << ", oom_fast_poll_ms=" << fastPollIntervalMs_ | ||
| 329 | + << ", oom_emergency_gb=" << BytesToGb(emergencyThresholdBytes_) | ||
| 330 | + << ", return_observe_cycles=" << observeCycles_; | ||
| 331 | + return UBSE_OK; | ||
| 332 | +} | ||
| 333 | + | ||
| 334 | +void ProcessMemPidDecision::UnInit() | ||
| 335 | +{ | ||
| 336 | + { | ||
| 337 | + std::lock_guard<std::mutex> lock(oomMutex_); | ||
| 338 | + oomRunning_ = false; | ||
| 339 | + } | ||
| 340 | + oomCv_.notify_all(); | ||
| 341 | + if (oomThread_.joinable()) { | ||
| 342 | + oomThread_.join(); | ||
| 343 | + } | ||
| 344 | + | ||
| 345 | + stopping_ = true; | ||
| 346 | + UbseTimerHandlerUnregister("ProcessMemDecisionTimer"); | ||
| 347 | + if (borrowExecutor_ != nullptr) { | ||
| 348 | + borrowExecutor_->Wait(); | ||
| 349 | + borrowExecutor_->Stop(); | ||
| 350 | + } | ||
| 351 | + if (returnExecutor_ != nullptr) { | ||
| 352 | + returnExecutor_->Wait(); | ||
| 353 | + returnExecutor_->Stop(); | ||
| 354 | + } | ||
| 355 | +} | ||
| 356 | + | ||
| 357 | +void ProcessMemPidDecision::RunBorrowRound(uint64_t roundNum) | ||
| 358 | +{ | ||
| 359 | + auto roundStart = std::chrono::steady_clock::now(); | ||
| 360 | + | ||
| 361 | + CheckTimeouts(roundNum); | ||
| 362 | + | ||
| 363 | + uint64_t shortage = 0; | ||
| 364 | + if (!CheckNodeFreeMemory(shortage)) { | ||
| 365 | + return; | ||
| 366 | + } | ||
| 367 | + | ||
| 368 | + uint64_t rawShortage = shortage; | ||
| 369 | + uint64_t pendingBorrow = GetPendingBorrowTotal(); | ||
| 370 | + shortage = (shortage > pendingBorrow) ? (shortage - pendingBorrow) : 0; | ||
| 371 | + | ||
| 372 | + uint64_t initialShortage = shortage; | ||
| 373 | + | ||
| 374 | + uint64_t nodeFree = freeMemoryThresholdBytes_ - rawShortage; | ||
| 375 | + BroadcastPassiveReturn(roundNum, nodeFree, false); | ||
| 376 | + | ||
| 377 | + auto candidates = BuildCandidates(roundNum); | ||
| 378 | + if (candidates.empty()) { | ||
| 379 | + UBSE_LOG_INFO << "[process_mem] borrow round=" << roundNum << " shortage_gb=" << BytesToGb(rawShortage) | ||
| 380 | + << " pending_gb=" << BytesToGb(pendingBorrow) | ||
| 381 | + << " borrowed_gb=0 slots=0 remaining_shortage_gb=" << BytesToGb(shortage) | ||
| 382 | + << " dur_ms=0 (no candidates)"; | ||
| 383 | + return; | ||
| 384 | + } | ||
| 385 | + | ||
| 386 | + ExecuteBorrowRound(candidates, shortage, roundNum); | ||
| 387 | + | ||
| 388 | + auto roundEnd = std::chrono::steady_clock::now(); | ||
| 389 | + auto durMs = std::chrono::duration_cast<std::chrono::milliseconds>(roundEnd - roundStart).count(); | ||
| 390 | + uint64_t borrowed = (shortage <= initialShortage) ? (initialShortage - shortage) : 0; | ||
| 391 | + | ||
| 392 | + UBSE_LOG_INFO << "[process_mem] borrow round=" << roundNum << " shortage_gb=" << BytesToGb(rawShortage) | ||
| 393 | + << " pending_gb=" << BytesToGb(pendingBorrow) << " borrowed_gb=" << BytesToGb(borrowed) | ||
| 394 | + << " remaining_shortage_gb=" << BytesToGb(shortage) << " dur_ms=" << durMs; | ||
| 395 | +} | ||
| 396 | + | ||
| 397 | +uint32_t ProcessMemPidDecision::OnDecisionTimer() | ||
| 398 | +{ | ||
| 399 | + uint64_t roundNum = roundNumber_.fetch_add(1) + 1; | ||
| 400 | + | ||
| 401 | + bool expected = false; | ||
| 402 | + if (!cycleRunning_.compare_exchange_strong(expected, true, std::memory_order_acq_rel)) { | ||
| 403 | + UBSE_LOG_DEBUG << "[process_mem] borrow round=" << roundNum << " skip: backpressure (prev round running)"; | ||
| 404 | + return UBSE_OK; | ||
| 405 | + } | ||
| 406 | + std::string traceId = TraceContext::GetTraceId(); | ||
| 407 | + if (!borrowExecutor_->Execute([this, roundNum, traceId]() { | ||
| 408 | + TraceContext::SetTraceId(traceId); | ||
| 409 | + RunBorrowRound(roundNum); | ||
| 410 | + cycleRunning_.store(false, std::memory_order_release); | ||
| 411 | + TraceContext::Clear(); | ||
| 412 | + })) { | ||
| 413 | + cycleRunning_.store(false, std::memory_order_release); | ||
| 414 | + UBSE_LOG_ERROR << "[process_mem] borrow round=" << roundNum << " enqueue failed"; | ||
| 415 | + } | ||
| 416 | + | ||
| 417 | + return UBSE_OK; | ||
| 418 | +} | ||
| 419 | + | ||
| 420 | +bool ProcessMemPidDecision::CheckNodeFreeMemory(uint64_t& outShortage) | ||
| 421 | +{ | ||
| 422 | + auto freeBytes = GetNodeFreeBytes(); | ||
| 423 | + if (!freeBytes.has_value()) { | ||
| 424 | + UBSE_LOG_WARN << "[process_mem] borrow round=" << roundNumber_.load() | ||
| 425 | + << " read memAvailable failed, skip round decision"; | ||
| 426 | + return false; | ||
| 427 | + } | ||
| 428 | + uint64_t totalFree = *freeBytes; | ||
| 429 | + | ||
| 430 | + uint64_t roundNum = roundNumber_.load(); | ||
| 431 | + UBSE_LOG_DEBUG << "[process_mem] borrow round=" << roundNum | ||
| 432 | + << " step=node_check node_free_gb=" << BytesToGbDouble(totalFree) | ||
| 433 | + << " threshold_gb=" << BytesToGbDouble(freeMemoryThresholdBytes_) << " shortage_gb=" | ||
| 434 | + << (totalFree >= freeMemoryThresholdBytes_ ? | ||
| 435 | + "0 (skip)" : | ||
| 436 | + std::to_string(BytesToGb(freeMemoryThresholdBytes_ - totalFree))); | ||
| 437 | + | ||
| 438 | + if (totalFree >= freeMemoryThresholdBytes_) { | ||
| 439 | + return false; | ||
| 440 | + } | ||
| 441 | + | ||
| 442 | + outShortage = freeMemoryThresholdBytes_ - totalFree; | ||
| 443 | + return true; | ||
| 444 | +} | ||
| 445 | + | ||
| 446 | +uint64_t ProcessMemPidDecision::GetPendingBorrowTotal() const | ||
| 447 | +{ | ||
| 448 | + uint64_t total = 0; | ||
| 449 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 450 | + for (const auto& [pid, entry] : snapshot) { | ||
| 451 | + for (const auto& s : entry.borrow.slots) { | ||
| 452 | + if (s.status == def::BorrowSlotStatus::BORROWING) { | ||
| 453 | + total += s.migratedBytes; | ||
| 454 | + } | ||
| 455 | + } | ||
| 456 | + } | ||
| 457 | + return total; | ||
| 458 | +} | ||
| 459 | + | ||
| 460 | +CandidateSkip AppendBorrowCandidate(pid_t pid, const def::ManagedPidEntry& entry, | ||
| 461 | + std::vector<def::BorrowCandidate>& candidates) | ||
| 462 | +{ | ||
| 463 | + if (entry.maxMemory == 0 || entry.remoteRatio <= 0.0) { | ||
| 464 | + return CandidateSkip::CAN_MIGRATE; | ||
| 465 | + } | ||
| 466 | + | ||
| 467 | + bool hasActiveBorrow = false; | ||
| 468 | + uint64_t pendingBorrow = 0; | ||
| 469 | + for (const auto& s : entry.borrow.slots) { | ||
| 470 | + if (s.status == def::BorrowSlotStatus::BORROWING) { | ||
| 471 | + hasActiveBorrow = true; | ||
| 472 | + pendingBorrow += s.migratedBytes; | ||
| 473 | + } | ||
| 474 | + } | ||
| 475 | + uint64_t targetRemote = static_cast<uint64_t>(entry.vmRss * entry.remoteRatio); | ||
| 476 | + uint64_t canMigrate = (targetRemote > entry.borrow.currentRemote) ? (targetRemote - entry.borrow.currentRemote) : 0; | ||
| 477 | + canMigrate = (canMigrate > pendingBorrow) ? (canMigrate - pendingBorrow) : 0; | ||
| 478 | + if (canMigrate == 0) { | ||
| 479 | + return CandidateSkip::CAN_MIGRATE; | ||
| 480 | + } | ||
| 481 | + | ||
| 482 | + def::BorrowCandidate candidate; | ||
| 483 | + candidate.pid = pid; | ||
| 484 | + candidate.isChild = entry.isChild; | ||
| 485 | + candidate.hasActiveBorrow = hasActiveBorrow; | ||
| 486 | + candidate.lastMigrateTime = entry.lastMigrateTime; | ||
| 487 | + candidate.actual = entry.vmRss; | ||
| 488 | + candidate.maxMemory = entry.maxMemory; | ||
| 489 | + candidate.remoteRatio = entry.remoteRatio; | ||
| 490 | + candidate.currentRemote = entry.borrow.currentRemote; | ||
| 491 | + candidate.canMigrate = canMigrate; | ||
| 492 | + candidates.push_back(candidate); | ||
| 493 | + return CandidateSkip::NONE; | ||
| 494 | +} | ||
| 495 | + | ||
| 496 | +void ProcessMemPidDecision::LogBorrowCandidates(uint64_t roundNum, const std::vector<def::BorrowCandidate>& candidates) | ||
| 497 | +{ | ||
| 498 | + for (const auto& c : candidates) { | ||
| 499 | + UBSE_LOG_DEBUG << "[process_mem] borrow round=" << roundNum << " candidate pid=" << c.pid | ||
| 500 | + << " actual_gb=" << BytesToGbDouble(c.actual) << " max_gb=" << BytesToGbDouble(c.maxMemory) | ||
| 501 | + << " ratio=" << c.remoteRatio << " cur_remote_gb=" << BytesToGbDouble(c.currentRemote) | ||
| 502 | + << " can_migrate_gb=" << BytesToGbDouble(c.canMigrate); | ||
| 503 | + } | ||
| 504 | +} | ||
| 505 | + | ||
| 506 | +std::vector<def::BorrowCandidate> ProcessMemPidDecision::BuildCandidates(uint64_t roundNum) | ||
| 507 | +{ | ||
| 508 | + auto cacheSnapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 509 | + | ||
| 510 | + std::vector<def::BorrowCandidate> candidates; | ||
| 511 | + size_t skipCanMigrate = 0; | ||
| 512 | + | ||
| 513 | + for (const auto& [pid, entry] : cacheSnapshot) { | ||
| 514 | + auto skip = AppendBorrowCandidate(pid, entry, candidates); | ||
| 515 | + if (skip == CandidateSkip::CAN_MIGRATE) { | ||
| 516 | + ++skipCanMigrate; | ||
| 517 | + } | ||
| 518 | + } | ||
| 519 | + | ||
| 520 | + constexpr int64_t staleIntervalSec = 60; | ||
| 521 | + auto now = std::chrono::steady_clock::now(); | ||
| 522 | + std::sort(candidates.begin(), candidates.end(), [now, staleIntervalSec](const auto& a, const auto& b) { | ||
| 523 | + if (a.hasActiveBorrow != b.hasActiveBorrow) { | ||
| 524 | + return !a.hasActiveBorrow; | ||
| 525 | + } | ||
| 526 | + bool aOverCap = a.actual > static_cast<uint64_t>(a.maxMemory * a.remoteRatio); | ||
| 527 | + bool bOverCap = b.actual > static_cast<uint64_t>(b.maxMemory * b.remoteRatio); | ||
| 528 | + if (aOverCap != bOverCap) { | ||
| 529 | + return aOverCap; | ||
| 530 | + } | ||
| 531 | + if (a.isChild != b.isChild) { | ||
| 532 | + return !a.isChild; | ||
| 533 | + } | ||
| 534 | + auto aStale = (a.lastMigrateTime.time_since_epoch().count() == 0) || | ||
| 535 | + (now - a.lastMigrateTime) > std::chrono::seconds(staleIntervalSec); | ||
| 536 | + auto bStale = (b.lastMigrateTime.time_since_epoch().count() == 0) || | ||
| 537 | + (now - b.lastMigrateTime) > std::chrono::seconds(staleIntervalSec); | ||
| 538 | + if (aStale != bStale) { | ||
| 539 | + return aStale; | ||
| 540 | + } | ||
| 541 | + return a.actual > b.actual; | ||
| 542 | + }); | ||
| 543 | + | ||
| 544 | + UBSE_LOG_DEBUG << "[process_mem] borrow round=" << roundNum | ||
| 545 | + << " step=candidates total_managed=" << cacheSnapshot.size() << " eligible=" << candidates.size() | ||
| 546 | + << " skip_canmigrate=" << skipCanMigrate; | ||
| 547 | + | ||
| 548 | + LogBorrowCandidates(roundNum, candidates); | ||
| 549 | + return candidates; | ||
| 550 | +} | ||
| 551 | + | ||
| 552 | +int ProcessMemPidDecision::CollectSrcNuma(pid_t pid, uint64_t roundNum) | ||
| 553 | +{ | ||
| 554 | + auto parseStart = std::chrono::steady_clock::now(); | ||
| 555 | + | ||
| 556 | + std::unordered_map<uint32_t, size_t> numaDistribution; | ||
| 557 | + auto ret = collect::ProcessMemPidCollect::GetInstance().CollectProcessNumaMemDistribution(pid, numaDistribution); | ||
| 558 | + if (ret != UBSE_OK || numaDistribution.empty()) { | ||
| 559 | + UBSE_LOG_DEBUG << "[process_mem] borrow round=" << roundNum << " step=numa pid=" << pid | ||
| 560 | + << " src_numa=-1 (no data)"; | ||
| 561 | + return -1; | ||
| 562 | + } | ||
| 563 | + | ||
| 564 | + int bestNuma = -1; | ||
| 565 | + size_t maxPages = 0; | ||
| 566 | + for (const auto& [numaId, memSize] : numaDistribution) { | ||
| 567 | + if (memSize > maxPages) { | ||
| 568 | + maxPages = memSize; | ||
| 569 | + bestNuma = static_cast<int>(numaId); | ||
| 570 | + } | ||
| 571 | + } | ||
| 572 | + | ||
| 573 | + auto parseEnd = std::chrono::steady_clock::now(); | ||
| 574 | + auto parseMs = std::chrono::duration_cast<std::chrono::milliseconds>(parseEnd - parseStart).count(); | ||
| 575 | + | ||
| 576 | + UBSE_LOG_DEBUG << "[process_mem] borrow round=" << roundNum << " step=numa pid=" << pid << " src_numa=" << bestNuma | ||
| 577 | + << " parse_ms=" << parseMs; | ||
| 578 | + | ||
| 579 | + return bestNuma; | ||
| 580 | +} | ||
| 581 | + | ||
| 582 | +void ProcessMemPidDecision::ExecuteBorrowRound(const std::vector<def::BorrowCandidate>& candidates, uint64_t& shortage, | ||
| 583 | + uint64_t roundNum, bool emergency) | ||
| 584 | +{ | ||
| 585 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 586 | + | ||
| 587 | + for (const auto& candidate : candidates) { | ||
| 588 | + if (shortage == 0) { | ||
| 589 | + break; | ||
| 590 | + } | ||
| 591 | + | ||
| 592 | + uint64_t amount = std::min(candidate.canMigrate, shortage); | ||
| 593 | + | ||
| 594 | + int srcNumaId = CollectSrcNuma(candidate.pid, roundNum); | ||
| 595 | + | ||
| 596 | + std::string debtId = RecordPendingBorrow(candidate.pid, amount, srcNumaId, roundNum); | ||
| 597 | + if (debtId.empty()) { | ||
| 598 | + continue; | ||
| 599 | + } | ||
| 600 | + | ||
| 601 | + if (emergency) { | ||
| 602 | + UBSE_LOG_INFO << "[process_mem] oom emergency_borrow pid=" << candidate.pid | ||
| 603 | + << " amount_gb=" << BytesToGbDouble(amount) << " (smap)"; | ||
| 604 | + } else { | ||
| 605 | + UBSE_LOG_INFO << "[process_mem] borrow round=" << roundNum << " step=execute pid=" << candidate.pid | ||
| 606 | + << " amount_gb=" << BytesToGbDouble(amount) << " same_plane_prefer=" << samePlanePrefer_ | ||
| 607 | + << " timeout_ms=" << ((amount / BYTES_PER_MB / MB_128 + 1) * timeoutPer128MbMs_); | ||
| 608 | + } | ||
| 609 | + | ||
| 610 | + std::string traceId = TraceContext::GetTraceId(); | ||
| 611 | + borrowExecutor_->Execute([this, debtId, pid = candidate.pid, amount, srcNumaId, roundNum, traceId]() { | ||
| 612 | + TraceContext::SetTraceId(traceId); | ||
| 613 | + AsyncBorrowAndMigrate(debtId, pid, amount, srcNumaId, roundNum); | ||
| 614 | + TraceContext::Clear(); | ||
| 615 | + }); | ||
| 616 | + | ||
| 617 | + shortage -= amount; | ||
| 618 | + } | ||
| 619 | +} | ||
| 620 | + | ||
| 621 | +std::string ProcessMemPidDecision::RecordPendingBorrow(pid_t pid, uint64_t amount, int srcNumaId, uint64_t roundNum) | ||
| 622 | +{ | ||
| 623 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 624 | + def::BorrowSlot slot; | ||
| 625 | + slot.migratedBytes = amount; | ||
| 626 | + slot.srcNumaId = srcNumaId; | ||
| 627 | + slot.borrowTime = std::chrono::steady_clock::now(); | ||
| 628 | + slot.status = def::BorrowSlotStatus::BORROWING; | ||
| 629 | + slot.debtId = GenerateBorrowName(pid); | ||
| 630 | + bool recorded = false; | ||
| 631 | + infoMgr.UpdateManagedPidBorrowStateAtomic(pid, [&](def::BorrowState& newBorrow, def::ProcessStatus& newStatus) { | ||
| 632 | + newBorrow.slots.push_back(slot); | ||
| 633 | + newStatus = def::ProcessStatus::BORROWING; | ||
| 634 | + recorded = true; | ||
| 635 | + }); | ||
| 636 | + if (!recorded) { | ||
| 637 | + UBSE_LOG_WARN << "[process_mem] borrow round=" << roundNum << " pid=" << pid | ||
| 638 | + << " record_skipped reason=pid_not_managed"; | ||
| 639 | + return ""; | ||
| 640 | + } | ||
| 641 | + | ||
| 642 | + UBSE_LOG_INFO << "[process_mem] borrow round=" << roundNum << " step=record pid=" << pid | ||
| 643 | + << " debt_id=" << slot.debtId << " amount_gb=" << BytesToGbDouble(amount) | ||
| 644 | + << " src_numa=" << srcNumaId; | ||
| 645 | + return slot.debtId; | ||
| 646 | +} | ||
| 647 | + | ||
| 648 | +def::AtomicMigrateResult ProcessMemPidDecision::CommitBorrowAndMigrate( | ||
| 649 | + pid_t pid, const std::string& debtId, const CreatedDebtInfo& created, const std::map<int, uint64_t>& increments, | ||
| 650 | + std::vector<std::pair<int, uint64_t>>& numaTargets) | ||
| 651 | +{ | ||
| 652 | + def::AtomicMigrateResult result = def::AtomicMigrateResult::kFail; | ||
| 653 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 654 | + infoMgr.UpdateManagedPidBorrowStateAtomic(pid, [&](def::BorrowState& newBorrow, def::ProcessStatus& newStatus) { | ||
| 655 | + if (created.capacity > 0) { | ||
| 656 | + auto slotIt = std::find_if(newBorrow.slots.begin(), newBorrow.slots.end(), | ||
| 657 | + [&debtId](const def::BorrowSlot& s) { return s.debtId == debtId; }); | ||
| 658 | + if (slotIt == newBorrow.slots.end() || slotIt->status != def::BorrowSlotStatus::BORROWING || | ||
| 659 | + slotIt->returnStatus != def::ReturnStatus::NONE) { | ||
| 660 | + result = def::AtomicMigrateResult::kVanish; | ||
| 661 | + return; | ||
| 662 | + } | ||
| 663 | + } | ||
| 664 | + BuildMigrateTargets(newBorrow, increments, numaTargets); | ||
| 665 | + if (numaTargets.empty() || RmrsMigrateToNumas(pid, numaTargets) != 0) { | ||
| 666 | + result = def::AtomicMigrateResult::kFail; | ||
| 667 | + return; | ||
| 668 | + } | ||
| 669 | + newBorrow.currentRemote += FlushBorrowIncrements(newBorrow, numaTargets, increments, debtId, created); | ||
| 670 | + newStatus = ComputeSlotStatus(newBorrow.slots); | ||
| 671 | + result = def::AtomicMigrateResult::kOk; | ||
| 672 | + }); | ||
| 673 | + return result; | ||
| 674 | +} | ||
| 675 | + | ||
| 676 | +void ProcessMemPidDecision::RemoveSlotFinalize(pid_t pid, const std::string& debtId) | ||
| 677 | +{ | ||
| 678 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 679 | + infoMgr.UpdateManagedPidBorrowStateAtomic(pid, [&](def::BorrowState& newBorrow, def::ProcessStatus& newStatus) { | ||
| 680 | + auto slotIt = std::find_if(newBorrow.slots.begin(), newBorrow.slots.end(), | ||
| 681 | + [&debtId](const def::BorrowSlot& s) { return s.debtId == debtId; }); | ||
| 682 | + if (slotIt != newBorrow.slots.end() && slotIt->status == def::BorrowSlotStatus::BORROWING) { | ||
| 683 | + newBorrow.slots.erase(slotIt); | ||
| 684 | + } | ||
| 685 | + newStatus = ComputeSlotStatus(newBorrow.slots); | ||
| 686 | + }); | ||
| 687 | +} | ||
| 688 | + | ||
| 689 | +uint64_t ProcessMemPidDecision::FlushBorrowIncrements(def::BorrowState& borrow, | ||
| 690 | + const std::vector<std::pair<int, uint64_t>>& numaTargets, | ||
| 691 | + const std::map<int, uint64_t>& increments, | ||
| 692 | + const std::string& ownDebtId, const CreatedDebtInfo& created) | ||
| 693 | +{ | ||
| 694 | + uint64_t delta = 0; | ||
| 695 | + for (auto& s : borrow.slots) { | ||
| 696 | + if (s.debtId != ownDebtId || s.status != def::BorrowSlotStatus::BORROWING) { | ||
| 697 | + continue; | ||
| 698 | + } | ||
| 699 | + s.remoteNumaId = created.remoteNumaId; | ||
| 700 | + s.exportSlotId = created.exportSlotId; | ||
| 701 | + s.capacity = created.capacity; | ||
| 702 | + } | ||
| 703 | + for (const auto& [numaId, memSizeKb] : numaTargets) { | ||
| 704 | + auto incIt = increments.find(numaId); | ||
| 705 | + if (incIt == increments.end() || incIt->second == 0) { | ||
| 706 | + continue; | ||
| 707 | + } | ||
| 708 | + uint64_t remaining = incIt->second; | ||
| 709 | + for (auto& s : borrow.slots) { | ||
| 710 | + if (s.debtId != ownDebtId || s.remoteNumaId != numaId || s.status != def::BorrowSlotStatus::BORROWING || | ||
| 711 | + s.capacity == 0 || s.returnStatus == def::ReturnStatus::RETURNING) { | ||
| 712 | + continue; | ||
| 713 | + } | ||
| 714 | + uint64_t fill = std::min(s.capacity, remaining); | ||
| 715 | + s.migratedBytes = fill; | ||
| 716 | + s.status = def::BorrowSlotStatus::COMPLETED; | ||
| 717 | + delta += fill; | ||
| 718 | + break; | ||
| 719 | + } | ||
| 720 | + } | ||
| 721 | + return delta; | ||
| 722 | +} | ||
| 723 | + | ||
| 724 | +bool ProcessMemPidDecision::BuildBorrower(pid_t pid, int srcNumaId, uint64_t need, uint64_t roundNum, | ||
| 725 | + ubse::mem::controller::UbseMemBorrower& borrower) | ||
| 726 | +{ | ||
| 727 | + borrower.nodeId = GetCurrentNodeId(); | ||
| 728 | + if (borrower.nodeId.empty()) { | ||
| 729 | + UBSE_LOG_ERROR << "[process_mem] borrow round=" << roundNum << " pid=" << pid | ||
| 730 | + << " slot_failed amount_gb=" << BytesToGbDouble(need) << " reason=borrower_nodeid_empty"; | ||
| 731 | + return false; | ||
| 732 | + } | ||
| 733 | + | ||
| 734 | + auto curNodeInfo = ubse::nodeController::UbseNodeController::GetInstance().GetCurNode(); | ||
| 735 | + for (const auto& [numaLoc, numaInfo] : curNodeInfo.numaInfos) { | ||
| 736 | + if (static_cast<int>(numaLoc.numaId) == srcNumaId) { | ||
| 737 | + borrower.affinitySocketId = numaInfo.socketId; | ||
| 738 | + break; | ||
| 739 | + } | ||
| 740 | + } | ||
| 741 | + borrower.samePlanePrefer = samePlanePrefer_; | ||
| 742 | + return true; | ||
| 743 | +} | ||
| 744 | + | ||
| 745 | +bool ProcessMemPidDecision::CreateNumaDebt(pid_t pid, uint64_t need, int srcNumaId, const std::string& debtId, | ||
| 746 | + uint64_t roundNum, CreatedDebtInfo& out) | ||
| 747 | +{ | ||
| 748 | + def::ProcessMemUsrInfo usrInfo{}; | ||
| 749 | + usrInfo.pid = static_cast<int32_t>(pid); | ||
| 750 | + usrInfo.srcNuma = static_cast<int32_t>(srcNumaId); | ||
| 751 | + | ||
| 752 | + ubse::mem::controller::UbseMemBorrower borrower{}; | ||
| 753 | + if (!BuildBorrower(pid, srcNumaId, need, roundNum, borrower)) { | ||
| 754 | + RemoveSlotFinalize(pid, debtId); | ||
| 755 | + return false; | ||
| 756 | + } | ||
| 757 | + | ||
| 758 | + ubse::mem::controller::UbseMemNumaCreateOpt opt; | ||
| 759 | + opt.highWatermark = kBorrowHighWatermark; | ||
| 760 | + opt.size = need; | ||
| 761 | + if (memcpy_s(opt.usrInfo, ubse::mem::controller::UBSE_MAX_USR_INFO_LEN, &usrInfo, sizeof(usrInfo)) != EOK) { | ||
| 762 | + UBSE_LOG_ERROR << "[process_mem] borrow round=" << roundNum << " pid=" << pid | ||
| 763 | + << " slot_failed amount_gb=" << BytesToGbDouble(need) << " reason=memcpy_usrinfo"; | ||
| 764 | + RemoveSlotFinalize(pid, debtId); | ||
| 765 | + return false; | ||
| 766 | + } | ||
| 767 | + | ||
| 768 | + ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 769 | + auto ret = ubse::mem::controller::UbseMemNumaCreate(debtId, borrower, opt, desc); | ||
| 770 | + if (ret != UBSE_OK) { | ||
| 771 | + UBSE_LOG_ERROR << "[process_mem] borrow round=" << roundNum << " pid=" << pid | ||
| 772 | + << " slot_failed amount_gb=" << BytesToGbDouble(need) << " reason=create_failed ret=" << ret; | ||
| 773 | + RemoveSlotFinalize(pid, debtId); | ||
| 774 | + return false; | ||
| 775 | + } | ||
| 776 | + | ||
| 777 | + out.remoteNumaId = static_cast<int>(desc.numaId); | ||
| 778 | + out.exportSlotId = static_cast<int32_t>(desc.exportNode.slotId); | ||
| 779 | + out.capacity = desc.size; | ||
| 780 | + return true; | ||
| 781 | +} | ||
| 782 | + | ||
| 783 | +void ProcessMemPidDecision::BuildMigrateTargets(const def::BorrowState& borrow, | ||
| 784 | + const std::map<int, uint64_t>& increments, | ||
| 785 | + std::vector<std::pair<int, uint64_t>>& numaTargets) | ||
| 786 | +{ | ||
| 787 | + numaTargets.clear(); | ||
| 788 | + constexpr uint64_t pageSizeKb = 4; | ||
| 789 | + std::map<int, uint64_t> targets; | ||
| 790 | + for (const auto& [numaId, migrated] : borrow.remoteNumaMigrated) { | ||
| 791 | + targets[numaId] += migrated; | ||
| 792 | + } | ||
| 793 | + for (const auto& [numaId, inc] : increments) { | ||
| 794 | + targets[numaId] += inc; | ||
| 795 | + } | ||
| 796 | + for (const auto& [numaId, target] : targets) { | ||
| 797 | + uint64_t memSizeKb = target / 1024 / pageSizeKb * pageSizeKb; | ||
| 798 | + if (memSizeKb == 0) { | ||
| 799 | + continue; | ||
| 800 | + } | ||
| 801 | + numaTargets.emplace_back(numaId, memSizeKb); | ||
| 802 | + auto incIt = increments.find(numaId); | ||
| 803 | + UBSE_LOG_INFO << "[process_mem] numa_target_migrate numa=" << numaId << " target_gb=" << BytesToGbDouble(target) | ||
| 804 | + << " increment_gb=" << BytesToGbDouble(incIt != increments.end() ? incIt->second : 0); | ||
| 805 | + } | ||
| 806 | +} | ||
| 807 | + | ||
| 808 | +void ProcessMemPidDecision::FailBorrowAbort(pid_t pid, const std::string& debtId, uint64_t need, uint64_t roundNum) | ||
| 809 | +{ | ||
| 810 | + UBSE_LOG_ERROR << "[process_mem] borrow round=" << roundNum << " pid=" << pid | ||
| 811 | + << " slot_failed reason=numa_target_migrate_failed"; | ||
| 812 | + if (need > 0) { | ||
| 813 | + pid::bridge::ProcessMemPidBridge::MemoryReturn(debtId); | ||
| 814 | + } | ||
| 815 | + RemoveSlotFinalize(pid, debtId); | ||
| 816 | +} | ||
| 817 | + | ||
| 818 | +void ProcessMemPidDecision::AsyncBorrowAndMigrate(const std::string& debtId, pid_t pid, uint64_t amount, int srcNumaId, | ||
| 819 | + uint64_t roundNum) | ||
| 820 | +{ | ||
| 821 | + uint64_t need = amount; | ||
| 822 | + std::map<int, uint64_t> increments; | ||
| 823 | + | ||
| 824 | + CreatedDebtInfo created; | ||
| 825 | + if (!CreateNumaDebt(pid, need, srcNumaId, debtId, roundNum, created)) { | ||
| 826 | + return; | ||
| 827 | + } | ||
| 828 | + increments[created.remoteNumaId] += need; | ||
| 829 | + UBSE_LOG_INFO << "[process_mem] borrow round=" << roundNum << " pid=" << pid << " ubse_created debt_id=" << debtId | ||
| 830 | + << " export_slot=" << created.exportSlotId << " remote_numa=" << created.remoteNumaId | ||
| 831 | + << " capacity_gb=" << BytesToGbDouble(created.capacity); | ||
| 832 | + | ||
| 833 | + std::vector<std::pair<int, uint64_t>> numaTargets; | ||
| 834 | + def::AtomicMigrateResult migrateResult = CommitBorrowAndMigrate(pid, debtId, created, increments, numaTargets); | ||
| 835 | + if (migrateResult == def::AtomicMigrateResult::kVanish) { | ||
| 836 | + pid::bridge::ProcessMemPidBridge::MemoryReturn(debtId); | ||
| 837 | + UBSE_LOG_WARN << "[process_mem] borrow round=" << roundNum << " pid=" << pid | ||
| 838 | + << " slot_vanished debt_id=" << debtId | ||
| 839 | + << " (removed by timeout or taken over by return, ubse debt returned)"; | ||
| 840 | + return; | ||
| 841 | + } | ||
| 842 | + if (migrateResult != def::AtomicMigrateResult::kOk) { | ||
| 843 | + FailBorrowAbort(pid, debtId, need, roundNum); | ||
| 844 | + return; | ||
| 845 | + } | ||
| 846 | + ProcessMemPidInfoManager::GetInstance().UpdateManagedPidLastMigrateTime(pid); | ||
| 847 | + UBSE_LOG_INFO << "[process_mem] borrow round=" << roundNum << " pid=" << pid << " slot_completed debt_id=" << debtId | ||
| 848 | + << " numa_target_migrate"; | ||
| 849 | +} | ||
| 850 | + | ||
| 851 | +int ProcessMemPidDecision::RmrsMigrateToNumas(pid_t pid, const std::vector<std::pair<int, uint64_t>>& numaTargets) | ||
| 852 | +{ | ||
| 853 | + std::vector<mempooling::smap::MigrateOutPayload> payloads{}; | ||
| 854 | + mempooling::smap::MigrateOutPayload payload{}; | ||
| 855 | + constexpr size_t kMaxInner = static_cast<size_t>(mempooling::smap::REMOTE_NUMA_NUM); | ||
| 856 | + size_t innerCount = std::min(numaTargets.size(), kMaxInner); | ||
| 857 | + if (numaTargets.size() > kMaxInner) { | ||
| 858 | + UBSE_LOG_WARN << "[process_mem] borrow pid=" << pid << " numa_targets=" << numaTargets.size() | ||
| 859 | + << " exceed inner capacity " << kMaxInner << ", truncated"; | ||
| 860 | + } | ||
| 861 | + for (size_t i = 0; i < innerCount; ++i) { | ||
| 862 | + mempooling::smap::MigrateOutPayloadInner inner{}; | ||
| 863 | + inner.migrateMode = mempooling::smap::MIG_MEMSIZE_MODE; | ||
| 864 | + inner.memSize = numaTargets[i].second; | ||
| 865 | + inner.destNid = numaTargets[i].first; | ||
| 866 | + payload.inner[i] = inner; | ||
| 867 | + } | ||
| 868 | + payload.count = static_cast<int>(innerCount); | ||
| 869 | + payload.pid = pid; | ||
| 870 | + payloads.push_back(payload); | ||
| 871 | + if (!pid::bridge::ProcessMemPidBridge::rmrsMigrateOut) { | ||
| 872 | + UBSE_LOG_ERROR << "[process_mem] borrow pid=" << pid << " rmrsMigrateOut not loaded, skip migrate"; | ||
| 873 | + return -1; | ||
| 874 | + } | ||
| 875 | + return pid::bridge::ProcessMemPidBridge::rmrsMigrateOut(payloads, 0); | ||
| 876 | +} | ||
| 877 | + | ||
| 878 | +void ProcessMemPidDecision::CheckTimeouts(uint64_t roundNum) | ||
| 879 | +{ | ||
| 880 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 881 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 882 | + | ||
| 883 | + for (const auto& snapshotItem : snapshot) { | ||
| 884 | + pid_t pid = snapshotItem.first; | ||
| 885 | + const auto& entry = snapshotItem.second; | ||
| 886 | + if (entry.borrow.slots.empty()) { | ||
| 887 | + continue; | ||
| 888 | + } | ||
| 889 | + infoMgr.UpdateManagedPidBorrowStateAtomic( | ||
| 890 | + pid, [this, pid, roundNum](def::BorrowState& newBorrow, def::ProcessStatus& newStatus) { | ||
| 891 | + if (!CheckPidTimeoutSlots(pid, newBorrow, roundNum)) { | ||
| 892 | + return; | ||
| 893 | + } | ||
| 894 | + newBorrow.currentRemote = 0; | ||
| 895 | + for (const auto& s : newBorrow.slots) { | ||
| 896 | + if (s.status == def::BorrowSlotStatus::COMPLETED) { | ||
| 897 | + newBorrow.currentRemote += s.migratedBytes; | ||
| 898 | + } | ||
| 899 | + } | ||
| 900 | + newBorrow.slots.erase( | ||
| 901 | + std::remove_if(newBorrow.slots.begin(), newBorrow.slots.end(), | ||
| 902 | + [](const def::BorrowSlot& s) { return s.status == def::BorrowSlotStatus::FAILED; }), | ||
| 903 | + newBorrow.slots.end()); | ||
| 904 | + newStatus = ComputeSlotStatus(newBorrow.slots); | ||
| 905 | + }); | ||
| 906 | + } | ||
| 907 | +} | ||
| 908 | + | ||
| 909 | +bool ProcessMemPidDecision::CheckPidTimeoutSlots(pid_t pid, def::BorrowState& borrow, uint64_t roundNum) | ||
| 910 | +{ | ||
| 911 | + bool hasChanges = false; | ||
| 912 | + for (auto& slot : borrow.slots) { | ||
| 913 | + if (slot.status != def::BorrowSlotStatus::BORROWING) { | ||
| 914 | + continue; | ||
| 915 | + } | ||
| 916 | + | ||
| 917 | + auto now = std::chrono::steady_clock::now(); | ||
| 918 | + auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - slot.borrowTime).count(); | ||
| 919 | + uint64_t blocks128Mb = (slot.migratedBytes / BYTES_PER_MB + MB_128 - 1) / MB_128; | ||
| 920 | + auto timeoutMs = static_cast<int64_t>(blocks128Mb * timeoutPer128MbMs_); | ||
| 921 | + | ||
| 922 | + if (elapsedMs <= timeoutMs) { | ||
| 923 | + continue; | ||
| 924 | + } | ||
| 925 | + | ||
| 926 | + UBSE_LOG_INFO << "[process_mem] borrow round=" << roundNum << " pid=" << pid | ||
| 927 | + << " slot_timeout migrated_gb=" << BytesToGbDouble(slot.migratedBytes) | ||
| 928 | + << " remote_numa=" << slot.remoteNumaId << " debt_id=" << slot.debtId | ||
| 929 | + << " elapsed_ms=" << elapsedMs << " timeout_ms=" << timeoutMs; | ||
| 930 | + | ||
| 931 | + slot.status = def::BorrowSlotStatus::FAILED; | ||
| 932 | + hasChanges = true; | ||
| 933 | + | ||
| 934 | + if (!EnqueueReturnDebt(pid, def::ReturnRequestItem{slot.debtId, slot.migratedBytes}, ReturnScene::TIMEOUT)) { | ||
| 935 | + pid::bridge::ProcessMemPidBridge::MemoryReturn(slot.debtId); | ||
| 936 | + } | ||
| 937 | + } | ||
| 938 | + return hasChanges; | ||
| 939 | +} | ||
| 940 | + | ||
| 941 | +std::optional<uint64_t> ProcessMemPidDecision::GetNodeFreeBytes() | ||
| 942 | +{ | ||
| 943 | + auto memAvailableKb = ReadMemAvailableKb(); | ||
| 944 | + if (!memAvailableKb.has_value()) { | ||
| 945 | + return std::nullopt; | ||
| 946 | + } | ||
| 947 | + return *memAvailableKb * 1024; | ||
| 948 | +} | ||
| 949 | + | ||
| 950 | +std::optional<uint64_t> ProcessMemPidDecision::ReadMemAvailableKb() | ||
| 951 | +{ | ||
| 952 | + if (memAvailableReader) { | ||
| 953 | + return memAvailableReader(); | ||
| 954 | + } | ||
| 955 | + std::ifstream meminfo("/proc/meminfo"); | ||
| 956 | + std::string line; | ||
| 957 | + while (std::getline(meminfo, line)) { | ||
| 958 | + if (line.rfind("MemAvailable:", 0) == 0) { | ||
| 959 | + std::istringstream iss(line); | ||
| 960 | + std::string key; | ||
| 961 | + uint64_t kb = 0; | ||
| 962 | + if (iss >> key >> kb) { | ||
| 963 | + return kb; | ||
| 964 | + } | ||
| 965 | + return std::nullopt; | ||
| 966 | + } | ||
| 967 | + } | ||
| 968 | + return std::nullopt; | ||
| 969 | +} | ||
| 970 | + | ||
| 971 | +void ProcessMemPidDecision::BroadcastPassiveReturn(uint64_t roundNum, uint64_t nodeFree, bool emergency) | ||
| 972 | +{ | ||
| 973 | + auto nodeId = GetCurrentNodeId(); | ||
| 974 | + | ||
| 975 | + std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo> debts; | ||
| 976 | + auto ret = ubse::mem::controller::UbseGetNumaMemDebtInfoWithNode(nodeId, debts); | ||
| 977 | + if (ret != UBSE_OK) { | ||
| 978 | + UBSE_LOG_ERROR << "[process_mem] return passive round=" << roundNum << " get own debts failed, ret=" << ret; | ||
| 979 | + return; | ||
| 980 | + } | ||
| 981 | + | ||
| 982 | + std::unordered_map<std::string, std::vector<def::ReturnRequestItem>> groups; | ||
| 983 | + uint64_t lentDebtCount = 0; | ||
| 984 | + for (const auto& debt : debts) { | ||
| 985 | + if (debt.lentNodeId != nodeId) { | ||
| 986 | + continue; | ||
| 987 | + } | ||
| 988 | + ++lentDebtCount; | ||
| 989 | + if (debt.borrowNodeId.empty() || debt.borrowNodeId == nodeId) { | ||
| 990 | + continue; | ||
| 991 | + } | ||
| 992 | + groups[debt.borrowNodeId].push_back(def::ReturnRequestItem{debt.name, debt.size}); | ||
| 993 | + } | ||
| 994 | + | ||
| 995 | + for (const auto& [borrowerNode, items] : groups) { | ||
| 996 | + auto sendRet = pid::bridge::ProcessMemPidBridge::SendReturnRequestToNode(borrowerNode, items); | ||
| 997 | + if (sendRet != UBSE_OK) { | ||
| 998 | + UBSE_LOG_WARN << "[process_mem] return passive round=" << roundNum << " send failed to " << borrowerNode | ||
| 999 | + << ", ret=" << sendRet; | ||
| 1000 | + } | ||
| 1001 | + } | ||
| 1002 | + | ||
| 1003 | + if (emergency) { | ||
| 1004 | + UBSE_LOG_INFO << "[process_mem] oom lender_emergency: broadcasting to " << groups.size() | ||
| 1005 | + << " borrowers, lent_debts=" << lentDebtCount; | ||
| 1006 | + } else { | ||
| 1007 | + UBSE_LOG_INFO << "[process_mem] return passive lender=" << nodeId << " broadcasting to " << groups.size() | ||
| 1008 | + << " borrowers (nodeFree=" << BytesToGbDouble(nodeFree) | ||
| 1009 | + << " < threshold=" << BytesToGbDouble(freeMemoryThresholdBytes_) << ")"; | ||
| 1010 | + } | ||
| 1011 | +} | ||
| 1012 | + | ||
| 1013 | +uint32_t ProcessMemPidDecision::HandleReturnRequest(const std::vector<def::ReturnRequestItem>& items) | ||
| 1014 | +{ | ||
| 1015 | + if (items.empty()) { | ||
| 1016 | + return UBSE_ERROR; | ||
| 1017 | + } | ||
| 1018 | + | ||
| 1019 | + for (const auto& item : items) { | ||
| 1020 | + if (!EnqueueReturnDebt(0, item, ReturnScene::PASSIVE)) { | ||
| 1021 | + UBSE_LOG_WARN << "[process_mem] return passive debt_id=" << item.name | ||
| 1022 | + << " enqueue failed (stopping/queue full)"; | ||
| 1023 | + } | ||
| 1024 | + } | ||
| 1025 | + return UBSE_OK; | ||
| 1026 | +} | ||
| 1027 | + | ||
| 1028 | +bool ProcessMemPidDecision::EnqueueReturnDebt(pid_t pid, const def::ReturnRequestItem& item, ReturnScene scene) | ||
| 1029 | +{ | ||
| 1030 | + if (stopping_) { | ||
| 1031 | + return false; | ||
| 1032 | + } | ||
| 1033 | + std::string traceId = TraceContext::GetTraceId(); | ||
| 1034 | + return returnExecutor_->Execute([this, pid, item, scene, traceId]() { | ||
| 1035 | + TraceContext::SetTraceId(traceId); | ||
| 1036 | + RunReturnDebt(pid, item, scene); | ||
| 1037 | + TraceContext::Clear(); | ||
| 1038 | + }); | ||
| 1039 | +} | ||
| 1040 | + | ||
| 1041 | +void ProcessMemPidDecision::RunReturnDebt(pid_t pid, def::ReturnRequestItem item, ReturnScene scene) | ||
| 1042 | +{ | ||
| 1043 | + if (pid <= 0) { | ||
| 1044 | + pid = ResolveDebtPid(item.name); | ||
| 1045 | + } | ||
| 1046 | + | ||
| 1047 | + uint32_t ret = UBSE_OK; | ||
| 1048 | + if (pid < 0) { | ||
| 1049 | + ret = UBSE_ERROR; | ||
| 1050 | + } else if (pid == 0) { | ||
| 1051 | + ProcessMemPidInfoManager::GetInstance().ResetSlotByDebtName(item.name); | ||
| 1052 | + UBSE_LOG_INFO << "[process_mem] return " << ReturnSceneToString(scene) << " debt_id=" << item.name | ||
| 1053 | + << " skip: debt not found locally"; | ||
| 1054 | + return; | ||
| 1055 | + } else { | ||
| 1056 | + ret = DoReturnDebtOnce(pid, item, scene); | ||
| 1057 | + } | ||
| 1058 | + | ||
| 1059 | + if (ret == UBSE_OK) { | ||
| 1060 | + return; | ||
| 1061 | + } | ||
| 1062 | + | ||
| 1063 | + if (ret == UBSE_ERR_NOT_EXIST) { | ||
| 1064 | + ProcessMemPidInfoManager::GetInstance().UpdateManagedPidSlotReturnStatus(pid, item.name, | ||
| 1065 | + def::ReturnStatus::NONE); | ||
| 1066 | + return; | ||
| 1067 | + } | ||
| 1068 | + | ||
| 1069 | + ProcessMemPidInfoManager::GetInstance().UpdateManagedPidSlotReturnStatus(pid, item.name, def::ReturnStatus::NONE); | ||
| 1070 | + UBSE_LOG_DEBUG << "[process_mem] return " << ReturnSceneToString(scene) << " debt_id=" << item.name | ||
| 1071 | + << " failed (ret=" << ret << "), retry later"; | ||
| 1072 | + uint32_t retryCount = 0; | ||
| 1073 | + RetryReturnEnqueue(retryCount, [this, pid, item, scene]() { return EnqueueReturnDebt(pid, item, scene); }); | ||
| 1074 | +} | ||
| 1075 | + | ||
| 1076 | +uint32_t ProcessMemPidDecision::DoReturnDebtOnce(pid_t pid, const def::ReturnRequestItem& item, ReturnScene scene) | ||
| 1077 | +{ | ||
| 1078 | + std::lock_guard<std::mutex> pidLock(GetPidMutex(pid)); | ||
| 1079 | + if (scene == ReturnScene::TIMEOUT) { | ||
| 1080 | + auto delRet = pid::bridge::ProcessMemPidBridge::MemoryReturn(item.name); | ||
| 1081 | + if (delRet == UBSE_OK || delRet == UBSE_ERR_NOT_EXIST) { | ||
| 1082 | + return UBSE_OK; | ||
| 1083 | + } | ||
| 1084 | + UBSE_LOG_WARN << "[process_mem] return timeout debt_id=" << item.name << " ubse delete failed ret=" << delRet; | ||
| 1085 | + return delRet; | ||
| 1086 | + } | ||
| 1087 | + ProcessMemPidInfoManager::GetInstance().UpdateManagedPidSlotReturnStatus(pid, item.name, | ||
| 1088 | + def::ReturnStatus::RETURNING); | ||
| 1089 | + if (item.size == 0) { | ||
| 1090 | + auto delRet = pid::bridge::ProcessMemPidBridge::MemoryReturn(item.name); | ||
| 1091 | + if (delRet == UBSE_OK || delRet == UBSE_ERR_NOT_EXIST) { | ||
| 1092 | + ProcessMemPidInfoManager::GetInstance().UpdateManagedPidSlotReturned(pid, item.name, 0); | ||
| 1093 | + return UBSE_OK; | ||
| 1094 | + } | ||
| 1095 | + UBSE_LOG_WARN << "[process_mem] return " << ReturnSceneToString(scene) << " debt_id=" << item.name | ||
| 1096 | + << " ubse delete failed ret=" << delRet; | ||
| 1097 | + return delRet; | ||
| 1098 | + } | ||
| 1099 | + if (scene == ReturnScene::ACTIVE) { | ||
| 1100 | + auto freeRet = pid::bridge::ProcessMemPidBridge::rmrsFreeWithMigrate(item.name); | ||
| 1101 | + if (freeRet != UBSE_OK) { | ||
| 1102 | + UBSE_LOG_WARN << "[process_mem] return " << ReturnSceneToString(scene) << " debt_id=" << item.name | ||
| 1103 | + << " migrate_back failed ret=" << freeRet; | ||
| 1104 | + return freeRet; | ||
| 1105 | + } | ||
| 1106 | + auto delRet = pid::bridge::ProcessMemPidBridge::MemoryReturn(item.name); | ||
| 1107 | + if (delRet == UBSE_OK || delRet == UBSE_ERR_NOT_EXIST) { | ||
| 1108 | + ProcessMemPidInfoManager::GetInstance().UpdateManagedPidSlotReturned(pid, item.name, item.size); | ||
| 1109 | + return UBSE_OK; | ||
| 1110 | + } | ||
| 1111 | + UBSE_LOG_WARN << "[process_mem] return " << ReturnSceneToString(scene) << " debt_id=" << item.name | ||
| 1112 | + << " ubse delete failed ret=" << delRet; | ||
| 1113 | + return delRet; | ||
| 1114 | + } | ||
| 1115 | + auto nodeFreeOpt = GetNodeFreeBytes(); | ||
| 1116 | + if (!nodeFreeOpt.has_value()) { | ||
| 1117 | + UBSE_LOG_WARN << "[process_mem] return " << ReturnSceneToString(scene) << " debt_id=" << item.name | ||
| 1118 | + << " read memAvailable failed, retry later"; | ||
| 1119 | + return UBSE_ERROR; | ||
| 1120 | + } | ||
| 1121 | + return ReturnOneDebt(item, *nodeFreeOpt); | ||
| 1122 | +} | ||
| 1123 | + | ||
| 1124 | +pid_t ProcessMemPidDecision::ResolveDebtPid(const std::string& name) | ||
| 1125 | +{ | ||
| 1126 | + std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo> debts; | ||
| 1127 | + if (ubse::mem::controller::UbseGetNumaMemDebtInfoWithNode(GetCurrentNodeId(), debts) != UBSE_OK) { | ||
| 1128 | + return -1; | ||
| 1129 | + } | ||
| 1130 | + for (const auto& debt : debts) { | ||
| 1131 | + if (debt.name != name) { | ||
| 1132 | + continue; | ||
| 1133 | + } | ||
| 1134 | + def::ProcessMemUsrInfo usrInfo{}; | ||
| 1135 | + if (memcpy_s(&usrInfo, sizeof(usrInfo), debt.usrInfo, sizeof(usrInfo)) == EOK && usrInfo.pid > 0) { | ||
| 1136 | + return static_cast<pid_t>(usrInfo.pid); | ||
| 1137 | + } | ||
| 1138 | + } | ||
| 1139 | + return 0; | ||
| 1140 | +} | ||
| 1141 | + | ||
| 1142 | +std::mutex& ProcessMemPidDecision::GetPidMutex(pid_t pid) | ||
| 1143 | +{ | ||
| 1144 | + return pidMutexes_[static_cast<size_t>(pid) % kPidMutexNum]; | ||
| 1145 | +} | ||
| 1146 | + | ||
| 1147 | +bool ProcessMemPidDecision::EnqueuePidReturn(pid_t pid, ReturnScene scene) | ||
| 1148 | +{ | ||
| 1149 | + if (stopping_) { | ||
| 1150 | + return false; | ||
| 1151 | + } | ||
| 1152 | + return returnExecutor_->Execute([this, pid, scene]() { RunPidReturn(pid, scene); }); | ||
| 1153 | +} | ||
| 1154 | + | ||
| 1155 | +uint32_t ProcessMemPidDecision::DoPidReturnOnce(pid_t pid, ReturnScene scene) | ||
| 1156 | +{ | ||
| 1157 | + std::lock_guard<std::mutex> pidLock(GetPidMutex(pid)); | ||
| 1158 | + auto nodeId = GetCurrentNodeId(); | ||
| 1159 | + | ||
| 1160 | + std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo> debts; | ||
| 1161 | + auto ret = ubse::mem::controller::UbseGetNumaMemDebtInfoWithNode(nodeId, debts); | ||
| 1162 | + if (ret != UBSE_OK) { | ||
| 1163 | + UBSE_LOG_WARN << "[process_mem] return " << ReturnSceneToString(scene) << " pid=" << pid | ||
| 1164 | + << " skip: get own debts failed, ret=" << ret; | ||
| 1165 | + return ret; | ||
| 1166 | + } | ||
| 1167 | + | ||
| 1168 | + auto matched = MatchPidDebts(pid, debts); | ||
| 1169 | + if (matched.empty()) { | ||
| 1170 | + UBSE_LOG_INFO << "[process_mem] return " << ReturnSceneToString(scene) << " pid=" << pid << " no debts"; | ||
| 1171 | + return UBSE_OK; | ||
| 1172 | + } | ||
| 1173 | + | ||
| 1174 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 1175 | + for (const auto* debt : matched) { | ||
| 1176 | + infoMgr.UpdateManagedPidSlotReturnStatus(pid, debt->name, def::ReturnStatus::RETURNING); | ||
| 1177 | + } | ||
| 1178 | + | ||
| 1179 | + std::unordered_map<uint32_t, size_t> numaDistribution; | ||
| 1180 | + bool procExited = collect::ProcessMemPidCollect::GetInstance().CollectProcessNumaMemDistribution( | ||
| 1181 | + pid, numaDistribution) != UBSE_OK; | ||
| 1182 | + | ||
| 1183 | + uint64_t freedBytes = 0; | ||
| 1184 | + uint32_t freedCount = 0; | ||
| 1185 | + uint32_t failCount = 0; | ||
| 1186 | + for (const auto* debt : matched) { | ||
| 1187 | + if (ReturnOnePidDebt(*debt, procExited, freedCount, freedBytes, failCount)) { | ||
| 1188 | + infoMgr.UpdateManagedPidSlotReturned(pid, debt->name, debt->size); | ||
| 1189 | + } | ||
| 1190 | + } | ||
| 1191 | + | ||
| 1192 | + if (freedCount > 0 || failCount > 0) { | ||
| 1193 | + UBSE_LOG_INFO << "[process_mem] return " << ReturnSceneToString(scene) << " pid=" << pid | ||
| 1194 | + << " debts=" << (freedCount + failCount) << " freed=" << freedCount << " failed=" << failCount | ||
| 1195 | + << " amount_gb=" << BytesToGbDouble(freedBytes); | ||
| 1196 | + } | ||
| 1197 | + for (const auto* debt : matched) { | ||
| 1198 | + infoMgr.UpdateManagedPidSlotReturnStatus(pid, debt->name, def::ReturnStatus::NONE); | ||
| 1199 | + } | ||
| 1200 | + return (failCount > 0) ? UBSE_ERROR : UBSE_OK; | ||
| 1201 | +} | ||
| 1202 | + | ||
| 1203 | +bool ProcessMemPidDecision::ReturnOnePidDebt(const ubse::mem::controller::UbseNumaMemoryDebtInfo& debt, bool procExited, | ||
| 1204 | + uint32_t& freedCount, uint64_t& freedBytes, uint32_t& failCount) | ||
| 1205 | +{ | ||
| 1206 | + if (procExited) { | ||
| 1207 | + auto delRet = pid::bridge::ProcessMemPidBridge::MemoryReturn(debt.name); | ||
| 1208 | + if (delRet != UBSE_OK && delRet != UBSE_ERR_NOT_EXIST) { | ||
| 1209 | + ++failCount; | ||
| 1210 | + UBSE_LOG_WARN << "[process_mem] return debt_id=" << debt.name << " ubse delete failed ret=" << delRet | ||
| 1211 | + << ", skip rmrs return"; | ||
| 1212 | + return false; | ||
| 1213 | + } | ||
| 1214 | + } | ||
| 1215 | + if (!pid::bridge::ProcessMemPidBridge::rmrsFreeWithMigrate) { | ||
| 1216 | + ++failCount; | ||
| 1217 | + UBSE_LOG_WARN << "[process_mem] return debt_id=" << debt.name << " rmrsFreeWithMigrate not loaded, skip free"; | ||
| 1218 | + return false; | ||
| 1219 | + } | ||
| 1220 | + auto freeRet = pid::bridge::ProcessMemPidBridge::rmrsFreeWithMigrate(debt.name); | ||
| 1221 | + if (freeRet != UBSE_OK) { | ||
| 1222 | + ++failCount; | ||
| 1223 | + UBSE_LOG_WARN << "[process_mem] return debt_id=" << debt.name << " free failed ret=" << freeRet; | ||
| 1224 | + return false; | ||
| 1225 | + } | ||
| 1226 | + ++freedCount; | ||
| 1227 | + freedBytes += debt.size; | ||
| 1228 | + return true; | ||
| 1229 | +} | ||
| 1230 | + | ||
| 1231 | +void ProcessMemPidDecision::RunPidReturn(pid_t pid, ReturnScene scene) | ||
| 1232 | +{ | ||
| 1233 | + uint32_t ret = DoPidReturnOnce(pid, scene); | ||
| 1234 | + if (ret == UBSE_OK || ret == UBSE_ERR_NOT_EXIST) { | ||
| 1235 | + return; | ||
| 1236 | + } | ||
| 1237 | + UBSE_LOG_DEBUG << "[process_mem] return " << ReturnSceneToString(scene) << " pid=" << pid << " failed (ret=" << ret | ||
| 1238 | + << "), retry later"; | ||
| 1239 | + uint32_t retryCount = 0; | ||
| 1240 | + RetryReturnEnqueue(retryCount, [this, pid, scene]() { return EnqueuePidReturn(pid, scene); }); | ||
| 1241 | +} | ||
| 1242 | + | ||
| 1243 | +uint32_t ProcessMemPidDecision::HandlePidExited(pid_t pid) | ||
| 1244 | +{ | ||
| 1245 | + if (EnqueuePidReturn(pid, ReturnScene::EXITED)) { | ||
| 1246 | + return UBSE_OK; | ||
| 1247 | + } | ||
| 1248 | + return UBSE_ERROR; | ||
| 1249 | +} | ||
| 1250 | + | ||
| 1251 | +void ProcessMemPidDecision::RecoverBorrowFromObmm() | ||
| 1252 | +{ | ||
| 1253 | + std::vector<ubse::mem::controller::UbseNumaMemoryImportDebtInfo> debts; | ||
| 1254 | + auto ret = ubse::mem::controller::UbseGetNumaMemImportDebtInfoWithLocalNode(debts); | ||
| 1255 | + if (ret != UBSE_OK) { | ||
| 1256 | + UBSE_LOG_WARN << "[process_mem] recover borrow: get import debts failed, ret=" << ret; | ||
| 1257 | + return; | ||
| 1258 | + } | ||
| 1259 | + if (debts.empty()) { | ||
| 1260 | + return; | ||
| 1261 | + } | ||
| 1262 | + | ||
| 1263 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 1264 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1265 | + | ||
| 1266 | + size_t recovered = 0; | ||
| 1267 | + size_t orphaned = 0; | ||
| 1268 | + size_t skipped = 0; | ||
| 1269 | + for (const auto& debt : debts) { | ||
| 1270 | + def::ProcessMemUsrInfo usrInfo{}; | ||
| 1271 | + if (memcpy_s(&usrInfo, sizeof(usrInfo), debt.usrInfo, sizeof(usrInfo)) != EOK || | ||
| 1272 | + usrInfo.pluginId != def::UsrInfoPluginType::PROCESS_MEM || usrInfo.pid <= 0) { | ||
| 1273 | + ++skipped; | ||
| 1274 | + continue; | ||
| 1275 | + } | ||
| 1276 | + pid_t pid = static_cast<pid_t>(usrInfo.pid); | ||
| 1277 | + if (IsBorrowBindable(pid, usrInfo, snapshot)) { | ||
| 1278 | + RecoverBindDebt(pid, debt); | ||
| 1279 | + ++recovered; | ||
| 1280 | + } else { | ||
| 1281 | + ++orphaned; | ||
| 1282 | + EnqueueOrphanReturn(debt.name); | ||
| 1283 | + } | ||
| 1284 | + } | ||
| 1285 | + UBSE_LOG_INFO << "[process_mem] recover borrow: debts=" << debts.size() << " recovered=" << recovered | ||
| 1286 | + << " orphaned=" << orphaned << " skipped=" << skipped; | ||
| 1287 | + | ||
| 1288 | + RecoverSmapProcessConfig(); | ||
| 1289 | +} | ||
| 1290 | + | ||
| 1291 | +void ProcessMemPidDecision::RecoverBindDebt(pid_t pid, const ubse::mem::controller::UbseNumaMemoryImportDebtInfo& debt) | ||
| 1292 | +{ | ||
| 1293 | + def::ProcessMemUsrInfo usrInfo{}; | ||
| 1294 | + if (memcpy_s(&usrInfo, sizeof(usrInfo), debt.usrInfo, sizeof(usrInfo)) != EOK) { | ||
| 1295 | + return; | ||
| 1296 | + } | ||
| 1297 | + def::BorrowSlot slot{}; | ||
| 1298 | + slot.debtId = debt.name; | ||
| 1299 | + slot.migratedBytes = debt.size; | ||
| 1300 | + slot.capacity = debt.size; | ||
| 1301 | + slot.remoteNumaId = static_cast<int>(debt.remoteNumaId); | ||
| 1302 | + slot.srcNumaId = static_cast<int>(usrInfo.srcNuma); | ||
| 1303 | + slot.borrowTime = std::chrono::steady_clock::now(); | ||
| 1304 | + slot.status = def::BorrowSlotStatus::COMPLETED; | ||
| 1305 | + ProcessMemPidInfoManager::GetInstance().UpdateManagedPidBorrowStateAtomic( | ||
| 1306 | + pid, [&slot](def::BorrowState& newBorrow, def::ProcessStatus& newStatus) { | ||
| 1307 | + newBorrow.slots.push_back(slot); | ||
| 1308 | + newStatus = def::ProcessStatus::BORROWED; | ||
| 1309 | + }); | ||
| 1310 | + UBSE_LOG_INFO << "[process_mem] recover borrow: pid=" << pid << " debt=" << debt.name | ||
| 1311 | + << " amount_gb=" << BytesToGbDouble(debt.size) << " recovered (status=COMPLETED)"; | ||
| 1312 | +} | ||
| 1313 | + | ||
| 1314 | +bool ProcessMemPidDecision::EnqueueOrphanReturn(const std::string& debtName) | ||
| 1315 | +{ | ||
| 1316 | + if (stopping_) { | ||
| 1317 | + return false; | ||
| 1318 | + } | ||
| 1319 | + std::string traceId = TraceContext::GetTraceId(); | ||
| 1320 | + return returnExecutor_->Execute([this, debtName, traceId]() { | ||
| 1321 | + TraceContext::SetTraceId(traceId); | ||
| 1322 | + RunOrphanReturn(debtName); | ||
| 1323 | + TraceContext::Clear(); | ||
| 1324 | + }); | ||
| 1325 | +} | ||
| 1326 | + | ||
| 1327 | +void ProcessMemPidDecision::RunOrphanReturn(const std::string& debtName) | ||
| 1328 | +{ | ||
| 1329 | + auto ret = pid::bridge::ProcessMemPidBridge::MemoryReturn(debtName); | ||
| 1330 | + if (ret == UBSE_OK || ret == UBSE_ERR_NOT_EXIST) { | ||
| 1331 | + UBSE_LOG_INFO << "[process_mem] recover borrow: debt=" << debtName << " orphaned, force returned (ret=" << ret | ||
| 1332 | + << ")"; | ||
| 1333 | + return; | ||
| 1334 | + } | ||
| 1335 | + | ||
| 1336 | + UBSE_LOG_DEBUG << "[process_mem] recover borrow: debt=" << debtName << " orphaned, force return failed (ret=" << ret | ||
| 1337 | + << "), retry later"; | ||
| 1338 | + uint32_t retryCount = 0; | ||
| 1339 | + RetryReturnEnqueue(retryCount, [this, debtName]() { return EnqueueOrphanReturn(debtName); }); | ||
| 1340 | +} | ||
| 1341 | + | ||
| 1342 | +void ProcessMemPidDecision::RetryReturnEnqueue(uint32_t& retryCount, const std::function<bool()>& enqueue) | ||
| 1343 | +{ | ||
| 1344 | + ++retryCount; | ||
| 1345 | + if (retryCount > kMaxReturnRetry) { | ||
| 1346 | + UBSE_LOG_WARN << "[process_mem] return retry exhausted (retries=" << retryCount << "), give up"; | ||
| 1347 | + return; | ||
| 1348 | + } | ||
| 1349 | + std::this_thread::sleep_for(std::chrono::milliseconds(returnRetryIntervalMs_)); | ||
| 1350 | + while (!enqueue() && !stopping_) { | ||
| 1351 | + std::this_thread::sleep_for(std::chrono::milliseconds(returnRetryIntervalMs_)); | ||
| 1352 | + } | ||
| 1353 | +} | ||
| 1354 | + | ||
| 1355 | +uint32_t ProcessMemPidDecision::RecoverSmapProcessConfig() | ||
| 1356 | +{ | ||
| 1357 | + std::unordered_map<pid_t, std::unordered_map<int, uint64_t>> smapMemKb; | ||
| 1358 | + std::optional<std::set<int>> failedNumas = std::set<int>{}; | ||
| 1359 | + size_t queryFail = 0; | ||
| 1360 | + QuerySmapProcessConfigs(smapMemKb, failedNumas, queryFail); | ||
| 1361 | + | ||
| 1362 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 1363 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1364 | + | ||
| 1365 | + if (!failedNumas.has_value()) { | ||
| 1366 | + UBSE_LOG_WARN << "[process_mem] recover config: smap query unavailable (plugin/import-debt), " | ||
| 1367 | + << "skip migratedBytes recover to avoid over-borrow"; | ||
| 1368 | + return 0; | ||
| 1369 | + } | ||
| 1370 | + | ||
| 1371 | + size_t filled = 0; | ||
| 1372 | + uint64_t filledBytes = 0; | ||
| 1373 | + for (const auto& [pid, state] : snapshot) { | ||
| 1374 | + if (state.borrow.slots.empty()) { | ||
| 1375 | + continue; | ||
| 1376 | + } | ||
| 1377 | + RecoverPidMigratedBytes(pid, smapMemKb, *failedNumas, filled, filledBytes); | ||
| 1378 | + } | ||
| 1379 | + | ||
| 1380 | + size_t dirty = 0; | ||
| 1381 | + size_t smapEntries = 0; | ||
| 1382 | + ReportDirtySmapStates(smapMemKb, snapshot, dirty, smapEntries); | ||
| 1383 | + UBSE_LOG_INFO << "[process_mem] recover config: smap_numa_entries=" << smapEntries << " filled=" << filled | ||
| 1384 | + << " filled_gb=" << BytesToGbDouble(filledBytes) << " dirty=" << dirty << " query_fail=" << queryFail; | ||
| 1385 | + return static_cast<uint32_t>(dirty); | ||
| 1386 | +} | ||
| 1387 | + | ||
| 1388 | +void ProcessMemPidDecision::RecoverPidMigratedBytes( | ||
| 1389 | + pid_t pid, const std::unordered_map<pid_t, std::unordered_map<int, uint64_t>>& smapMemKb, | ||
| 1390 | + const std::set<int>& failedNumas, size_t& filled, uint64_t& filledBytes) | ||
| 1391 | +{ | ||
| 1392 | + constexpr uint64_t kBytesPerKb = 1024; | ||
| 1393 | + static const std::unordered_map<int, uint64_t> kEmptyNumaMap; | ||
| 1394 | + auto numaIt = smapMemKb.find(pid); | ||
| 1395 | + const auto& numaMap = (numaIt != smapMemKb.end()) ? numaIt->second : kEmptyNumaMap; | ||
| 1396 | + | ||
| 1397 | + size_t slotCount = 0; | ||
| 1398 | + uint64_t total = 0; | ||
| 1399 | + ProcessMemPidInfoManager::GetInstance().UpdateManagedPidBorrowStateAtomic( | ||
| 1400 | + pid, [&](def::BorrowState& newBorrow, def::ProcessStatus& newStatus) { | ||
| 1401 | + slotCount = newBorrow.slots.size(); | ||
| 1402 | + std::unordered_map<int, uint64_t> remainingByNuma; | ||
| 1403 | + for (auto& slot : newBorrow.slots) { | ||
| 1404 | + if (failedNumas.count(slot.remoteNumaId) > 0) { | ||
| 1405 | + continue; | ||
| 1406 | + } | ||
| 1407 | + auto it = numaMap.find(slot.remoteNumaId); | ||
| 1408 | + if (it == numaMap.end() || it->second == 0) { | ||
| 1409 | + slot.migratedBytes = 0; | ||
| 1410 | + } else { | ||
| 1411 | + auto remIt = remainingByNuma.find(slot.remoteNumaId); | ||
| 1412 | + if (remIt == remainingByNuma.end()) { | ||
| 1413 | + remIt = remainingByNuma.emplace(slot.remoteNumaId, it->second * kBytesPerKb).first; | ||
| 1414 | + } | ||
| 1415 | + if (remIt->second >= slot.capacity) { | ||
| 1416 | + slot.migratedBytes = slot.capacity; | ||
| 1417 | + remIt->second -= slot.capacity; | ||
| 1418 | + } else { | ||
| 1419 | + slot.migratedBytes = remIt->second; | ||
| 1420 | + remIt->second = 0; | ||
| 1421 | + } | ||
| 1422 | + } | ||
| 1423 | + ++filled; | ||
| 1424 | + filledBytes += slot.migratedBytes; | ||
| 1425 | + } | ||
| 1426 | + for (const auto& slot : newBorrow.slots) { | ||
| 1427 | + total += slot.migratedBytes; | ||
| 1428 | + } | ||
| 1429 | + newBorrow.currentRemote = total; | ||
| 1430 | + newStatus = def::ProcessStatus::BORROWED; | ||
| 1431 | + }); | ||
| 1432 | + UBSE_LOG_INFO << "[process_mem] recover config: pid=" << pid << " slots=" << slotCount | ||
| 1433 | + << " currentRemote_gb=" << BytesToGbDouble(total); | ||
| 1434 | +} | ||
| 1435 | + | ||
| 1436 | +void ProcessMemPidDecision::ReportDirtySmapStates( | ||
| 1437 | + const std::unordered_map<pid_t, std::unordered_map<int, uint64_t>>& smapMemKb, | ||
| 1438 | + const std::map<pid_t, def::ManagedPidEntry>& snapshot, size_t& dirty, size_t& smapEntries) | ||
| 1439 | +{ | ||
| 1440 | + for (const auto& [pid, numaMap] : smapMemKb) { | ||
| 1441 | + smapEntries += numaMap.size(); | ||
| 1442 | + uint64_t pidTotalKb = 0; | ||
| 1443 | + for (const auto& [numa, memKb] : numaMap) { | ||
| 1444 | + pidTotalKb += memKb; | ||
| 1445 | + } | ||
| 1446 | + auto it = snapshot.find(pid); | ||
| 1447 | + if (it == snapshot.end()) { | ||
| 1448 | + UBSE_LOG_WARN << "[process_mem] recover config: pid=" << pid << " smap mem_kb=" << pidTotalKb | ||
| 1449 | + << " not in managed cache (dirty state)"; | ||
| 1450 | + ++dirty; | ||
| 1451 | + continue; | ||
| 1452 | + } | ||
| 1453 | + for (const auto& [numa, memKb] : numaMap) { | ||
| 1454 | + bool covered = false; | ||
| 1455 | + for (const auto& slot : it->second.borrow.slots) { | ||
| 1456 | + if (slot.remoteNumaId == numa) { | ||
| 1457 | + covered = true; | ||
| 1458 | + break; | ||
| 1459 | + } | ||
| 1460 | + } | ||
| 1461 | + if (!covered) { | ||
| 1462 | + UBSE_LOG_WARN << "[process_mem] recover config: pid=" << pid << " numa=" << numa | ||
| 1463 | + << " smap mem_kb=" << memKb << " but no slot (UBSE ledger lost?)"; | ||
| 1464 | + ++dirty; | ||
| 1465 | + } | ||
| 1466 | + } | ||
| 1467 | + } | ||
| 1468 | +} | ||
| 1469 | + | ||
| 1470 | +uint32_t ProcessMemPidDecision::ReturnOneDebt(const def::ReturnRequestItem& item, uint64_t& nodeFree) | ||
| 1471 | +{ | ||
| 1472 | + auto nodeId = GetCurrentNodeId(); | ||
| 1473 | + | ||
| 1474 | + std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo> debts; | ||
| 1475 | + auto ret = ubse::mem::controller::UbseGetNumaMemDebtInfoWithNode(nodeId, debts); | ||
| 1476 | + if (ret != UBSE_OK) { | ||
| 1477 | + UBSE_LOG_WARN << "[process_mem] return passive debt_id=" << item.name | ||
| 1478 | + << " skip: get own debts failed, ret=" << ret; | ||
| 1479 | + return ret; | ||
| 1480 | + } | ||
| 1481 | + | ||
| 1482 | + const ubse::mem::controller::UbseNumaMemoryDebtInfo* matched = nullptr; | ||
| 1483 | + for (const auto& debt : debts) { | ||
| 1484 | + if (debt.name == item.name) { | ||
| 1485 | + matched = &debt; | ||
| 1486 | + break; | ||
| 1487 | + } | ||
| 1488 | + } | ||
| 1489 | + if (matched == nullptr) { | ||
| 1490 | + UBSE_LOG_WARN << "[process_mem] return passive debt_id=" << item.name << " skip: debt not found locally"; | ||
| 1491 | + return UBSE_ERR_NOT_EXIST; | ||
| 1492 | + } | ||
| 1493 | + | ||
| 1494 | + pid_t pid = 0; | ||
| 1495 | + int srcNuma = -1; | ||
| 1496 | + def::ProcessMemUsrInfo usrInfo{}; | ||
| 1497 | + if (memcpy_s(&usrInfo, sizeof(usrInfo), matched->usrInfo, sizeof(usrInfo)) == EOK && usrInfo.pid > 0) { | ||
| 1498 | + pid = static_cast<pid_t>(usrInfo.pid); | ||
| 1499 | + srcNuma = usrInfo.srcNuma; | ||
| 1500 | + } | ||
| 1501 | + | ||
| 1502 | + if (item.size == 0) { | ||
| 1503 | + return ReturnDebtToLocal(pid, item, nodeFree, ReturnScene::PASSIVE); | ||
| 1504 | + } | ||
| 1505 | + | ||
| 1506 | + if (nodeFree >= freeMemoryThresholdBytes_ + RETURN_HEADROOM_BYTES) { | ||
| 1507 | + return ReturnDebtToLocal(pid, item, nodeFree, ReturnScene::PASSIVE); | ||
| 1508 | + } | ||
| 1509 | + | ||
| 1510 | + return ReturnDebtRemoteToRemote(item, matched->lentNodeId, static_cast<int>(matched->remoteNumaId), pid, srcNuma); | ||
| 1511 | +} | ||
| 1512 | + | ||
| 1513 | +uint32_t ProcessMemPidDecision::ReturnDebtToLocal(pid_t pid, const def::ReturnRequestItem& item, uint64_t& nodeFree, | ||
| 1514 | + ReturnScene scene) | ||
| 1515 | +{ | ||
| 1516 | + if (!pid::bridge::ProcessMemPidBridge::rmrsFreeWithMigrate) { | ||
| 1517 | + UBSE_LOG_WARN << "[process_mem] return " << ReturnSceneToString(scene) << " debt_id=" << item.name | ||
| 1518 | + << " skip: rmrsFreeWithMigrate unavailable"; | ||
| 1519 | + return UBSE_ERROR; | ||
| 1520 | + } | ||
| 1521 | + auto freeRet = pid::bridge::ProcessMemPidBridge::rmrsFreeWithMigrate(item.name); | ||
| 1522 | + if (freeRet != UBSE_OK) { | ||
| 1523 | + UBSE_LOG_WARN << "[process_mem] return " << ReturnSceneToString(scene) << " debt_id=" << item.name | ||
| 1524 | + << " direct_return failed ret=" << freeRet; | ||
| 1525 | + return freeRet; | ||
| 1526 | + } | ||
| 1527 | + | ||
| 1528 | + UBSE_LOG_INFO << "[process_mem] return " << ReturnSceneToString(scene) << " debt_id=" << item.name | ||
| 1529 | + << " amount_gb=" << BytesToGbDouble(item.size) << " direct_return (本地充裕)"; | ||
| 1530 | + nodeFree += item.size; | ||
| 1531 | + if (pid > 0) { | ||
| 1532 | + ProcessMemPidInfoManager::GetInstance().UpdateManagedPidSlotReturned(pid, item.name, item.size); | ||
| 1533 | + } | ||
| 1534 | + return UBSE_OK; | ||
| 1535 | +} | ||
| 1536 | + | ||
| 1537 | +uint32_t ProcessMemPidDecision::ReturnDebtRemoteToRemote(const def::ReturnRequestItem& item, | ||
| 1538 | + const std::string& oldLenderNodeId, int oldRemoteNuma, | ||
| 1539 | + pid_t pid, int srcNuma) | ||
| 1540 | +{ | ||
| 1541 | + if (pid <= 0) { | ||
| 1542 | + UBSE_LOG_INFO << "[process_mem] return passive debt_id=" << item.name << " skip: no pid in usrInfo"; | ||
| 1543 | + return UBSE_ERR_NOT_EXIST; | ||
| 1544 | + } | ||
| 1545 | + if (!pid::bridge::ProcessMemPidBridge::rmrsRemoteToRemote) { | ||
| 1546 | + UBSE_LOG_INFO << "[process_mem] return passive debt_id=" << item.name << " skip: RemoteNumaMigrate unavailable"; | ||
| 1547 | + return UBSE_ERROR; | ||
| 1548 | + } | ||
| 1549 | + if (oldRemoteNuma < 0) { | ||
| 1550 | + UBSE_LOG_INFO << "[process_mem] return passive debt_id=" << item.name | ||
| 1551 | + << " skip: invalid remote numa=" << oldRemoteNuma; | ||
| 1552 | + return UBSE_ERR_NOT_EXIST; | ||
| 1553 | + } | ||
| 1554 | + | ||
| 1555 | + bool migrationDone = false; | ||
| 1556 | + { | ||
| 1557 | + std::lock_guard<std::mutex> lock(pendingOldDebtDeletesMutex_); | ||
| 1558 | + migrationDone = pendingOldDebtDeletes_.count(item.name) > 0; | ||
| 1559 | + } | ||
| 1560 | + | ||
| 1561 | + std::string newDebtId; | ||
| 1562 | + int newRemoteNuma = -1; | ||
| 1563 | + if (!migrationDone) { | ||
| 1564 | + ReplacementDebtCtx ctx; | ||
| 1565 | + ctx.srcNuma = srcNuma; | ||
| 1566 | + ctx.oldRemoteNuma = oldRemoteNuma; | ||
| 1567 | + ctx.oldLenderNodeId = oldLenderNodeId; | ||
| 1568 | + auto migrateRet = MigrateDebtToReplacement(item, pid, ctx, newDebtId, newRemoteNuma); | ||
| 1569 | + if (migrateRet != UBSE_OK) { | ||
| 1570 | + return migrateRet; | ||
| 1571 | + } | ||
| 1572 | + } | ||
| 1573 | + | ||
| 1574 | + auto delRet = DeleteOldReturnDebt(item.name); | ||
| 1575 | + if (delRet != UBSE_OK) { | ||
| 1576 | + UBSE_LOG_WARN << "[process_mem] return passive debt_id=" << item.name | ||
| 1577 | + << " old debt delete failed ret=" << delRet << ", keep slot for retry"; | ||
| 1578 | + return delRet; | ||
| 1579 | + } | ||
| 1580 | + | ||
| 1581 | + if (migrationDone) { | ||
| 1582 | + UBSE_LOG_INFO << "[process_mem] return passive debt_id=" << item.name | ||
| 1583 | + << " remote_to_remote: old debt delete retry succeeded"; | ||
| 1584 | + } else { | ||
| 1585 | + UBSE_LOG_INFO << "[process_mem] return passive debt_id=" << item.name | ||
| 1586 | + << " amount_gb=" << BytesToGbDouble(item.size) | ||
| 1587 | + << " remote_to_remote: old_lender=" << oldLenderNodeId << " new_remote_numa=" << newRemoteNuma; | ||
| 1588 | + } | ||
| 1589 | + return UBSE_OK; | ||
| 1590 | +} | ||
| 1591 | + | ||
| 1592 | +uint32_t ProcessMemPidDecision::MigrateDebtToReplacement(const def::ReturnRequestItem& item, pid_t pid, | ||
| 1593 | + const ReplacementDebtCtx& ctx, std::string& newDebtId, | ||
| 1594 | + int& newRemoteNuma) | ||
| 1595 | +{ | ||
| 1596 | + auto createRet = CreateReplacementDebt(item, pid, ctx, newDebtId, newRemoteNuma); | ||
| 1597 | + if (createRet != UBSE_OK) { | ||
| 1598 | + return createRet; | ||
| 1599 | + } | ||
| 1600 | + | ||
| 1601 | + int migrateRet = MigrateDebtRemoteToRemote(pid, ctx.oldRemoteNuma, newRemoteNuma, item.size); | ||
| 1602 | + if (migrateRet != 0) { | ||
| 1603 | + pid::bridge::ProcessMemPidBridge::MemoryReturn(newDebtId); | ||
| 1604 | + UBSE_LOG_WARN << "[process_mem] return passive debt_id=" << item.name | ||
| 1605 | + << " remote_to_remote failed ret=" << migrateRet << " keep old lender"; | ||
| 1606 | + return UBSE_ERROR; | ||
| 1607 | + } | ||
| 1608 | + | ||
| 1609 | + ProcessMemPidInfoManager::GetInstance().UpdateManagedPidBorrowStateAtomic( | ||
| 1610 | + pid, [&](def::BorrowState& borrow, def::ProcessStatus&) { | ||
| 1611 | + auto slotIt = std::find_if(borrow.slots.begin(), borrow.slots.end(), | ||
| 1612 | + [&item](const def::BorrowSlot& s) { return s.debtId == item.name; }); | ||
| 1613 | + if (slotIt != borrow.slots.end()) { | ||
| 1614 | + slotIt->debtId = newDebtId; | ||
| 1615 | + slotIt->remoteNumaId = newRemoteNuma; | ||
| 1616 | + slotIt->capacity = item.size; | ||
| 1617 | + slotIt->migratedBytes = item.size; | ||
| 1618 | + slotIt->returnStatus = def::ReturnStatus::NONE; | ||
| 1619 | + } | ||
| 1620 | + }); | ||
| 1621 | + return UBSE_OK; | ||
| 1622 | +} | ||
| 1623 | + | ||
| 1624 | +uint32_t ProcessMemPidDecision::DeleteOldReturnDebt(const std::string& debtId) | ||
| 1625 | +{ | ||
| 1626 | + ubse::mem::controller::UbseMemBorrower delBorrower{}; | ||
| 1627 | + delBorrower.nodeId = GetCurrentNodeId(); | ||
| 1628 | + auto delRet = ubse::mem::controller::UbseMemNumaDelete(debtId, delBorrower); | ||
| 1629 | + if (delRet != UBSE_OK && delRet != UBSE_ERR_NOT_EXIST) { | ||
| 1630 | + std::lock_guard<std::mutex> lock(pendingOldDebtDeletesMutex_); | ||
| 1631 | + pendingOldDebtDeletes_.insert(debtId); | ||
| 1632 | + return delRet; | ||
| 1633 | + } | ||
| 1634 | + | ||
| 1635 | + std::lock_guard<std::mutex> lock(pendingOldDebtDeletesMutex_); | ||
| 1636 | + pendingOldDebtDeletes_.erase(debtId); | ||
| 1637 | + return UBSE_OK; | ||
| 1638 | +} | ||
| 1639 | + | ||
| 1640 | +std::vector<std::string> ProcessMemPidDecision::BuildReplacementCandidates(const std::string& oldLenderNodeId) | ||
| 1641 | +{ | ||
| 1642 | + std::vector<std::string> candidates; | ||
| 1643 | + auto allNodes = ubse::nodeController::UbseNodeController::GetInstance().GetAllNodes(); | ||
| 1644 | + for (const auto& nodeEntry : allNodes) { | ||
| 1645 | + if (nodeEntry.first == oldLenderNodeId) { | ||
| 1646 | + continue; | ||
| 1647 | + } | ||
| 1648 | + candidates.push_back(nodeEntry.first); | ||
| 1649 | + } | ||
| 1650 | + if (candidates.empty()) { | ||
| 1651 | + UBSE_LOG_WARN << "[process_mem] return passive: no replacement lender candidate " | ||
| 1652 | + << "(old lender node=" << oldLenderNodeId << ")"; | ||
| 1653 | + } | ||
| 1654 | + return candidates; | ||
| 1655 | +} | ||
| 1656 | + | ||
| 1657 | +uint32_t ProcessMemPidDecision::CreateReplacementDebt(const def::ReturnRequestItem& item, pid_t pid, | ||
| 1658 | + const ReplacementDebtCtx& ctx, std::string& newDebtId, | ||
| 1659 | + int& newRemoteNuma) | ||
| 1660 | +{ | ||
| 1661 | + newDebtId = GenerateBorrowName(pid); | ||
| 1662 | + ubse::mem::controller::UbseMemBorrower borrower{}; | ||
| 1663 | + if (!BuildBorrower(pid, ctx.srcNuma, item.size, 0, borrower)) { | ||
| 1664 | + UBSE_LOG_WARN << "[process_mem] return passive debt_id=" << item.name << " skip: build borrower failed"; | ||
| 1665 | + return UBSE_ERROR; | ||
| 1666 | + } | ||
| 1667 | + | ||
| 1668 | + ubse::mem::controller::UbseMemNumaCandidateOpt opt; | ||
| 1669 | + opt.highWatermark = kBorrowHighWatermark; | ||
| 1670 | + opt.size = item.size; | ||
| 1671 | + // 替换债不能落到旧借出节点:被动归还要的就是把内存从旧借出方腾走,排除其候选 | ||
| 1672 | + opt.slotIds = BuildReplacementCandidates(ctx.oldLenderNodeId); | ||
| 1673 | + if (opt.slotIds.empty()) { | ||
| 1674 | + UBSE_LOG_WARN << "[process_mem] return passive debt_id=" << item.name << " skip: no replacement lender"; | ||
| 1675 | + return UBSE_ERROR; | ||
| 1676 | + } | ||
| 1677 | + def::ProcessMemUsrInfo usrInfo{}; | ||
| 1678 | + usrInfo.pid = static_cast<int32_t>(pid); | ||
| 1679 | + usrInfo.srcNuma = static_cast<int32_t>(ctx.srcNuma); | ||
| 1680 | + if (memcpy_s(opt.usrInfo, ubse::mem::controller::UBSE_MAX_USR_INFO_LEN, &usrInfo, sizeof(usrInfo)) != EOK) { | ||
| 1681 | + UBSE_LOG_ERROR << "[process_mem] return passive debt_id=" << item.name << " skip: memcpy_usrinfo"; | ||
| 1682 | + return UBSE_ERROR; | ||
| 1683 | + } | ||
| 1684 | + | ||
| 1685 | + ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 1686 | + auto createRet = ubse::mem::controller::UbseMemNumaCreateWithCandidate(newDebtId, borrower, opt, desc); | ||
| 1687 | + if (createRet != UBSE_OK) { | ||
| 1688 | + UBSE_LOG_INFO << "[process_mem] return passive debt_id=" << item.name | ||
| 1689 | + << " skip: no available lender (ret=" << createRet << ")"; | ||
| 1690 | + return createRet; | ||
| 1691 | + } | ||
| 1692 | + newRemoteNuma = static_cast<int>(desc.numaId); | ||
| 1693 | + return UBSE_OK; | ||
| 1694 | +} | ||
| 1695 | + | ||
| 1696 | +int ProcessMemPidDecision::MigrateDebtRemoteToRemote(pid_t pid, int oldRemoteNuma, int newRemoteNuma, | ||
| 1697 | + uint64_t sizeBytes) | ||
| 1698 | +{ | ||
| 1699 | + mempooling::smap::MigrateEscapeMsg msg{}; | ||
| 1700 | + msg.count = 1; | ||
| 1701 | + msg.payload[0].pid = pid; | ||
| 1702 | + msg.payload[0].srcNid = oldRemoteNuma; | ||
| 1703 | + msg.payload[0].destNid = newRemoteNuma; | ||
| 1704 | + uint64_t memSizeKb = sizeBytes / 1024; | ||
| 1705 | + constexpr uint64_t pageSizeKb = 4; | ||
| 1706 | + msg.payload[0].memSize = memSizeKb / pageSizeKb * pageSizeKb; | ||
| 1707 | + msg.payload[0].migrateMode = mempooling::smap::MIG_MEMSIZE_MODE; | ||
| 1708 | + return pid::bridge::ProcessMemPidBridge::rmrsRemoteToRemote(msg); | ||
| 1709 | +} | ||
| 1710 | + | ||
| 1711 | +void ProcessMemPidDecision::OomLoop() | ||
| 1712 | +{ | ||
| 1713 | + std::unique_lock<std::mutex> lock(oomMutex_); | ||
| 1714 | + while (oomRunning_) { | ||
| 1715 | + uint32_t intervalMs = OomPollOnce(); | ||
| 1716 | + if (!oomRunning_) { | ||
| 1717 | + break; | ||
| 1718 | + } | ||
| 1719 | + oomCv_.wait_for(lock, std::chrono::milliseconds(intervalMs)); | ||
| 1720 | + } | ||
| 1721 | +} | ||
| 1722 | + | ||
| 1723 | +void ProcessMemPidDecision::OomRearrangeMigrated() | ||
| 1724 | +{ | ||
| 1725 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 1726 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1727 | + for (const auto& [pid, entry] : snapshot) { | ||
| 1728 | + bool hasCompleted = false; | ||
| 1729 | + for (const auto& s : entry.borrow.slots) { | ||
| 1730 | + if (s.status == def::BorrowSlotStatus::COMPLETED && s.returnStatus != def::ReturnStatus::RETURNING) { | ||
| 1731 | + hasCompleted = true; | ||
| 1732 | + break; | ||
| 1733 | + } | ||
| 1734 | + } | ||
| 1735 | + if (!hasCompleted) { | ||
| 1736 | + continue; | ||
| 1737 | + } | ||
| 1738 | + size_t changed = 0; | ||
| 1739 | + size_t emptySlots = 0; | ||
| 1740 | + size_t numaGroups = 0; | ||
| 1741 | + uint64_t spareBytes = 0; | ||
| 1742 | + infoMgr.UpdateManagedPidBorrowStateAtomic(pid, [&](def::BorrowState& borrow, def::ProcessStatus&) { | ||
| 1743 | + std::vector<size_t> before; | ||
| 1744 | + for (const auto& s : borrow.slots) { | ||
| 1745 | + before.push_back(s.migratedBytes); | ||
| 1746 | + } | ||
| 1747 | + RearrangePidMigrated(borrow); | ||
| 1748 | + std::map<int, size_t> groups; | ||
| 1749 | + for (size_t i = 0; i < borrow.slots.size(); ++i) { | ||
| 1750 | + const auto& s = borrow.slots[i]; | ||
| 1751 | + if (s.status != def::BorrowSlotStatus::COMPLETED || s.returnStatus == def::ReturnStatus::RETURNING) { | ||
| 1752 | + continue; | ||
| 1753 | + } | ||
| 1754 | + groups[s.remoteNumaId]++; | ||
| 1755 | + if (s.migratedBytes == 0) { | ||
| 1756 | + ++emptySlots; | ||
| 1757 | + } | ||
| 1758 | + spareBytes += (s.capacity > s.migratedBytes) ? (s.capacity - s.migratedBytes) : 0; | ||
| 1759 | + if (i < before.size() && before[i] != s.migratedBytes) { | ||
| 1760 | + ++changed; | ||
| 1761 | + } | ||
| 1762 | + } | ||
| 1763 | + numaGroups = groups.size(); | ||
| 1764 | + }); | ||
| 1765 | + if (changed > 0 || emptySlots > 0) { | ||
| 1766 | + UBSE_LOG_DEBUG << "[process_mem] oom rearrange: pid=" << pid << " numa_groups=" << numaGroups | ||
| 1767 | + << " changed_slots=" << changed << " empty_slots=" << emptySlots | ||
| 1768 | + << " spare_gb=" << BytesToGbDouble(spareBytes); | ||
| 1769 | + } | ||
| 1770 | + } | ||
| 1771 | +} | ||
| 1772 | + | ||
| 1773 | +uint32_t ProcessMemPidDecision::OomPollOnce() | ||
| 1774 | +{ | ||
| 1775 | + OomRearrangeMigrated(); | ||
| 1776 | + uint64_t roundNum = oomRoundNumber_.fetch_add(1) + 1; | ||
| 1777 | + auto memAvailableOpt = ReadMemAvailableKb(); | ||
| 1778 | + auto nodeFreeOpt = GetNodeFreeBytes(); | ||
| 1779 | + if (!memAvailableOpt.has_value() || !nodeFreeOpt.has_value()) { | ||
| 1780 | + UBSE_LOG_WARN << "[process_mem] oom detector: read memAvailable failed, skip round"; | ||
| 1781 | + return NORMAL_POLL_INTERVAL_MS; | ||
| 1782 | + } | ||
| 1783 | + uint64_t memAvailableBytes = *memAvailableOpt * 1024; | ||
| 1784 | + uint64_t detectThresholdBytes = freeMemoryThresholdBytes_ * 3; | ||
| 1785 | + uint64_t nodeFree = *nodeFreeOpt; | ||
| 1786 | + | ||
| 1787 | + if (memAvailableBytes < detectThresholdBytes) { | ||
| 1788 | + if (!oomFastPoll_) { | ||
| 1789 | + oomFastWindowCount_ = 0; | ||
| 1790 | + oomFastWindowMin_ = 0; | ||
| 1791 | + } | ||
| 1792 | + oomFastPoll_ = true; | ||
| 1793 | + UBSE_LOG_INFO << "[process_mem] oom detector: fast poll enabled (MemAvailable=" | ||
| 1794 | + << BytesToGbDouble(memAvailableBytes) << "GB < detect=" << BytesToGbDouble(detectThresholdBytes) | ||
| 1795 | + << "GB)"; | ||
| 1796 | + | ||
| 1797 | + UpdateOomFastWindow(nodeFree); | ||
| 1798 | + | ||
| 1799 | + if (memAvailableBytes < emergencyThresholdBytes_) { | ||
| 1800 | + OomEmergencyBorrow(roundNum, nodeFree); | ||
| 1801 | + auto now = std::chrono::steady_clock::now(); | ||
| 1802 | + auto sinceLast = | ||
| 1803 | + std::chrono::duration_cast<std::chrono::milliseconds>(now - lastEmergencyBroadcast_).count(); | ||
| 1804 | + if (sinceLast >= kEmergencyBroadcastIntervalMs) { | ||
| 1805 | + BroadcastPassiveReturn(roundNum, nodeFree, true); | ||
| 1806 | + lastEmergencyBroadcast_ = now; | ||
| 1807 | + } | ||
| 1808 | + } | ||
| 1809 | + } else { | ||
| 1810 | + oomFastPoll_ = false; | ||
| 1811 | + oomFastWindowCount_ = 0; | ||
| 1812 | + oomFastWindowMin_ = 0; | ||
| 1813 | + UBSE_LOG_INFO << "[process_mem] oom detector: fast poll disabled (MemAvailable=" | ||
| 1814 | + << BytesToGbDouble(memAvailableBytes) << "GB >= detect=" << BytesToGbDouble(detectThresholdBytes) | ||
| 1815 | + << "GB)"; | ||
| 1816 | + } | ||
| 1817 | + | ||
| 1818 | + UBSE_LOG_DEBUG << "[process_mem] oom detector heartbeat: MemAvailable=" << BytesToGbDouble(memAvailableBytes) | ||
| 1819 | + << "GB detect=" << BytesToGbDouble(detectThresholdBytes) | ||
| 1820 | + << "GB emergency=" << BytesToGbDouble(emergencyThresholdBytes_) | ||
| 1821 | + << "GB poll=" << (oomFastPoll_ ? "fast" : "normal"); | ||
| 1822 | + | ||
| 1823 | + return oomFastPoll_ ? fastPollIntervalMs_ : NORMAL_POLL_INTERVAL_MS; | ||
| 1824 | +} | ||
| 1825 | + | ||
| 1826 | +void ProcessMemPidDecision::UpdateOomFastWindow(uint64_t nodeFree) | ||
| 1827 | +{ | ||
| 1828 | + if (oomFastWindowCount_ == 0) { | ||
| 1829 | + oomFastWindowMin_ = nodeFree; | ||
| 1830 | + } else { | ||
| 1831 | + oomFastWindowMin_ = std::min(oomFastWindowMin_, nodeFree); | ||
| 1832 | + } | ||
| 1833 | + ++oomFastWindowCount_; | ||
| 1834 | + if (oomFastWindowCount_ >= observeCycles_) { | ||
| 1835 | + UBSE_LOG_DEBUG << "[process_mem] oom active window settle: samples=" << oomFastWindowCount_ | ||
| 1836 | + << " min_gb=" << BytesToGbDouble(oomFastWindowMin_) | ||
| 1837 | + << " threshold_gb=" << BytesToGbDouble(freeMemoryThresholdBytes_); | ||
| 1838 | + if (oomFastWindowMin_ > freeMemoryThresholdBytes_) { | ||
| 1839 | + OomActiveReturn(oomFastWindowMin_); | ||
| 1840 | + } | ||
| 1841 | + oomFastWindowCount_ = 0; | ||
| 1842 | + oomFastWindowMin_ = 0; | ||
| 1843 | + } | ||
| 1844 | +} | ||
| 1845 | + | ||
| 1846 | +void ProcessMemPidDecision::OomEmergencyBorrow(uint64_t roundNum, uint64_t nodeFree) | ||
| 1847 | +{ | ||
| 1848 | + if (nodeFree >= freeMemoryThresholdBytes_) { | ||
| 1849 | + return; | ||
| 1850 | + } | ||
| 1851 | + uint64_t shortage = freeMemoryThresholdBytes_ - nodeFree; | ||
| 1852 | + uint64_t pendingBorrow = GetPendingBorrowTotal(); | ||
| 1853 | + shortage = (shortage > pendingBorrow) ? (shortage - pendingBorrow) : 0; | ||
| 1854 | + | ||
| 1855 | + auto candidates = BuildCandidates(roundNum); | ||
| 1856 | + if (candidates.empty()) { | ||
| 1857 | + return; | ||
| 1858 | + } | ||
| 1859 | + ExecuteBorrowRound(candidates, shortage, roundNum, true); | ||
| 1860 | +} | ||
| 1861 | + | ||
| 1862 | +void ProcessMemPidDecision::OomActiveReturn(uint64_t windowMinFree) | ||
| 1863 | +{ | ||
| 1864 | + if (windowMinFree <= freeMemoryThresholdBytes_) { | ||
| 1865 | + return; | ||
| 1866 | + } | ||
| 1867 | + uint64_t budget = windowMinFree - freeMemoryThresholdBytes_; | ||
| 1868 | + | ||
| 1869 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 1870 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1871 | + | ||
| 1872 | + std::vector<std::pair<pid_t, def::ReturnRequestItem>> debts; | ||
| 1873 | + uint64_t totalRemote = 0; | ||
| 1874 | + CollectActiveReturnDebts(snapshot, debts, totalRemote); | ||
| 1875 | + | ||
| 1876 | + std::sort(debts.begin(), debts.end(), [](const auto& a, const auto& b) { return a.second.size > b.second.size; }); | ||
| 1877 | + | ||
| 1878 | + UBSE_LOG_INFO << "[process_mem] return active triggered: window_min_gb=" << BytesToGbDouble(windowMinFree) | ||
| 1879 | + << " budget_gb=" << BytesToGbDouble(budget) << " total_remote_gb=" << BytesToGbDouble(totalRemote); | ||
| 1880 | + | ||
| 1881 | + uint64_t returned = 0; | ||
| 1882 | + for (const auto& [debtPid, item] : debts) { | ||
| 1883 | + if (item.size > budget) { | ||
| 1884 | + UBSE_LOG_DEBUG << "[process_mem] return active debt_id=" << item.name | ||
| 1885 | + << " skip: size_gb=" << BytesToGbDouble(item.size) | ||
| 1886 | + << " > budget_gb=" << BytesToGbDouble(budget); | ||
| 1887 | + continue; | ||
| 1888 | + } | ||
| 1889 | + if (!EnqueueReturnDebt(debtPid, item, ReturnScene::ACTIVE)) { | ||
| 1890 | + UBSE_LOG_WARN << "[process_mem] return active debt_id=" << item.name | ||
| 1891 | + << " enqueue failed (stopping/queue full)"; | ||
| 1892 | + continue; | ||
| 1893 | + } | ||
| 1894 | + budget -= item.size; | ||
| 1895 | + returned += item.size; | ||
| 1896 | + } | ||
| 1897 | + | ||
| 1898 | + UBSE_LOG_INFO << "[process_mem] return active done: returned_gb=" << BytesToGbDouble(returned) | ||
| 1899 | + << " remaining_budget_gb=" << BytesToGbDouble(budget); | ||
| 1900 | +} | ||
| 1901 | + | ||
| 1902 | +void ProcessMemPidDecision::CollectActiveReturnDebts(const std::map<pid_t, def::ManagedPidEntry>& snapshot, | ||
| 1903 | + std::vector<std::pair<pid_t, def::ReturnRequestItem>>& debts, | ||
| 1904 | + uint64_t& totalRemote) | ||
| 1905 | +{ | ||
| 1906 | + for (const auto& [pid, entry] : snapshot) { | ||
| 1907 | + for (const auto& slot : entry.borrow.slots) { | ||
| 1908 | + if (slot.status == def::BorrowSlotStatus::COMPLETED && slot.returnStatus != def::ReturnStatus::RETURNING) { | ||
| 1909 | + debts.emplace_back(pid, def::ReturnRequestItem{slot.debtId, slot.migratedBytes}); | ||
| 1910 | + totalRemote += slot.migratedBytes; | ||
| 1911 | + } | ||
| 1912 | + } | ||
| 1913 | + } | ||
| 1914 | +} | ||
| 1915 | + | ||
| 1916 | +} // namespace process_mem::decision | ||
| @@ -0,0 +1,226 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved. | ||
| 3 | + * ubs-engine is licensed under Mulan PSL v2. | ||
| 4 | + * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 5 | + * You may obtain a copy of Mulan PSL v2 at: | ||
| 6 | + * http://license.coscl.org.cn/MulanPSL2 | ||
| 7 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 8 | + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 9 | + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 10 | + * See the Mulan PSL v2 for more details. | ||
| 11 | + */ | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | +namespace process_mem::decision { | ||
| 33 | + | ||
| 34 | +enum class ReturnScene : uint8_t | ||
| 35 | +{ | ||
| 36 | + PASSIVE, | ||
| 37 | + ACTIVE, | ||
| 38 | + TIMEOUT, | ||
| 39 | + EXITED | ||
| 40 | +}; | ||
| 41 | + | ||
| 42 | +struct CreatedDebtInfo { | ||
| 43 | + int remoteNumaId{-1}; | ||
| 44 | + int32_t exportSlotId{-1}; | ||
| 45 | + uint64_t capacity{0}; | ||
| 46 | +}; | ||
| 47 | + | ||
| 48 | +struct ReplacementDebtCtx { | ||
| 49 | + int srcNuma{-1}; | ||
| 50 | + int oldRemoteNuma{-1}; | ||
| 51 | + std::string oldLenderNodeId{}; | ||
| 52 | +}; | ||
| 53 | + | ||
| 54 | +class ProcessMemPidDecision { | ||
| 55 | +public: | ||
| 56 | + static ProcessMemPidDecision& GetInstance() | ||
| 57 | + { | ||
| 58 | + static ProcessMemPidDecision instance; | ||
| 59 | + return instance; | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + uint32_t Init(); | ||
| 63 | + void UnInit(); | ||
| 64 | + | ||
| 65 | + uint32_t HandleReturnRequest(const std::vector<def::ReturnRequestItem>& items); | ||
| 66 | + | ||
| 67 | + bool EnqueuePidReturn(pid_t pid, ReturnScene scene); | ||
| 68 | + | ||
| 69 | + uint32_t HandlePidExited(pid_t pid); | ||
| 70 | + | ||
| 71 | + uint32_t OomPollOnce(); | ||
| 72 | + | ||
| 73 | + inline static std::function<std::optional<uint64_t>()> memAvailableReader; | ||
| 74 | + | ||
| 75 | +private: | ||
| 76 | + uint32_t OnDecisionTimer(); | ||
| 77 | + | ||
| 78 | + bool CheckNodeFreeMemory(uint64_t& outShortage); | ||
| 79 | + | ||
| 80 | + std::vector<def::BorrowCandidate> BuildCandidates(uint64_t roundNum); | ||
| 81 | + | ||
| 82 | + int CollectSrcNuma(pid_t pid, uint64_t roundNum); | ||
| 83 | + | ||
| 84 | + void ExecuteBorrowRound(const std::vector<def::BorrowCandidate>& candidates, uint64_t& shortage, uint64_t roundNum, | ||
| 85 | + bool emergency = false); | ||
| 86 | + | ||
| 87 | + void CheckTimeouts(uint64_t roundNum); | ||
| 88 | + | ||
| 89 | + uint64_t GetPendingBorrowTotal() const; | ||
| 90 | + | ||
| 91 | + std::string RecordPendingBorrow(pid_t pid, uint64_t amount, int srcNumaId, uint64_t roundNum); | ||
| 92 | + | ||
| 93 | + void AsyncBorrowAndMigrate(const std::string& debtId, pid_t pid, uint64_t amount, int srcNumaId, uint64_t roundNum); | ||
| 94 | + | ||
| 95 | + void BuildMigrateTargets(const def::BorrowState& borrow, const std::map<int, uint64_t>& increments, | ||
| 96 | + std::vector<std::pair<int, uint64_t>>& numaTargets); | ||
| 97 | + | ||
| 98 | + def::AtomicMigrateResult CommitBorrowAndMigrate(pid_t pid, const std::string& debtId, | ||
| 99 | + const CreatedDebtInfo& created, | ||
| 100 | + const std::map<int, uint64_t>& increments, | ||
| 101 | + std::vector<std::pair<int, uint64_t>>& numaTargets); | ||
| 102 | + | ||
| 103 | + void FailBorrowAbort(pid_t pid, const std::string& debtId, uint64_t need, uint64_t roundNum); | ||
| 104 | + | ||
| 105 | + std::string GetCurrentNodeId(); | ||
| 106 | + | ||
| 107 | + std::optional<uint64_t> GetNodeFreeBytes(); | ||
| 108 | + | ||
| 109 | + std::optional<uint64_t> ReadMemAvailableKb(); | ||
| 110 | + | ||
| 111 | + bool EnqueueReturnDebt(pid_t pid, const def::ReturnRequestItem& item, ReturnScene scene); | ||
| 112 | + | ||
| 113 | + void RunReturnDebt(pid_t pid, def::ReturnRequestItem item, ReturnScene scene); | ||
| 114 | + | ||
| 115 | + void RunPidReturn(pid_t pid, ReturnScene scene); | ||
| 116 | + | ||
| 117 | + uint32_t DoPidReturnOnce(pid_t pid, ReturnScene scene); | ||
| 118 | + | ||
| 119 | + pid_t ResolveDebtPid(const std::string& name); | ||
| 120 | + | ||
| 121 | + std::mutex& GetPidMutex(pid_t pid); | ||
| 122 | + | ||
| 123 | + void BroadcastPassiveReturn(uint64_t roundNum, uint64_t nodeFree, bool emergency); | ||
| 124 | + | ||
| 125 | + uint32_t ReturnOneDebt(const def::ReturnRequestItem& item, uint64_t& nodeFree); | ||
| 126 | + | ||
| 127 | + uint32_t ReturnDebtToLocal(pid_t pid, const def::ReturnRequestItem& item, uint64_t& nodeFree, ReturnScene scene); | ||
| 128 | + | ||
| 129 | + uint32_t ReturnDebtRemoteToRemote(const def::ReturnRequestItem& item, const std::string& oldLenderNodeId, | ||
| 130 | + int oldRemoteNuma, pid_t pid, int srcNuma); | ||
| 131 | + | ||
| 132 | + void OomEmergencyBorrow(uint64_t roundNum, uint64_t nodeFree); | ||
| 133 | + | ||
| 134 | + void OomActiveReturn(uint64_t windowMinFree); | ||
| 135 | + | ||
| 136 | + void OomRearrangeMigrated(); | ||
| 137 | + | ||
| 138 | + void OomLoop(); | ||
| 139 | + | ||
| 140 | + void RecoverBorrowFromObmm(); | ||
| 141 | + | ||
| 142 | + bool EnqueueOrphanReturn(const std::string& debtName); | ||
| 143 | + | ||
| 144 | + void RunOrphanReturn(const std::string& debtName); | ||
| 145 | + | ||
| 146 | + void RetryReturnEnqueue(uint32_t& retryCount, const std::function<bool()>& enqueue); | ||
| 147 | + | ||
| 148 | + uint32_t RecoverSmapProcessConfig(); | ||
| 149 | + | ||
| 150 | + void LoadConfig(); | ||
| 151 | + void LoadOomReturnConfig(); | ||
| 152 | + bool StartExecutors(); | ||
| 153 | + void RunBorrowRound(uint64_t roundNum); | ||
| 154 | + void LogBorrowCandidates(uint64_t roundNum, const std::vector<def::BorrowCandidate>& candidates); | ||
| 155 | + | ||
| 156 | + void RemoveSlotFinalize(pid_t pid, const std::string& debtId); | ||
| 157 | + uint64_t FlushBorrowIncrements(def::BorrowState& borrow, const std::vector<std::pair<int, uint64_t>>& numaTargets, | ||
| 158 | + const std::map<int, uint64_t>& increments, const std::string& ownDebtId, | ||
| 159 | + const CreatedDebtInfo& created); | ||
| 160 | + bool BuildBorrower(pid_t pid, int srcNumaId, uint64_t need, uint64_t roundNum, | ||
| 161 | + ubse::mem::controller::UbseMemBorrower& borrower); | ||
| 162 | + bool CreateNumaDebt(pid_t pid, uint64_t need, int srcNumaId, const std::string& debtId, uint64_t roundNum, | ||
| 163 | + CreatedDebtInfo& out); | ||
| 164 | + int RmrsMigrateToNumas(pid_t pid, const std::vector<std::pair<int, uint64_t>>& numaTargets); | ||
| 165 | + | ||
| 166 | + bool CheckPidTimeoutSlots(pid_t pid, def::BorrowState& borrow, uint64_t roundNum); | ||
| 167 | + uint32_t DoReturnDebtOnce(pid_t pid, const def::ReturnRequestItem& item, ReturnScene scene); | ||
| 168 | + bool ReturnOnePidDebt(const ubse::mem::controller::UbseNumaMemoryDebtInfo& debt, bool procExited, | ||
| 169 | + uint32_t& freedCount, uint64_t& freedBytes, uint32_t& failCount); | ||
| 170 | + | ||
| 171 | + void RecoverBindDebt(pid_t pid, const ubse::mem::controller::UbseNumaMemoryImportDebtInfo& debt); | ||
| 172 | + void RecoverPidMigratedBytes(pid_t pid, | ||
| 173 | + const std::unordered_map<pid_t, std::unordered_map<int, uint64_t>>& smapMemKb, | ||
| 174 | + const std::set<int>& failedNumas, size_t& filled, uint64_t& filledBytes); | ||
| 175 | + void ReportDirtySmapStates(const std::unordered_map<pid_t, std::unordered_map<int, uint64_t>>& smapMemKb, | ||
| 176 | + const std::map<pid_t, def::ManagedPidEntry>& snapshot, size_t& dirty, | ||
| 177 | + size_t& smapEntries); | ||
| 178 | + | ||
| 179 | + uint32_t CreateReplacementDebt(const def::ReturnRequestItem& item, pid_t pid, const ReplacementDebtCtx& ctx, | ||
| 180 | + std::string& newDebtId, int& newRemoteNuma); | ||
| 181 | + int MigrateDebtRemoteToRemote(pid_t pid, int oldRemoteNuma, int newRemoteNuma, uint64_t sizeBytes); | ||
| 182 | + uint32_t MigrateDebtToReplacement(const def::ReturnRequestItem& item, pid_t pid, const ReplacementDebtCtx& ctx, | ||
| 183 | + std::string& newDebtId, int& newRemoteNuma); | ||
| 184 | + uint32_t DeleteOldReturnDebt(const std::string& debtId); | ||
| 185 | + std::vector<std::string> BuildReplacementCandidates(const std::string& oldLenderNodeId); | ||
| 186 | + | ||
| 187 | + void UpdateOomFastWindow(uint64_t nodeFree); | ||
| 188 | + void CollectActiveReturnDebts(const std::map<pid_t, def::ManagedPidEntry>& snapshot, | ||
| 189 | + std::vector<std::pair<pid_t, def::ReturnRequestItem>>& debts, uint64_t& totalRemote); | ||
| 190 | + | ||
| 191 | + uint32_t processIntervalSec_{5}; | ||
| 192 | + uint64_t freeMemoryThresholdBytes_{0}; | ||
| 193 | + uint32_t timeoutPer128MbMs_{1000}; | ||
| 194 | + bool samePlanePrefer_{false}; | ||
| 195 | + uint64_t fastPollIntervalMs_{200}; | ||
| 196 | + uint64_t emergencyThresholdBytes_{0}; | ||
| 197 | + uint32_t observeCycles_{6}; | ||
| 198 | + uint64_t returnRetryIntervalMs_{1000}; | ||
| 199 | + | ||
| 200 | + ubse::task_executor::UbseTaskExecutorPtr borrowExecutor_{}; | ||
| 201 | + ubse::task_executor::UbseTaskExecutorPtr returnExecutor_{}; | ||
| 202 | + | ||
| 203 | + static constexpr uint32_t kPidMutexNum = 64; | ||
| 204 | + std::array<std::mutex, kPidMutexNum> pidMutexes_{}; | ||
| 205 | + | ||
| 206 | + // remote-to-remote 归还中旧 debt 删除失败的 debt 名;重试时跳过 create+migrate 只重删旧 debt | ||
| 207 | + std::set<std::string> pendingOldDebtDeletes_{}; | ||
| 208 | + std::mutex pendingOldDebtDeletesMutex_{}; | ||
| 209 | + | ||
| 210 | + std::atomic<uint64_t> roundNumber_{0}; | ||
| 211 | + std::atomic<bool> cycleRunning_{false}; | ||
| 212 | + std::atomic<bool> stopping_{false}; | ||
| 213 | + | ||
| 214 | + std::thread oomThread_{}; | ||
| 215 | + std::mutex oomMutex_{}; | ||
| 216 | + std::condition_variable oomCv_{}; | ||
| 217 | + bool oomRunning_{false}; | ||
| 218 | + bool oomFastPoll_{false}; | ||
| 219 | + uint32_t oomFastWindowCount_{0}; | ||
| 220 | + uint64_t oomFastWindowMin_{0}; | ||
| 221 | + std::chrono::steady_clock::time_point lastEmergencyBroadcast_{}; | ||
| 222 | + std::atomic<uint64_t> oomRoundNumber_{0}; | ||
| 223 | +}; | ||
| 224 | + | ||
| 225 | +} // namespace process_mem::decision | ||
| 226 | + | ||
| @@ -11,1164 +11,961 @@ | |||
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | -#include <mutex> | 14 | +#include <algorithm> |
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 15 | 19 | ||
| 16 | 20 | ||
| 17 | 21 | ||
| 18 | 22 | ||
| 19 | - | ||
| 20 | - | ||
| 21 | 23 | ||
| 22 | 24 | ||
| 25 | + | ||
| 23 | 26 | ||
| 24 | - | ||
| 25 | 27 | ||
| 26 | 28 | ||
| 27 | namespace process_mem::manager { | 29 | namespace process_mem::manager { |
| 28 | UBSE_DEFINE_THIS_MODULE("process_mem"); | 30 | UBSE_DEFINE_THIS_MODULE("process_mem"); |
| 29 | -static uint32_t g_borrowTimeOut = 30; | ||
| 30 | - | ||
| 31 | -uint32_t ProcessMemPidInfoManager::UpdatePidMemBorrowInfo(pid_t pid, const def::DebtInfo& debtInfo) | ||
| 32 | -{ | ||
| 33 | - std::unique_lock<std::shared_mutex> lock(pidInfoMutex); | ||
| 34 | - auto pidIt = pidInfoMap.find(pid); | ||
| 35 | - if (pidIt == pidInfoMap.end()) { | ||
| 36 | - return UBSE_ERROR; | ||
| 37 | - } | ||
| 38 | - pidIt->second.memBorrowInfo.debtInfos[debtInfo.numaDesc.name] = debtInfo; | ||
| 39 | - return UBSE_OK; | ||
| 40 | -} | ||
| 41 | - | ||
| 42 | -uint32_t ProcessMemPidInfoManager::UpdatePidMemBorrowInfo(pid_t pid, | ||
| 43 | - const ubse::mem::controller::UbseMemBorrower& borrower, | ||
| 44 | - const def::DebtInfo& debtInfo) | ||
| 45 | -{ | ||
| 46 | - std::unique_lock<std::shared_mutex> lock(pidInfoMutex); | ||
| 47 | - auto pidIt = pidInfoMap.find(pid); | ||
| 48 | - if (pidIt == pidInfoMap.end()) { | ||
| 49 | - return UBSE_ERROR; | ||
| 50 | - } | ||
| 51 | - pidIt->second.memBorrowInfo.importSocketId = borrower.affinitySocketId; | ||
| 52 | - pidIt->second.memBorrowInfo.remoteNumaId = debtInfo.numaDesc.numaId; | ||
| 53 | - pidIt->second.memBorrowInfo.exportSlotId = debtInfo.numaDesc.exportNode.slotId; | ||
| 54 | - pidIt->second.memBorrowInfo.debtInfos[debtInfo.numaDesc.name] = debtInfo; | ||
| 55 | - return UBSE_OK; | ||
| 56 | -} | ||
| 57 | - | ||
| 58 | -uint32_t MigrateOut(const def::ProcessMemPidInfo& pidInfo, uint64_t targetRemoteMemory); | ||
| 59 | -bool GetMigrateResult(pid_t pid, int64_t remoteNumaId, uint64_t expectSize, bool isBack); | ||
| 60 | - | ||
| 61 | -void ProcessMemPidInfoManager::DeletePidMemBorrowInfo(pid_t pid, const std::string& borrowName) | ||
| 62 | -{ | ||
| 63 | - int remoteNumaId = -1; | ||
| 64 | - { | ||
| 65 | - std::unique_lock<std::shared_mutex> lock(pidInfoMutex); | ||
| 66 | - auto pidIt = pidInfoMap.find(pid); | ||
| 67 | - if (pidIt == pidInfoMap.end()) { | ||
| 68 | - return; | ||
| 69 | - } | ||
| 70 | - pidIt->second.memBorrowInfo.debtInfos.erase(borrowName); | ||
| 71 | - if (!pidIt->second.memBorrowInfo.debtInfos.empty()) { | ||
| 72 | - return; | ||
| 73 | - } | ||
| 74 | - remoteNumaId = pidIt->second.memBorrowInfo.remoteNumaId; | ||
| 75 | - pidIt->second.memBorrowInfo = {}; | ||
| 76 | - } | ||
| 77 | - if (remoteNumaId == -1) { | ||
| 78 | - return; | ||
| 79 | - } | ||
| 80 | - def::ProcessMemPidInfo tmpInfo{}; | ||
| 81 | - tmpInfo.configInfo.pid = pid; | ||
| 82 | - tmpInfo.memBorrowInfo.remoteNumaId = remoteNumaId; | ||
| 83 | - MigrateOut(tmpInfo, 0); | ||
| 84 | - bool res = GetMigrateResult(pid, static_cast<int64_t>(remoteNumaId), 0, true); | ||
| 85 | - if (!res) { | ||
| 86 | - UBSE_LOG_ERROR << "MigrateOut failed, pid=" << pid; | ||
| 87 | - } | ||
| 88 | - std::vector<pid_t> pids = {pid}; | ||
| 89 | - UBSE_LOG_INFO << "rmrsRemove called, pid=" << pid; | ||
| 90 | - pid::bridge::ProcessMemPidBridge::rmrsRemove(def::INVALID_REMOTE_NUMA, pids, 0); | ||
| 91 | -} | ||
| 92 | - | ||
| 93 | -void ProcessMemPidInfoManager::ResetPidMemBorrowInfo(pid_t pid) | ||
| 94 | -{ | ||
| 95 | - std::unique_lock<std::shared_mutex> lock(pidInfoMutex); | ||
| 96 | - auto pidIt = pidInfoMap.find(pid); | ||
| 97 | - if (pidIt == pidInfoMap.end()) { | ||
| 98 | - return; | ||
| 99 | - } | ||
| 100 | - pidIt->second.memBorrowInfo = {}; | ||
| 101 | -} | ||
| 102 | - | ||
| 103 | -uint32_t ProcessMemPidInfoManager::SetPidInfoMap(const def::ProcessMemPidInfo& pidInfo) | ||
| 104 | -{ | ||
| 105 | - std::unique_lock<std::shared_mutex> lock(pidInfoMutex); | ||
| 106 | - if (pidInfo.startTime == 0) { | ||
| 107 | - UBSE_LOG_ERROR << "Failed to get exact start time for pid: " << pidInfo.configInfo.pid; | ||
| 108 | - return UBSE_ERR_NOT_EXIST; | ||
| 109 | - } | ||
| 110 | - | ||
| 111 | - auto pidIt = pidInfoMap.find(pidInfo.configInfo.pid); | ||
| 112 | - bool existsInPidMap = (pidIt != pidInfoMap.end()); | ||
| 113 | - auto srcNumaStr = pidInfo.configInfo.srcNumaId.has_value() ? std::to_string(pidInfo.configInfo.srcNumaId.value()) : | ||
| 114 | - "N/A"; | ||
| 115 | - UBSE_LOG_INFO << "srcNuma Id is " << srcNumaStr; | ||
| 116 | - if (!existsInPidMap) { | ||
| 117 | - ProcessMemPidConfigManager::PersistPidConfigInfo(pidInfo); | ||
| 118 | - pidInfoMap.insert_or_assign(pidInfo.configInfo.pid, pidInfo); | ||
| 119 | - return UBSE_OK; | ||
| 120 | - } | ||
| 121 | - | ||
| 122 | - if (pidInfo.startTime != pidIt->second.startTime) { | ||
| 123 | - UBSE_LOG_ERROR << "Start time mismatch for pid: " << pidInfo.configInfo.pid | ||
| 124 | - << ", provided: " << pidInfo.startTime << ", actual: " << pidIt->second.startTime; | ||
| 125 | - return UBSE_ERR_INVALID_ARG; | ||
| 126 | - } | ||
| 127 | - if (!pidIt->second.memBorrowInfo.debtInfos.empty() && | ||
| 128 | - pidInfo.configInfo.srcNumaId != pidIt->second.configInfo.srcNumaId) { | ||
| 129 | - UBSE_LOG_ERROR << "Cannot modify srcNumaId while pid=" << pidInfo.configInfo.pid | ||
| 130 | - << " has existing borrow debts"; | ||
| 131 | - return UBSE_ERR_RESOURCE_BUSY; | ||
| 132 | - } | ||
| 133 | - pidIt->second.configInfo = pidInfo.configInfo; | ||
| 134 | - ProcessMemPidConfigManager::PersistPidConfigInfo(pidIt->second); | ||
| 135 | - return UBSE_OK; | ||
| 136 | -} | ||
| 137 | - | ||
| 138 | -void PrintConfigInfo(const std::vector<def::ProcessMemPidInfo>& pidInfos) | ||
| 139 | -{ | ||
| 140 | - for (const auto& pidInfo : pidInfos) { | ||
| 141 | - auto srcNumaStr = | ||
| 142 | - pidInfo.configInfo.srcNumaId.has_value() ? std::to_string(pidInfo.configInfo.srcNumaId.value()) : "N/A"; | ||
| 143 | - UBSE_LOG_INFO << "Pid: " << pidInfo.configInfo.pid << ", NumaId: " << srcNumaStr; | ||
| 144 | - } | ||
| 145 | -} | ||
| 146 | 31 | ||
| 147 | void ProcessMemPidInfoManager::Init() | 32 | void ProcessMemPidInfoManager::Init() |
| 148 | { | 33 | { |
| 149 | - std::vector<def::ProcessMemPidInfo> pidInfos{}; | 34 | + RefreshProcMemConfigCache(); |
| 150 | - const std::string section = "process_mem.pid"; | ||
| 151 | - const std::string configKey = "borrow.timeout"; | ||
| 152 | - ubse::config::UbseGetUInt(section, configKey, g_borrowTimeOut); | ||
| 153 | - constexpr uint32_t borrowDefaultTimeOut = 30; | ||
| 154 | - if (g_borrowTimeOut == 0) { | ||
| 155 | - g_borrowTimeOut = borrowDefaultTimeOut; | ||
| 156 | - } | ||
| 157 | 35 | ||
| 158 | - const int borrowThreadNum = 10; | 36 | + collect::VmRssCollectHandler vmRssHandler = [](const collect::PidCollectInfoMap& collectInfo) { |
| 159 | - const int returnThreadNum = 10; | 37 | + ProcessMemPidInfoManager::GetInstance().VmRssCheckCallBack(collectInfo); |
| 160 | - const int exceptionThreadNum = 4; | ||
| 161 | - const int queSize = 1024; | ||
| 162 | - borrowExecutor = ubse::task_executor::UbseTaskExecutor::Create("PidBorrow", borrowThreadNum, queSize); | ||
| 163 | - if (borrowExecutor == nullptr || !borrowExecutor->Start()) { | ||
| 164 | - UBSE_LOG_ERROR << "borrowExecutor start failed"; | ||
| 165 | - } | ||
| 166 | - returnExecutor = ubse::task_executor::UbseTaskExecutor::Create("PidReturn", returnThreadNum, queSize); | ||
| 167 | - if (returnExecutor == nullptr || !returnExecutor->Start()) { | ||
| 168 | - UBSE_LOG_ERROR << "returnExecutor start failed"; | ||
| 169 | - } | ||
| 170 | - exceptionHandleExecutor = | ||
| 171 | - ubse::task_executor::UbseTaskExecutor::Create("ExceptionHandle", exceptionThreadNum, queSize); | ||
| 172 | - if (exceptionHandleExecutor == nullptr || !exceptionHandleExecutor->Start()) { | ||
| 173 | - UBSE_LOG_ERROR << "exceptionHandleExecutor start failed"; | ||
| 174 | - } | ||
| 175 | - | ||
| 176 | - ProcessMemPidConfigManager::GetAllPersistedPidConfigInfo(pidInfos); | ||
| 177 | - PrintConfigInfo(pidInfos); | ||
| 178 | - for (auto& pidInfo : pidInfos) { | ||
| 179 | - if (!ProcessMemPidConfigManager::CheckPidConfigInfo(pidInfo)) { | ||
| 180 | - ProcessMemPidConfigManager::DeletePidConfigInfo(pidInfo.configInfo.pid); | ||
| 181 | - continue; | ||
| 182 | - } | ||
| 183 | - SetPidInfoMap(pidInfo); | ||
| 184 | - } | ||
| 185 | - | ||
| 186 | - collect::NumaMemDistributionCollectHandler handler = [](const collect::CollectInfoMap& collectInfo) { | ||
| 187 | - ProcessMemPidInfoManager::GetInstance().TotalMemoryCheckCallBack(collectInfo); | ||
| 188 | }; | 38 | }; |
| 189 | - process_mem::collect::ProcessMemPidCollect::GetInstance().RegisterCollectHandler("pidCollectCallback", handler); | 39 | + process_mem::collect::ProcessMemPidCollect::GetInstance().RegisterVmRssCollectHandler("pidVmRssCallback", |
| 40 | + vmRssHandler); | ||
| 41 | + | ||
| 190 | process_mem::collect::ProcessMemPidCollect::GetInstance().Init(); | 42 | process_mem::collect::ProcessMemPidCollect::GetInstance().Init(); |
| 191 | 43 | ||
| 192 | - UBSE_LOG_INFO << "borrowExecutor and returnExecutor start success"; | 44 | + RebuildManagedPidCache(); |
| 45 | + | ||
| 46 | + UBSE_LOG_INFO << "ProcessMemPidInfoManager init done"; | ||
| 193 | } | 47 | } |
| 194 | 48 | ||
| 195 | void ProcessMemPidInfoManager::UnInit() | 49 | void ProcessMemPidInfoManager::UnInit() |
| 196 | { | 50 | { |
| 197 | - // 停止采集和定时检查,不再投递新任务 | 51 | + process_mem::collect::ProcessMemPidCollect::GetInstance().UnRegisterVmRssCollectHandler("pidVmRssCallback"); |
| 198 | - process_mem::collect::ProcessMemPidCollect::GetInstance().UnRegisterCollectHandler("pidCollectCallback"); | ||
| 199 | process_mem::collect::ProcessMemPidCollect::GetInstance().UnInit(); | 52 | process_mem::collect::ProcessMemPidCollect::GetInstance().UnInit(); |
| 200 | - ubse::timer::UbseTimerHandlerUnregister("PidMemoryCheck"); | ||
| 201 | - | ||
| 202 | - // 排空队列后停止,避免积压的借用/归还任务被丢弃 | ||
| 203 | - if (borrowExecutor != nullptr) { | ||
| 204 | - borrowExecutor->Wait(); | ||
| 205 | - borrowExecutor->Stop(); | ||
| 206 | - } | ||
| 207 | - if (returnExecutor != nullptr) { | ||
| 208 | - returnExecutor->Wait(); | ||
| 209 | - returnExecutor->Stop(); | ||
| 210 | - } | ||
| 211 | - if (exceptionHandleExecutor != nullptr) { | ||
| 212 | - exceptionHandleExecutor->Stop(); | ||
| 213 | - } | ||
| 214 | } | 53 | } |
| 215 | 54 | ||
| 216 | -uint32_t GetPidNumaSize(pid_t pid, int64_t numaId, uint64_t& numaSize) | 55 | +int DoMigrateOut(pid_t pid, const std::map<int, uint64_t>& numaTargets) |
| 217 | { | 56 | { |
| 218 | - uint64_t tmpNumaSize = 0; | ||
| 219 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 220 | - auto& collector = process_mem::collect::ProcessMemPidCollect::GetInstance(); | ||
| 221 | - auto ret = collector.CollectProcessNumaMemDistribution(pid, numaMemDistribution); | ||
| 222 | - if (ret != UBSE_OK) { | ||
| 223 | - UBSE_LOG_ERROR << "GetPidNumaSize failed, pid is " << pid << ", numaSize is " << numaSize; | ||
| 224 | - return UBSE_ERROR; | ||
| 225 | - } | ||
| 226 | - for (const auto& [key, numaSize] : numaMemDistribution) { | ||
| 227 | - if (key == numaId) { | ||
| 228 | - tmpNumaSize += numaSize; | ||
| 229 | - } | ||
| 230 | - } | ||
| 231 | - numaSize = tmpNumaSize; | ||
| 232 | - return UBSE_OK; | ||
| 233 | -} | ||
| 234 | - | ||
| 235 | -def::ProcessMemPidInfo ProcessMemPidInfoManager::GetPidInfoMap(pid_t pid) | ||
| 236 | -{ | ||
| 237 | - std::shared_lock<std::shared_mutex> lock(pidInfoMutex); | ||
| 238 | - auto pidIt = pidInfoMap.find(pid); | ||
| 239 | - if (pidIt == pidInfoMap.end()) { | ||
| 240 | - def::ProcessMemPidInfo pidInfo; | ||
| 241 | - pidInfo.configInfo.pid = -1; | ||
| 242 | - return pidInfo; | ||
| 243 | - } | ||
| 244 | - return pidIt->second; | ||
| 245 | -} | ||
| 246 | - | ||
| 247 | -void ProcessMemPidInfoManager::GetAllPidInfo(std::vector<def::ProcessMemPidInfo>& pidInfos) | ||
| 248 | -{ | ||
| 249 | - std::shared_lock<std::shared_mutex> lock(pidInfoMutex); | ||
| 250 | - for (auto [key, value] : pidInfoMap) { | ||
| 251 | - pidInfos.push_back(value); | ||
| 252 | - } | ||
| 253 | - return; | ||
| 254 | -} | ||
| 255 | - | ||
| 256 | -bool GetMigrateResult(pid_t pid, int64_t remoteNumaId, uint64_t expectSize, bool isBack = false) | ||
| 257 | -{ | ||
| 258 | - const int retryTime = 10; | ||
| 259 | - const int sleepSec = 2; | ||
| 260 | - int nowTime = 0; | ||
| 261 | - uint64_t numaSize = 0; | ||
| 262 | - uint32_t ret = 0; | ||
| 263 | - int def = 10 * (1 << 20); | ||
| 264 | - if (isBack) { | ||
| 265 | - def = 0; | ||
| 266 | - } | ||
| 267 | - auto checkFunc = [def](uint64_t numaSize, uint64_t expectSize) { | ||
| 268 | - if (numaSize > expectSize) { | ||
| 269 | - return (numaSize - expectSize) <= def; | ||
| 270 | - } | ||
| 271 | - return (expectSize - numaSize) <= def; | ||
| 272 | - }; | ||
| 273 | - do { | ||
| 274 | - ret |= GetPidNumaSize(pid, remoteNumaId, numaSize); | ||
| 275 | - ++nowTime; | ||
| 276 | - if (nowTime < retryTime) { | ||
| 277 | - sleep(sleepSec); | ||
| 278 | - } | ||
| 279 | - } while (nowTime < retryTime && !checkFunc(numaSize, expectSize)); | ||
| 280 | - | ||
| 281 | - return checkFunc(numaSize, expectSize) && ret == 0; | ||
| 282 | -} | ||
| 283 | - | ||
| 284 | -std::string GenerateSimpleName(pid_t pid) | ||
| 285 | -{ | ||
| 286 | - auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()) | ||
| 287 | - .count(); | ||
| 288 | - constexpr size_t maxNameLen = 48; | ||
| 289 | - constexpr size_t truncateLen = maxNameLen - 1; | ||
| 290 | - auto nodeId = ubse::nodeController::UbseNodeController::GetInstance().GetCurrentNodeId(); | ||
| 291 | - std::string name = nodeId + "-" + std::to_string(pid) + "-" + std::to_string(ms); | ||
| 292 | - if (name.size() >= maxNameLen) { | ||
| 293 | - name.resize(truncateLen); | ||
| 294 | - } | ||
| 295 | - return name; | ||
| 296 | -} | ||
| 297 | - | ||
| 298 | -bool CheckIsNeedBorrow(const def::ProcessMemPidInfo& pidInfo, uint64_t expectRemoteMemory, uint64_t& needBorrowSize) | ||
| 299 | -{ | ||
| 300 | - uint64_t totalSecured = 0; | ||
| 301 | - std::vector<std::string> timeoutDebts; | ||
| 302 | - for (const auto& [name, debtInfo] : pidInfo.memBorrowInfo.debtInfos) { | ||
| 303 | - auto now = std::chrono::steady_clock::now(); | ||
| 304 | - if (debtInfo.status == def::BorrowStatus::CREATING && | ||
| 305 | - (now - debtInfo.borrowStartTime) > std::chrono::seconds(g_borrowTimeOut)) { | ||
| 306 | - timeoutDebts.push_back(name); | ||
| 307 | - continue; | ||
| 308 | - } | ||
| 309 | - totalSecured += debtInfo.numaDesc.size; | ||
| 310 | - } | ||
| 311 | - for (const auto& name : timeoutDebts) { | ||
| 312 | - ProcessMemPidInfoManager::GetInstance().DeletePidMemBorrowInfo(pidInfo.configInfo.pid, name); | ||
| 313 | - } | ||
| 314 | - UBSE_LOG_INFO << "expectRemoteMemory is " << expectRemoteMemory << ", totalSecured is " << totalSecured; | ||
| 315 | - if (expectRemoteMemory <= totalSecured) { | ||
| 316 | - return false; | ||
| 317 | - } | ||
| 318 | - needBorrowSize = expectRemoteMemory - totalSecured; | ||
| 319 | - return true; | ||
| 320 | -} | ||
| 321 | - | ||
| 322 | -uint32_t GetUbseMemBorrower(const def::ProcessMemPidInfo& pidInfo, ubse::mem::controller::UbseMemBorrower& borrower) | ||
| 323 | -{ | ||
| 324 | - borrower.nodeId = ubse::nodeController::UbseNodeController::GetInstance().GetCurrentNodeId(); | ||
| 325 | - if (borrower.nodeId.empty()) { | ||
| 326 | - UBSE_LOG_ERROR << "Get borrowNodeId failed"; | ||
| 327 | - return UBSE_ERROR; | ||
| 328 | - } | ||
| 329 | - | ||
| 330 | - borrower.affinitySocketId = pidInfo.memBorrowInfo.importSocketId; | ||
| 331 | - if (borrower.affinitySocketId != -1) { | ||
| 332 | - return UBSE_OK; | ||
| 333 | - } | ||
| 334 | - | ||
| 335 | - uint64_t targetNumaId = 0; | ||
| 336 | - if (pidInfo.configInfo.srcNumaId.has_value()) { | ||
| 337 | - targetNumaId = pidInfo.configInfo.srcNumaId.value(); | ||
| 338 | - } else { | ||
| 339 | - std::unordered_map<uint32_t, size_t> numaDistribution{}; | ||
| 340 | - auto ret = process_mem::collect::ProcessMemPidCollect::GetInstance().CollectProcessNumaMemDistribution( | ||
| 341 | - pidInfo.configInfo.pid, numaDistribution); | ||
| 342 | - if (ret != UBSE_OK || numaDistribution.empty()) { | ||
| 343 | - return UBSE_OK; | ||
| 344 | - } | ||
| 345 | - size_t maxMemSize = 0; | ||
| 346 | - for (const auto& [numaId, memSize] : numaDistribution) { | ||
| 347 | - if (memSize > maxMemSize) { | ||
| 348 | - maxMemSize = memSize; | ||
| 349 | - targetNumaId = numaId; | ||
| 350 | - } | ||
| 351 | - } | ||
| 352 | - } | ||
| 353 | - | ||
| 354 | - auto curNodeInfo = ubse::nodeController::UbseNodeController::GetInstance().GetCurNode(); | ||
| 355 | - for (const auto& [numaLoc, numaInfo] : curNodeInfo.numaInfos) { | ||
| 356 | - if (numaLoc.numaId == targetNumaId) { | ||
| 357 | - borrower.affinitySocketId = numaInfo.socketId; | ||
| 358 | - } | ||
| 359 | - } | ||
| 360 | - return UBSE_OK; | ||
| 361 | -} | ||
| 362 | - | ||
| 363 | -void ExceptionMemoryHandle(const std::string& name) | ||
| 364 | -{ | ||
| 365 | - const int maxRetryTimes = 10; | ||
| 366 | - int flag = 0; | ||
| 367 | - bool isOk = false; | ||
| 368 | - while (flag < maxRetryTimes) { | ||
| 369 | - ++flag; | ||
| 370 | - auto ret = process_mem::pid::bridge::ProcessMemPidBridge::MemoryReturn(name); | ||
| 371 | - if (ret == UBSE_OK || ret == UBSE_ERR_NOT_EXIST) { | ||
| 372 | - isOk = true; | ||
| 373 | - break; | ||
| 374 | - } | ||
| 375 | - sleep(1); | ||
| 376 | - } | ||
| 377 | - | ||
| 378 | - if (!isOk) { | ||
| 379 | - ProcessMemPidInfoManager::GetInstance().exceptionHandleExecutor->Execute( | ||
| 380 | - [name]() { ExceptionMemoryHandle(name); }); | ||
| 381 | - } | ||
| 382 | -} | ||
| 383 | - | ||
| 384 | -int DoMigrateOut(pid_t pid, int destNid, uint64_t memSizeKb) | ||
| 385 | -{ | ||
| 386 | - mempooling::smap::MigrateOutPayload payload{}; | ||
| 387 | std::vector<mempooling::smap::MigrateOutPayload> payloads{}; | 57 | std::vector<mempooling::smap::MigrateOutPayload> payloads{}; |
| 388 | - mempooling::smap::MigrateOutPayloadInner inner{}; | 58 | + for (const auto& [destNid, memSizeKb] : numaTargets) { |
| 389 | - payload.count = 1; | 59 | + mempooling::smap::MigrateOutPayload payload{}; |
| 390 | - inner.migrateMode = mempooling::smap::MIG_MEMSIZE_MODE; | 60 | + mempooling::smap::MigrateOutPayloadInner inner{}; |
| 391 | - inner.memSize = memSizeKb; | 61 | + payload.count = 1; |
| 392 | - inner.destNid = destNid; | 62 | + inner.migrateMode = mempooling::smap::MIG_MEMSIZE_MODE; |
| 393 | - payload.pid = pid; | 63 | + inner.memSize = memSizeKb; |
| 394 | - payload.inner[0] = inner; | 64 | + inner.destNid = destNid; |
| 395 | - payloads.push_back(payload); | 65 | + payload.pid = pid; |
| 66 | + payload.inner[0] = inner; | ||
| 67 | + payloads.push_back(payload); | ||
| 68 | + } | ||
| 396 | return process_mem::pid::bridge::ProcessMemPidBridge::rmrsMigrateOut(payloads, 0); | 69 | return process_mem::pid::bridge::ProcessMemPidBridge::rmrsMigrateOut(payloads, 0); |
| 397 | } | 70 | } |
| 398 | 71 | ||
| 399 | -uint32_t MigrateOut(const def::ProcessMemPidInfo& pidInfo, uint64_t targetRemoteMemory) | 72 | +static std::string MakeProcMemKey(bool isPid, const std::string& identifier) |
| 400 | { | 73 | { |
| 401 | - if (pidInfo.memBorrowInfo.remoteNumaId == -1) { | 74 | + return (isPid ? "pid_" : "name_") + identifier; |
| 402 | - return UBSE_OK; | ||
| 403 | - } | ||
| 404 | - | ||
| 405 | - auto memSizeKb = targetRemoteMemory / 1024; | ||
| 406 | - constexpr uint64_t pageSizeKb = 4; | ||
| 407 | - memSizeKb = memSizeKb / pageSizeKb * pageSizeKb; | ||
| 408 | - | ||
| 409 | - UBSE_LOG_INFO << "MigrateOut begin, targetSize:" << memSizeKb | ||
| 410 | - << "KB, remoteNumaId:" << pidInfo.memBorrowInfo.remoteNumaId; | ||
| 411 | - int ret = DoMigrateOut(pidInfo.configInfo.pid, pidInfo.memBorrowInfo.remoteNumaId, memSizeKb); | ||
| 412 | - if (ret == MEM_POOLING_HANDLING_FAULT) { | ||
| 413 | - UBSE_LOG_ERROR << "MigrateOut fault: MEM_POOLING_HANDLING_FAULT, interrupting all business."; | ||
| 414 | - return UBSE_ERROR; | ||
| 415 | - } | ||
| 416 | - return UBSE_OK; | ||
| 417 | } | 75 | } |
| 418 | 76 | ||
| 419 | -uint64_t RefreshExpectRemoteMemory(pid_t pid, const def::ProcessMemPidInfo& pidInfo, uint64_t defaultExpectRemote) | 77 | +static bool IsRootFilterEnabled() |
| 420 | { | 78 | { |
| 421 | - std::unordered_map<uint32_t, size_t> numaDistribution{}; | 79 | + bool filterRoot = true; |
| 422 | - auto ret = process_mem::collect::ProcessMemPidCollect::GetInstance().CollectProcessNumaMemDistribution( | 80 | + ubse::config::UbseGetBool("process_mem", "filter_root_process", filterRoot); |
| 423 | - pid, numaDistribution); | 81 | + return filterRoot; |
| 424 | - if (ret != UBSE_OK) { | 82 | +} |
| 425 | - UBSE_LOG_WARN << "re-collect numa distribution failed, use original: " << defaultExpectRemote; | ||
| 426 | - return defaultExpectRemote; | ||
| 427 | - } | ||
| 428 | 83 | ||
| 429 | - const int64_t remoteNuma = pidInfo.memBorrowInfo.remoteNumaId; | 84 | +static bool IsRootPid(pid_t pid) |
| 430 | - uint64_t localMemory = 0; | 85 | +{ |
| 431 | - uint64_t remoteMemory = 0; | 86 | + return IsRootFilterEnabled() && collect::GetProcUid(pid) == 0; |
| 432 | - for (const auto& [numaId, memSize] : numaDistribution) { | 87 | +} |
| 433 | - if (numaId == remoteNuma) { | 88 | + |
| 434 | - remoteMemory = memSize; | 89 | +void ProcessMemPidInfoManager::RefreshProcMemConfigCache() |
| 90 | +{ | ||
| 91 | + std::unique_lock<std::shared_mutex> lock(procMemConfigMutex_); | ||
| 92 | + procMemConfigCache_.clear(); | ||
| 93 | + std::vector<def::ProcessMemNewConfigInfo> allConfigs; | ||
| 94 | + ProcessMemPidConfigManager::GetAllPersistedProcMemConfigs(allConfigs); | ||
| 95 | + | ||
| 96 | + size_t restoredPidCount = 0; | ||
| 97 | + size_t restoredNameCount = 0; | ||
| 98 | + | ||
| 99 | + for (auto& cfg : allConfigs) { | ||
| 100 | + if (cfg.isPid) { | ||
| 101 | + auto pidOpt = def::ParsePidFromIdentifier(cfg.identifier); | ||
| 102 | + if (!pidOpt.has_value()) { | ||
| 103 | + UBSE_LOG_WARN << "[process_mem] init restore: invalid PID identifier=" << cfg.identifier | ||
| 104 | + << ", skip recovery, deleting stale config"; | ||
| 105 | + (void)ProcessMemPidConfigManager::DeleteProcMemConfig(cfg.isPid, cfg.identifier); | ||
| 106 | + continue; | ||
| 107 | + } | ||
| 108 | + ++restoredPidCount; | ||
| 435 | } else { | 109 | } else { |
| 436 | - localMemory += memSize; | 110 | + ++restoredNameCount; |
| 437 | } | 111 | } |
| 112 | + procMemConfigCache_.push_back(cfg); | ||
| 438 | } | 113 | } |
| 439 | - auto freshExpectRemote = ((localMemory + remoteMemory) * pidInfo.configInfo.targetEvictThreshold) / 100; | 114 | + UBSE_LOG_INFO << "[process_mem] init restore: scanned " << allConfigs.size() << " persisted keys, restored " |
| 440 | - UBSE_LOG_INFO << "refresh expectRemoteMemory: " << freshExpectRemote << ", localMemory=" << localMemory | 115 | + << procMemConfigCache_.size() << " configs (pid=" << restoredPidCount << " name=" << restoredNameCount |
| 441 | - << ", remoteMemory=" << remoteMemory; | 116 | + << ")"; |
| 442 | - return freshExpectRemote; | ||
| 443 | } | 117 | } |
| 444 | 118 | ||
| 445 | -void AsyncBorrowAndMigrateExecute(pid_t pid, const def::DebtInfo& debtInfo, uint64_t expectRemoteMemory) | 119 | +uint32_t ProcessMemPidInfoManager::FossilizePidConfig(pid_t pid, const std::string& name, uint64_t maxMemory, |
| 120 | + double remoteRatio, bool force) | ||
| 446 | { | 121 | { |
| 447 | - auto freshInfo = ProcessMemPidInfoManager::GetInstance().GetPidInfoMap(pid); | 122 | + const std::string idStr = std::to_string(pid); |
| 448 | - if (freshInfo.configInfo.pid == -1) { | 123 | + if (!force) { |
| 449 | - return; | 124 | + std::shared_lock<std::shared_mutex> lock(procMemConfigMutex_); |
| 450 | - } | 125 | + for (const auto& cfg : procMemConfigCache_) { |
| 451 | - | 126 | + if (cfg.isPid && cfg.identifier == idStr) { |
| 452 | - ubse::mem::controller::UbseMemBorrower borrower{}; | 127 | + return UBSE_OK; |
| 453 | - if (GetUbseMemBorrower(freshInfo, borrower) != UBSE_OK) { | ||
| 454 | - ProcessMemPidInfoManager::GetInstance().DeletePidMemBorrowInfo(pid, debtInfo.numaDesc.name); | ||
| 455 | - return; | ||
| 456 | - } | ||
| 457 | - | ||
| 458 | - ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 459 | - pid::bridge::MemoryBorrowRequest request{}; | ||
| 460 | - auto curNodeInfo = ubse::nodeController::UbseNodeController::GetInstance().GetCurNode(); | ||
| 461 | - uint64_t blockSizeBytes = static_cast<uint64_t>(curNodeInfo.blockSize) * 1024 * 1024; | ||
| 462 | - request.name = debtInfo.numaDesc.name; | ||
| 463 | - // 根据ubse的对齐规则,size向上取整对齐到blockSize大小,+ blockSize - 1把余数部分"推过"下一个 block 边界 | ||
| 464 | - request.size = ((debtInfo.numaDesc.size + blockSizeBytes - 1) / blockSizeBytes) * blockSizeBytes; | ||
| 465 | - if (memcpy_s(request.usrInfo, ubse::mem::controller::UBSE_MAX_USR_INFO_LEN, debtInfo.numaDesc.usrInfo, | ||
| 466 | - ubse::mem::controller::UBSE_MAX_USR_INFO_LEN) != EOK) { | ||
| 467 | - ProcessMemPidInfoManager::GetInstance().DeletePidMemBorrowInfo(pid, debtInfo.numaDesc.name); | ||
| 468 | - return; | ||
| 469 | - } | ||
| 470 | - auto ret = process_mem::pid::bridge::ProcessMemPidBridge::MemoryBorrow(freshInfo, borrower, request, desc); | ||
| 471 | - auto now = std::chrono::steady_clock::now(); | ||
| 472 | - if ((now - debtInfo.borrowStartTime > std::chrono::seconds(g_borrowTimeOut)) || ret != UBSE_OK) { | ||
| 473 | - UBSE_LOG_ERROR << "MemoryBorrow failed or timeout, pid=" << pid << ", name=" << desc.name; | ||
| 474 | - ProcessMemPidInfoManager::GetInstance().DeletePidMemBorrowInfo(pid, desc.name); | ||
| 475 | - ProcessMemPidInfoManager::GetInstance().exceptionHandleExecutor->Execute( | ||
| 476 | - [name = desc.name]() { ExceptionMemoryHandle(name); }); | ||
| 477 | - return; | ||
| 478 | - } | ||
| 479 | - | ||
| 480 | - def::DebtInfo completedDebt = debtInfo; | ||
| 481 | - completedDebt.status = def::BorrowStatus::COMPLETED; | ||
| 482 | - completedDebt.numaDesc = desc; | ||
| 483 | - ret = ProcessMemPidInfoManager::GetInstance().UpdatePidMemBorrowInfo(pid, borrower, completedDebt); | ||
| 484 | - if (ret != UBSE_OK) { | ||
| 485 | - UBSE_LOG_ERROR << "UpdatePidMemBorrowInfo failed after borrow, pid=" << pid; | ||
| 486 | - ProcessMemPidInfoManager::GetInstance().exceptionHandleExecutor->Execute( | ||
| 487 | - [name = desc.name]() { ExceptionMemoryHandle(name); }); | ||
| 488 | - return; | ||
| 489 | - } | ||
| 490 | - | ||
| 491 | - auto infoAfterBorrow = ProcessMemPidInfoManager::GetInstance().GetPidInfoMap(pid); | ||
| 492 | - if (infoAfterBorrow.configInfo.pid == -1) { | ||
| 493 | - return; | ||
| 494 | - } | ||
| 495 | - auto freshExpectRemote = RefreshExpectRemoteMemory(pid, infoAfterBorrow, expectRemoteMemory); | ||
| 496 | - if (MigrateOut(infoAfterBorrow, freshExpectRemote) != UBSE_OK) { | ||
| 497 | - UBSE_LOG_ERROR << "MigrateOut failed after borrow, interrupting business for pid=" << pid; | ||
| 498 | - return; | ||
| 499 | - } | ||
| 500 | -} | ||
| 501 | - | ||
| 502 | -uint32_t BorrowAndMigrate(def::ProcessMemPidInfo& pidInfo, uint64_t expectRemoteMemory, int32_t minNuma) | ||
| 503 | -{ | ||
| 504 | - bool isNeedBorrow = false; | ||
| 505 | - uint64_t needBorrowSize = 0; | ||
| 506 | - isNeedBorrow = CheckIsNeedBorrow(pidInfo, expectRemoteMemory, needBorrowSize); | ||
| 507 | - if (!isNeedBorrow) { | ||
| 508 | - if (MigrateOut(pidInfo, expectRemoteMemory) != UBSE_OK) { | ||
| 509 | - UBSE_LOG_ERROR << "MigrateOut failed, interrupting business for pid=" << pidInfo.configInfo.pid; | ||
| 510 | - return UBSE_ERROR; | ||
| 511 | - } | ||
| 512 | - return UBSE_OK; | ||
| 513 | - } | ||
| 514 | - | ||
| 515 | - def::DebtInfo debtInfo{}; | ||
| 516 | - std::string name = GenerateSimpleName(pidInfo.configInfo.pid); | ||
| 517 | - debtInfo.borrowStartTime = std::chrono::steady_clock::now(); | ||
| 518 | - debtInfo.numaDesc.name = name; | ||
| 519 | - debtInfo.numaDesc.size = needBorrowSize; | ||
| 520 | - debtInfo.status = def::BorrowStatus::CREATING; | ||
| 521 | - const def::ProcessMemUsrInfo usrInfo{ | ||
| 522 | - .pid = pidInfo.configInfo.pid, .startTime = pidInfo.startTime, .srcNuma = minNuma}; | ||
| 523 | - auto ret = | ||
| 524 | - memcpy_s(debtInfo.numaDesc.usrInfo, ubse::mem::controller::UBSE_MAX_USR_INFO_LEN, &usrInfo, sizeof(usrInfo)); | ||
| 525 | - if (ret != EOK) { | ||
| 526 | - UBSE_LOG_ERROR << "memcpy_s failed"; | ||
| 527 | - return UBSE_ERROR; | ||
| 528 | - } | ||
| 529 | - ProcessMemPidInfoManager::GetInstance().UpdatePidMemBorrowInfo(pidInfo.configInfo.pid, debtInfo); | ||
| 530 | - | ||
| 531 | - auto pid = pidInfo.configInfo.pid; | ||
| 532 | - ProcessMemPidInfoManager::GetInstance().borrowExecutor->Execute( | ||
| 533 | - [pid, debtInfo, expectRemoteMemory]() { AsyncBorrowAndMigrateExecute(pid, debtInfo, expectRemoteMemory); }); | ||
| 534 | - | ||
| 535 | - return UBSE_OK; | ||
| 536 | -} | ||
| 537 | - | ||
| 538 | -uint32_t BorrowMemoryAndMigrateOut(def::ProcessMemPidInfo& pidInfo, uint64_t localMemorySize, uint64_t remoteMemorySize, | ||
| 539 | - int32_t minNuma) | ||
| 540 | -{ | ||
| 541 | - auto expectRemoteMemory = ((localMemorySize + remoteMemorySize) * pidInfo.configInfo.targetEvictThreshold) / 100; | ||
| 542 | - UBSE_LOG_INFO << "localMemory is " << localMemorySize << ", remoteMemory is " << remoteMemorySize | ||
| 543 | - << ", expect RemoteMemory is " << expectRemoteMemory; | ||
| 544 | - if (expectRemoteMemory <= remoteMemorySize) { | ||
| 545 | - if (MigrateOut(pidInfo, expectRemoteMemory) != UBSE_OK) { | ||
| 546 | - UBSE_LOG_ERROR << "MigrateOut failed, interrupting business for pid=" << pidInfo.configInfo.pid; | ||
| 547 | - return UBSE_ERROR; | ||
| 548 | - } | ||
| 549 | - return UBSE_OK; | ||
| 550 | - } | ||
| 551 | - BorrowAndMigrate(pidInfo, expectRemoteMemory, minNuma); | ||
| 552 | - return UBSE_OK; | ||
| 553 | -} | ||
| 554 | - | ||
| 555 | -// 归还内存 | ||
| 556 | -uint32_t MigrateBackAndReturnMemoryAsyncExecute(const def::ProcessMemPidInfo& pidInfo) | ||
| 557 | -{ | ||
| 558 | - if (!ProcessMemPidInfoManager::GetInstance().IsRecoverCompleted()) { | ||
| 559 | - UBSE_LOG_INFO << "Recover not completed, retry MigrateBackAndReturnMemory later, pid=" | ||
| 560 | - << pidInfo.configInfo.pid; | ||
| 561 | - ProcessMemPidInfoManager::GetInstance().returnExecutor->Execute( | ||
| 562 | - [pidInfo]() { MigrateBackAndReturnMemoryAsyncExecute(pidInfo); }); | ||
| 563 | - return UBSE_OK; | ||
| 564 | - } | ||
| 565 | - | ||
| 566 | - int remoteNuma = pidInfo.memBorrowInfo.remoteNumaId; | ||
| 567 | - if (!pidInfo.memBorrowInfo.debtInfos.empty()) { | ||
| 568 | - if (MigrateOut(pidInfo, 0) != UBSE_OK) { | ||
| 569 | - UBSE_LOG_ERROR << "MigrateOut failed in return path, interrupting business for pid=" | ||
| 570 | - << pidInfo.configInfo.pid; | ||
| 571 | - return UBSE_ERROR; | ||
| 572 | - } | ||
| 573 | - bool res = GetMigrateResult(pidInfo.configInfo.pid, remoteNuma, 0, true); | ||
| 574 | - if (!res) { | ||
| 575 | - UBSE_LOG_ERROR << "Memory Return rmrsMigrateOut failed"; | ||
| 576 | - } | ||
| 577 | - std::vector<pid_t> pids; | ||
| 578 | - pids.push_back(pidInfo.configInfo.pid); | ||
| 579 | - UBSE_LOG_INFO << "rmrsRemove called, pid=" << pidInfo.configInfo.pid; | ||
| 580 | - res = pid::bridge::ProcessMemPidBridge::rmrsRemove(def::INVALID_REMOTE_NUMA, pids, 0); | ||
| 581 | - for (const auto& [name, debtInfo] : pidInfo.memBorrowInfo.debtInfos) { | ||
| 582 | - auto borrowName = name; | ||
| 583 | - auto ret = process_mem::pid::bridge::ProcessMemPidBridge::rmrsFreeWithMigrate(debtInfo.numaDesc.name); | ||
| 584 | - if (ret == MEM_POOLING_HANDLING_FAULT) { | ||
| 585 | - UBSE_LOG_ERROR << "rmrsFreeWithMigrate fault: MEM_POOLING_HANDLING_FAULT, " | ||
| 586 | - << "interrupting all business for pid=" << pidInfo.configInfo.pid; | ||
| 587 | - ProcessMemPidInfoManager::GetInstance().DeletePidMemBorrowInfo(pidInfo.configInfo.pid, name); | ||
| 588 | - ProcessMemPidInfoManager::GetInstance().ResetPidMemBorrowInfo(pidInfo.configInfo.pid); | ||
| 589 | - return UBSE_ERROR; | ||
| 590 | - } | ||
| 591 | - if (ret != UBSE_OK && ret != UBSE_ERR_NOT_EXIST) { | ||
| 592 | - UBSE_LOG_ERROR << "rmrsFreeWithMigrate failed"; | ||
| 593 | - ProcessMemPidInfoManager::GetInstance().exceptionHandleExecutor->Execute( | ||
| 594 | - [borrowName]() { ExceptionMemoryHandle(borrowName); }); | ||
| 595 | - } | ||
| 596 | - ProcessMemPidInfoManager::GetInstance().DeletePidMemBorrowInfo(pidInfo.configInfo.pid, name); | ||
| 597 | - } | ||
| 598 | - } | ||
| 599 | - // 重置借用信息,下次借用从头开始,exportSlotId 回归 -1 | ||
| 600 | - ProcessMemPidInfoManager::GetInstance().ResetPidMemBorrowInfo(pidInfo.configInfo.pid); | ||
| 601 | - | ||
| 602 | - return UBSE_OK; | ||
| 603 | -} | ||
| 604 | - | ||
| 605 | -uint32_t MigrateBackAndReturnMemory(def::ProcessMemPidInfo& pidInfo) | ||
| 606 | -{ | ||
| 607 | - auto& mgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 608 | - if (mgr.returnExecutor.Get() == nullptr) { | ||
| 609 | - return UBSE_ERROR; | ||
| 610 | - } | ||
| 611 | - mgr.returnExecutor->Execute([pidInfo]() { MigrateBackAndReturnMemoryAsyncExecute(pidInfo); }); | ||
| 612 | - return UBSE_OK; | ||
| 613 | -} | ||
| 614 | - | ||
| 615 | -uint32_t MemoryCheckHandle(def::ProcessMemPidInfo& info, const std::unordered_map<uint32_t, size_t>& numaMemory) | ||
| 616 | -{ | ||
| 617 | - const int64_t remoteNuma = info.memBorrowInfo.remoteNumaId; | ||
| 618 | - uint64_t localMemory = 0; | ||
| 619 | - uint64_t remoteMemory = 0; | ||
| 620 | - | ||
| 621 | - for (const auto& [numaId, memorySize] : numaMemory) { | ||
| 622 | - if (numaId == remoteNuma) { | ||
| 623 | - remoteMemory = memorySize; | ||
| 624 | - } else { | ||
| 625 | - localMemory += memorySize; | ||
| 626 | - } | ||
| 627 | - } | ||
| 628 | - | ||
| 629 | - const auto totalMemory = localMemory + remoteMemory; | ||
| 630 | - const auto expectMemory = info.configInfo.expectedMemoryUsage; | ||
| 631 | - if (expectMemory == 0) { | ||
| 632 | - return UBSE_OK; | ||
| 633 | - } | ||
| 634 | - const auto needBorrow = totalMemory * 100 > static_cast<uint64_t>(info.configInfo.evictThreshold) * expectMemory; | ||
| 635 | - const auto needReturn = totalMemory * 100 < static_cast<uint64_t>(info.configInfo.reclaimThreshold) * expectMemory; | ||
| 636 | - | ||
| 637 | - UBSE_LOG_INFO << "needBorrow IS " << needBorrow << ", needReturn is" << needReturn << ", localMemory is " | ||
| 638 | - << localMemory << ", remote memory is " << remoteMemory << ",expectMemory is " << expectMemory; | ||
| 639 | - if (needBorrow) { | ||
| 640 | - int32_t minNuma = -1; | ||
| 641 | - for (const auto& [numaId, _] : numaMemory) { | ||
| 642 | - if (minNuma == -1 || static_cast<int32_t>(numaId) < minNuma) { | ||
| 643 | - minNuma = static_cast<int32_t>(numaId); | ||
| 644 | - } | ||
| 645 | - } | ||
| 646 | - BorrowMemoryAndMigrateOut(info, localMemory, remoteMemory, minNuma); | ||
| 647 | - } else if (needReturn) { | ||
| 648 | - MigrateBackAndReturnMemory(info); | ||
| 649 | - } | ||
| 650 | - return UBSE_OK; | ||
| 651 | -} | ||
| 652 | - | ||
| 653 | -uint32_t ProcessMemPidInfoManager::PerPidMemoryCheckCallBack(pid_t pid, | ||
| 654 | - const std::unordered_map<uint32_t, size_t>& numaMemory) | ||
| 655 | -{ | ||
| 656 | - if (!isRecoverCompleted_) { | ||
| 657 | - UBSE_LOG_INFO << "isRecoverCompleted_ is false"; | ||
| 658 | - return UBSE_OK; | ||
| 659 | - } | ||
| 660 | - def::ProcessMemPidInfo info; | ||
| 661 | - { | ||
| 662 | - std::unique_lock<std::shared_mutex> lock(pidInfoMutex); | ||
| 663 | - auto pidIt = pidInfoMap.find(pid); | ||
| 664 | - if (pidIt == pidInfoMap.end()) { | ||
| 665 | - return UBSE_OK; | ||
| 666 | - } | ||
| 667 | - if (pidIt->second.processStatus == def::ProcessStatus::FAULT) { | ||
| 668 | - return UBSE_OK; | ||
| 669 | - } | ||
| 670 | - info = pidIt->second; | ||
| 671 | - } | ||
| 672 | - for (const auto& [key, value] : numaMemory) { | ||
| 673 | - UBSE_LOG_INFO << "pid is " << pid << ", numaId is " << key << ", size is " << value; | ||
| 674 | - } | ||
| 675 | - | ||
| 676 | - UBSE_LOG_INFO << "evictThreshold is " << info.configInfo.evictThreshold << "target evictThreshold is " | ||
| 677 | - << info.configInfo.targetEvictThreshold << "total used is" << info.configInfo.expectedMemoryUsage; | ||
| 678 | - MemoryCheckHandle(info, numaMemory); | ||
| 679 | - return UBSE_OK; | ||
| 680 | -} | ||
| 681 | - | ||
| 682 | -template <typename T> | ||
| 683 | -bool IsProcessMemDebt(const T& debt) | ||
| 684 | -{ | ||
| 685 | - def::ProcessMemUsrInfo usrInfo{}; | ||
| 686 | - if (memcpy_s(&usrInfo, sizeof(usrInfo), debt.usrInfo, sizeof(usrInfo)) != EOK) { | ||
| 687 | - return false; | ||
| 688 | - } | ||
| 689 | - return usrInfo.pluginId == def::UsrInfoPluginType::PROCESS_MEM; | ||
| 690 | -} | ||
| 691 | - | ||
| 692 | -namespace { | ||
| 693 | -struct ProcessMemDebtEntry { | ||
| 694 | - pid_t pid{}; | ||
| 695 | - ubse::mem::controller::UbseNumaMemoryImportDebtInfo debtInfo{}; | ||
| 696 | -}; | ||
| 697 | - | ||
| 698 | -std::vector<ProcessMemDebtEntry> CollectProcessMemImportDebts() | ||
| 699 | -{ | ||
| 700 | - std::vector<ubse::mem::controller::UbseNumaMemoryImportDebtInfo> debtInfos{}; | ||
| 701 | - auto ret = ubse::mem::controller::UbseGetNumaMemImportDebtInfoWithLocalNode(debtInfos); | ||
| 702 | - if (ret != UBSE_OK) { | ||
| 703 | - UBSE_LOG_WARN << "RefreshBorrowInfo: query import debts failed, ret=" << ret; | ||
| 704 | - return {}; | ||
| 705 | - } | ||
| 706 | - | ||
| 707 | - std::vector<ProcessMemDebtEntry> result; | ||
| 708 | - for (const auto& debtInfo : debtInfos) { | ||
| 709 | - if (!IsProcessMemDebt(debtInfo)) { | ||
| 710 | - continue; | ||
| 711 | - } | ||
| 712 | - def::ProcessMemUsrInfo usrInfo{}; | ||
| 713 | - if (memcpy_s(&usrInfo, sizeof(usrInfo), debtInfo.usrInfo, sizeof(usrInfo)) != EOK || usrInfo.pid <= 0) { | ||
| 714 | - continue; | ||
| 715 | - } | ||
| 716 | - result.push_back({usrInfo.pid, debtInfo}); | ||
| 717 | - } | ||
| 718 | - return result; | ||
| 719 | -} | ||
| 720 | - | ||
| 721 | -void ApplyImportDebtToBorrowInfo(def::BorrowInfo& borrowInfo, | ||
| 722 | - const ubse::mem::controller::UbseNumaMemoryImportDebtInfo& debt) | ||
| 723 | -{ | ||
| 724 | - borrowInfo.remoteNumaId = debt.remoteNumaId; | ||
| 725 | - if (!debt.borrowSocketIdList.empty()) { | ||
| 726 | - borrowInfo.importSocketId = debt.borrowSocketIdList[0]; | ||
| 727 | - } | ||
| 728 | -} | ||
| 729 | - | ||
| 730 | -void MergeOrUpdateDebt(def::BorrowInfo& borrowInfo, const ubse::mem::controller::UbseNumaMemoryImportDebtInfo& debt) | ||
| 731 | -{ | ||
| 732 | - auto debtIt = borrowInfo.debtInfos.find(debt.name); | ||
| 733 | - if (debtIt != borrowInfo.debtInfos.end()) { | ||
| 734 | - debtIt->second.numaDesc.size = debt.size; | ||
| 735 | - debtIt->second.numaDesc.numaId = debt.remoteNumaId; | ||
| 736 | - return; | ||
| 737 | - } | ||
| 738 | - | ||
| 739 | - def::DebtInfo newDebt{}; | ||
| 740 | - newDebt.status = def::BorrowStatus::COMPLETED; | ||
| 741 | - newDebt.numaDesc.name = debt.name; | ||
| 742 | - newDebt.numaDesc.numaId = debt.remoteNumaId; | ||
| 743 | - newDebt.numaDesc.size = debt.size; | ||
| 744 | - memcpy_s(newDebt.numaDesc.usrInfo, ubse::mem::controller::UBSE_MAX_USR_INFO_LEN, debt.usrInfo, | ||
| 745 | - ubse::mem::controller::UBSE_MAX_USR_INFO_LEN); | ||
| 746 | - borrowInfo.debtInfos[debt.name] = newDebt; | ||
| 747 | -} | ||
| 748 | - | ||
| 749 | -void RemoveStaleDebts(def::BorrowInfo& borrowInfo, const std::unordered_set<std::string>& activeNames) | ||
| 750 | -{ | ||
| 751 | - auto& debtMap = borrowInfo.debtInfos; | ||
| 752 | - for (auto it = debtMap.begin(); it != debtMap.end();) { | ||
| 753 | - bool inUbse = activeNames.count(it->first) > 0; | ||
| 754 | - if (inUbse) { | ||
| 755 | - ++it; | ||
| 756 | - continue; | ||
| 757 | - } | ||
| 758 | - if (it->second.status == def::BorrowStatus::CREATING) { | ||
| 759 | - ++it; | ||
| 760 | - continue; | ||
| 761 | - } | ||
| 762 | - UBSE_LOG_INFO << "RefreshBorrowInfo: remove stale debt " << it->first; | ||
| 763 | - it = debtMap.erase(it); | ||
| 764 | - } | ||
| 765 | -} | ||
| 766 | - | ||
| 767 | -struct RemovedPidDebt { | ||
| 768 | - pid_t pid; | ||
| 769 | - std::vector<std::string> debtNames; | ||
| 770 | - int remoteNumaId; | ||
| 771 | -}; | ||
| 772 | - | ||
| 773 | -void AsyncMigrateBackAndFreeForRemovedPid(const RemovedPidDebt& entry) | ||
| 774 | -{ | ||
| 775 | - UBSE_LOG_INFO << "RefreshBorrowInfo: async process removed pid=" << entry.pid | ||
| 776 | - << ", debtCount=" << entry.debtNames.size() << ", remoteNumaId=" << entry.remoteNumaId; | ||
| 777 | - | ||
| 778 | - auto exactStartTime = ProcessMemPidConfigManager::GetExactStartTime(entry.pid); | ||
| 779 | - if (exactStartTime == 0) { | ||
| 780 | - UBSE_LOG_INFO << "RefreshBorrowInfo: process exited, returning memory directly for pid=" << entry.pid; | ||
| 781 | - for (const auto& name : entry.debtNames) { | ||
| 782 | - auto ret = process_mem::pid::bridge::ProcessMemPidBridge::MemoryReturn(name); | ||
| 783 | - if (ret == UBSE_ERR_NOT_EXIST || ret == UBSE_ERR_UNIMPORT_SUCCESS || ret == UBSE_ERR_DELETING) { | ||
| 784 | - continue; | ||
| 785 | - } | ||
| 786 | - if (ret != UBSE_OK) { | ||
| 787 | - UBSE_LOG_ERROR << "RefreshBorrowInfo MemoryReturn failed for exited pid=" << entry.pid | ||
| 788 | - << ", debt=" << name << ", ret=" << ret; | ||
| 789 | - } | ||
| 790 | - } | ||
| 791 | - return; | ||
| 792 | - } | ||
| 793 | - | ||
| 794 | - int ret = DoMigrateOut(entry.pid, entry.remoteNumaId, 0); | ||
| 795 | - if (ret == MEM_POOLING_HANDLING_FAULT) { | ||
| 796 | - UBSE_LOG_ERROR << "RefreshBorrowInfo MigrateOut fault: MEM_POOLING_HANDLING_FAULT," | ||
| 797 | - << " interrupting for removed pid=" << entry.pid; | ||
| 798 | - return; | ||
| 799 | - } | ||
| 800 | - if (ret != MEM_POOLING_OK) { | ||
| 801 | - UBSE_LOG_ERROR << "RefreshBorrowInfo DoMigrateOut failed for removed pid=" << entry.pid << ", ret=" << ret; | ||
| 802 | - return; | ||
| 803 | - } | ||
| 804 | - bool migrateDone = GetMigrateResult(entry.pid, static_cast<int64_t>(entry.remoteNumaId), 0, true); | ||
| 805 | - if (!migrateDone) { | ||
| 806 | - UBSE_LOG_ERROR << "RefreshBorrowInfo GetMigrateResult failed for removed pid=" << entry.pid; | ||
| 807 | - return; | ||
| 808 | - } | ||
| 809 | - for (const auto& name : entry.debtNames) { | ||
| 810 | - auto freeRet = process_mem::pid::bridge::ProcessMemPidBridge::rmrsFreeWithMigrate(name); | ||
| 811 | - if (freeRet == MEM_POOLING_HANDLING_FAULT) { | ||
| 812 | - UBSE_LOG_ERROR << "RefreshBorrowInfo rmrsFreeWithMigrate fault: MEM_POOLING_HANDLING_FAULT," | ||
| 813 | - << " interrupting for removed pid=" << entry.pid << ", debt=" << name; | ||
| 814 | - continue; | ||
| 815 | - } | ||
| 816 | - if (freeRet != UBSE_OK && freeRet != UBSE_ERR_NOT_EXIST) { | ||
| 817 | - UBSE_LOG_ERROR << "RefreshBorrowInfo rmrsFreeWithMigrate failed for removed pid=" << entry.pid | ||
| 818 | - << ", debt=" << name << ", ret=" << freeRet; | ||
| 819 | - } | ||
| 820 | - } | ||
| 821 | -} | ||
| 822 | - | ||
| 823 | -} // namespace | ||
| 824 | - | ||
| 825 | -void ProcessMemPidInfoManager::RefreshBorrowInfo() | ||
| 826 | -{ | ||
| 827 | - auto entries = CollectProcessMemImportDebts(); | ||
| 828 | - | ||
| 829 | - std::unordered_map<pid_t, std::unordered_set<std::string>> activeDebts; | ||
| 830 | - for (const auto& entry : entries) { | ||
| 831 | - activeDebts[entry.pid].insert(entry.debtInfo.name); | ||
| 832 | - } | ||
| 833 | - | ||
| 834 | - // 收集已移除纳管的 pid(UBSE 中有账本但 pidInfoMap 中无配置),按 pid 汇聚 | ||
| 835 | - std::vector<RemovedPidDebt> removedPidDebts; | ||
| 836 | - std::vector<std::pair<pid_t, int>> clearedPids; | ||
| 837 | - { | ||
| 838 | - std::unique_lock<std::shared_mutex> lock(pidInfoMutex); | ||
| 839 | - | ||
| 840 | - for (const auto& entry : entries) { | ||
| 841 | - if (pidInfoMap.find(entry.pid) != pidInfoMap.end()) { | ||
| 842 | - continue; | ||
| 843 | - } | ||
| 844 | - auto it = std::find_if(removedPidDebts.begin(), removedPidDebts.end(), | ||
| 845 | - [pid = entry.pid](const auto& item) { return item.pid == pid; }); | ||
| 846 | - if (it != removedPidDebts.end()) { | ||
| 847 | - it->debtNames.push_back(entry.debtInfo.name); | ||
| 848 | - } else { | ||
| 849 | - removedPidDebts.push_back( | ||
| 850 | - {entry.pid, {entry.debtInfo.name}, static_cast<int>(entry.debtInfo.remoteNumaId)}); | ||
| 851 | - } | ||
| 852 | - } | ||
| 853 | - | ||
| 854 | - // 合并/更新仍在管 pid 的运行态账本 | ||
| 855 | - for (const auto& entry : entries) { | ||
| 856 | - auto pidIt = pidInfoMap.find(entry.pid); | ||
| 857 | - if (pidIt == pidInfoMap.end()) { | ||
| 858 | - continue; | ||
| 859 | - } | ||
| 860 | - auto& borrowInfo = pidIt->second.memBorrowInfo; | ||
| 861 | - ApplyImportDebtToBorrowInfo(borrowInfo, entry.debtInfo); | ||
| 862 | - MergeOrUpdateDebt(borrowInfo, entry.debtInfo); | ||
| 863 | - } | ||
| 864 | - | ||
| 865 | - // 清理 UBSE 中已不存在的账本(仅清本地缓存,不回迁),保留 CREATING 状态 | ||
| 866 | - static const std::unordered_set<std::string> emptySet; | ||
| 867 | - for (auto& [pid, pidInfo] : pidInfoMap) { | ||
| 868 | - auto activeIt = activeDebts.find(pid); | ||
| 869 | - const auto& activeNames = (activeIt != activeDebts.end()) ? activeIt->second : emptySet; | ||
| 870 | - RemoveStaleDebts(pidInfo.memBorrowInfo, activeNames); | ||
| 871 | - if (pidInfo.memBorrowInfo.debtInfos.empty()) { | ||
| 872 | - clearedPids.emplace_back(pid, pidInfo.memBorrowInfo.remoteNumaId); | ||
| 873 | - pidInfo.memBorrowInfo = {}; | ||
| 874 | } | 128 | } |
| 875 | } | 129 | } |
| 876 | } | 130 | } |
| 877 | 131 | ||
| 878 | - // 异步处理已移除纳管的 pid:一次性回迁所有远端内存,再逐个归还账本 | 132 | + auto startTime = ProcessMemPidConfigManager::GetExactStartTime(pid); |
| 879 | - for (auto& entry : removedPidDebts) { | 133 | + if (startTime == 0) { |
| 880 | - UBSE_LOG_INFO << "RefreshBorrowInfo: removed pid=" << entry.pid << ", debtCount=" << entry.debtNames.size() | 134 | + UBSE_LOG_WARN << "FossilizePidConfig: pid=" << pid << " does not exist, skip"; |
| 881 | - << ", remoteNumaId=" << entry.remoteNumaId << ", submit to exception executor"; | ||
| 882 | - exceptionHandleExecutor->Execute([entry = std::move(entry)]() { AsyncMigrateBackAndFreeForRemovedPid(entry); }); | ||
| 883 | - } | ||
| 884 | - | ||
| 885 | - for (const auto& [pid, remoteNumaId] : clearedPids) { | ||
| 886 | - std::vector<pid_t> pids = {pid}; | ||
| 887 | - UBSE_LOG_INFO << "rmrsRemove called, pid=" << pid; | ||
| 888 | - pid::bridge::ProcessMemPidBridge::rmrsRemove(def::INVALID_REMOTE_NUMA, pids, 0); | ||
| 889 | - } | ||
| 890 | -} | ||
| 891 | - | ||
| 892 | -void ProcessMemPidInfoManager::TotalMemoryCheckCallBack(const collect::CollectInfoMap& collectInfo) | ||
| 893 | -{ | ||
| 894 | - CheckFaultNodesRecovery(); | ||
| 895 | - RefreshBorrowInfo(); | ||
| 896 | - for (const auto& [pid, numaMemoryInfo] : collectInfo) { | ||
| 897 | - auto ret = PerPidMemoryCheckCallBack(pid, numaMemoryInfo); | ||
| 898 | - if (ret != UBSE_OK) { | ||
| 899 | - UBSE_LOG_ERROR << "PerPidMemoryCheckCallBack failed"; | ||
| 900 | - } | ||
| 901 | - } | ||
| 902 | -} | ||
| 903 | - | ||
| 904 | -uint32_t ProcessMemPidInfoManager::UnsetPidInfo(pid_t pid) | ||
| 905 | -{ | ||
| 906 | - std::unique_lock<std::shared_mutex> lock(pidInfoMutex); | ||
| 907 | - auto pidIt = pidInfoMap.find(pid); | ||
| 908 | - if (pidIt == pidInfoMap.end()) { | ||
| 909 | - UBSE_LOG_INFO << "pid not found in pidInfoMap, pid is " << pid; | ||
| 910 | return UBSE_ERR_NOT_EXIST; | 135 | return UBSE_ERR_NOT_EXIST; |
| 911 | } | 136 | } |
| 912 | - UBSE_LOG_INFO << "pid has borrow info, pid is " << pid; | 137 | + |
| 913 | - MigrateBackAndReturnMemory(pidIt->second); | 138 | + def::FossilPidConfigInfo fossil{}; |
| 914 | - ProcessMemPidConfigManager::DeletePidConfigInfo(pid); | 139 | + fossil.name = name; |
| 915 | - pidInfoMap.erase(pid); | 140 | + fossil.maxMemory = maxMemory; |
| 141 | + fossil.remoteRatio = remoteRatio; | ||
| 142 | + fossil.startTime = static_cast<uint64_t>(startTime); | ||
| 143 | + auto persistRet = ProcessMemPidConfigManager::PersistFossilConfig(pid, fossil); | ||
| 144 | + if (persistRet != UBSE_OK) { | ||
| 145 | + UBSE_LOG_ERROR << "FossilizePidConfig: persist failed for pid=" << pid | ||
| 146 | + << ", ret=" << ubse::log::FormatRetCode(persistRet); | ||
| 147 | + return UBSE_ERROR; | ||
| 148 | + } | ||
| 149 | + UBSE_LOG_INFO << "FossilizePidConfig: pid=" << pid << " name=" << name << " maxMemory=" << maxMemory | ||
| 150 | + << " remoteRatio=" << remoteRatio; | ||
| 916 | return UBSE_OK; | 151 | return UBSE_OK; |
| 917 | } | 152 | } |
| 918 | 153 | ||
| 919 | -namespace { | 154 | +bool ProcessMemPidInfoManager::HasExplicitPidConfig(pid_t pid) const |
| 920 | -void RecoverSingleDebtInfo(std::unordered_map<pid_t, def::ProcessMemPidInfo>& pidInfoMap, | ||
| 921 | - const ubse::mem::controller::UbseNumaMemoryDebtInfo& debtInfo, | ||
| 922 | - ubse::task_executor::UbseTaskExecutorPtr& exceptionHandleExecutor) | ||
| 923 | { | 155 | { |
| 924 | - def::ProcessMemUsrInfo usrInfo{}; | 156 | + std::shared_lock<std::shared_mutex> lock(procMemConfigMutex_); |
| 925 | - if (memcpy_s(&usrInfo, sizeof(usrInfo), debtInfo.usrInfo, sizeof(usrInfo)) != EOK) { | 157 | + const std::string idStr = std::to_string(pid); |
| 926 | - UBSE_LOG_ERROR << "memcpy_s usrInfo failed"; | 158 | + for (const auto& cfg : procMemConfigCache_) { |
| 927 | - return; | 159 | + if (cfg.isPid && cfg.identifier == idStr) { |
| 928 | - } | ||
| 929 | - UBSE_LOG_INFO << "RecoverSingleDebtInfo usrInfo: pluginId=" << static_cast<int>(usrInfo.pluginId) | ||
| 930 | - << ", pid=" << usrInfo.pid << ", startTime=" << usrInfo.startTime << ", name=" << debtInfo.name | ||
| 931 | - << ", size=" << debtInfo.size << ", remoteNumaId=" << debtInfo.remoteNumaId; | ||
| 932 | - if (usrInfo.pluginId != def::UsrInfoPluginType::PROCESS_MEM) { | ||
| 933 | - UBSE_LOG_DEBUG << "skip non-process_mem debt, pluginId=" << static_cast<int>(usrInfo.pluginId) | ||
| 934 | - << ", name=" << debtInfo.name; | ||
| 935 | - return; | ||
| 936 | - } | ||
| 937 | - | ||
| 938 | - auto pidIt = pidInfoMap.find(usrInfo.pid); | ||
| 939 | - if (pidIt == pidInfoMap.end() || pidIt->second.startTime != usrInfo.startTime) { | ||
| 940 | - UBSE_LOG_WARN << "pid not found or startTime mismatch, return memory, name=" << debtInfo.name; | ||
| 941 | - if (pidIt != pidInfoMap.end()) { | ||
| 942 | - pidInfoMap.erase(pidIt); | ||
| 943 | - } | ||
| 944 | - exceptionHandleExecutor->Execute([name = debtInfo.name]() { ExceptionMemoryHandle(name); }); | ||
| 945 | - return; | ||
| 946 | - } | ||
| 947 | - | ||
| 948 | - def::DebtInfo newDebtInfo{}; | ||
| 949 | - newDebtInfo.status = def::BorrowStatus::COMPLETED; | ||
| 950 | - newDebtInfo.numaDesc.name = debtInfo.name; | ||
| 951 | - newDebtInfo.numaDesc.numaId = debtInfo.remoteNumaId; | ||
| 952 | - newDebtInfo.numaDesc.size = debtInfo.size; | ||
| 953 | - if (memcpy_s(newDebtInfo.numaDesc.usrInfo, ubse::mem::controller::UBSE_MAX_USR_INFO_LEN, debtInfo.usrInfo, | ||
| 954 | - ubse::mem::controller::UBSE_MAX_USR_INFO_LEN) != EOK) { | ||
| 955 | - UBSE_LOG_ERROR << "memcpy_s newDebtInfo usrInfo failed"; | ||
| 956 | - return; | ||
| 957 | - } | ||
| 958 | - auto& borrowInfo = pidIt->second.memBorrowInfo; | ||
| 959 | - borrowInfo.remoteNumaId = debtInfo.remoteNumaId; | ||
| 960 | - if (!debtInfo.borrowSocketIdList.empty()) { | ||
| 961 | - borrowInfo.importSocketId = debtInfo.borrowSocketIdList[0]; | ||
| 962 | - } | ||
| 963 | - if (!debtInfo.lentNodeId.empty()) { | ||
| 964 | - borrowInfo.exportSlotId = std::stoi(debtInfo.lentNodeId); | ||
| 965 | - newDebtInfo.numaDesc.exportNode.slotId = static_cast<uint32_t>(borrowInfo.exportSlotId); | ||
| 966 | - } | ||
| 967 | - | ||
| 968 | - borrowInfo.debtInfos[debtInfo.name] = newDebtInfo; | ||
| 969 | - UBSE_LOG_INFO << "Recover debt success, pid=" << usrInfo.pid << ", name=" << debtInfo.name | ||
| 970 | - << ", remoteNumaId=" << debtInfo.remoteNumaId << ", exportSlotId=" << borrowInfo.exportSlotId; | ||
| 971 | -} | ||
| 972 | -} // namespace | ||
| 973 | - | ||
| 974 | -void ProcessMemPidInfoManager::RecoverAllDebtInfoData() | ||
| 975 | -{ | ||
| 976 | - UBSE_LOG_INFO << "RecoverAllDebtInfoData begin"; | ||
| 977 | - auto curNodeId = ubse::nodeController::UbseNodeController::GetInstance().GetCurrentNodeId(); | ||
| 978 | - std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo> debtInfos{}; | ||
| 979 | - constexpr int retryTime = 10; | ||
| 980 | - constexpr int retryIntervalSec = 2; | ||
| 981 | - int remainRetry = retryTime; | ||
| 982 | - auto ret = ubse::mem::controller::UbseGetNumaMemDebtInfoWithNode(curNodeId, debtInfos); | ||
| 983 | - while (ret != UBSE_OK && remainRetry > 0) { | ||
| 984 | - UBSE_LOG_WARN << "RecoverAllDebtInfoData retry, ret=" << ret << ", remainRetry=" << remainRetry; | ||
| 985 | - sleep(retryIntervalSec); | ||
| 986 | - debtInfos.clear(); | ||
| 987 | - ret = ubse::mem::controller::UbseGetNumaMemDebtInfoWithNode(curNodeId, debtInfos); | ||
| 988 | - --remainRetry; | ||
| 989 | - } | ||
| 990 | - if (ret != UBSE_OK) { | ||
| 991 | - UBSE_LOG_ERROR << "RecoverAllDebtInfoData failed after retry, ret=" << ret | ||
| 992 | - << ", debtInfos.size=" << debtInfos.size(); | ||
| 993 | - } | ||
| 994 | - UBSE_LOG_INFO << "UbseGetNumaMemDebtInfoWithNode success, total debtInfos=" << debtInfos.size(); | ||
| 995 | - | ||
| 996 | - uint32_t matchedCount = 0; | ||
| 997 | - uint32_t recoveredCount = 0; | ||
| 998 | - { | ||
| 999 | - std::unique_lock<std::shared_mutex> lock(pidInfoMutex); | ||
| 1000 | - UBSE_LOG_INFO << "current pidInfoMap size=" << pidInfoMap.size(); | ||
| 1001 | - for (const auto& debtInfo : debtInfos) { | ||
| 1002 | - if (debtInfo.borrowNodeId != curNodeId) { | ||
| 1003 | - continue; | ||
| 1004 | - } | ||
| 1005 | - if (!IsProcessMemDebt(debtInfo)) { | ||
| 1006 | - continue; | ||
| 1007 | - } | ||
| 1008 | - ++matchedCount; | ||
| 1009 | - UBSE_LOG_INFO << "recovering debt: name=" << debtInfo.name << ", size=" << debtInfo.size | ||
| 1010 | - << ", remoteNumaId=" << debtInfo.remoteNumaId; | ||
| 1011 | - ++recoveredCount; | ||
| 1012 | - RecoverSingleDebtInfo(pidInfoMap, debtInfo, exceptionHandleExecutor); | ||
| 1013 | - } | ||
| 1014 | - } | ||
| 1015 | - UBSE_LOG_INFO << "RecoverAllDebtInfoData matched=" << matchedCount << ", recovered=" << recoveredCount; | ||
| 1016 | - if (matchedCount == 0 && !debtInfos.empty()) { | ||
| 1017 | - UBSE_LOG_WARN << "no process_mem debts found among " << debtInfos.size() << " total debts"; | ||
| 1018 | - } | ||
| 1019 | - isRecoverCompleted_ = true; | ||
| 1020 | - UBSE_LOG_INFO << "RecoverAllDebtInfoData completed, isRecoverCompleted_=true"; | ||
| 1021 | -} | ||
| 1022 | - | ||
| 1023 | -namespace { | ||
| 1024 | -uint32_t QueryDebtInfoWithRetry(const std::string& nodeId, | ||
| 1025 | - std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo>& debtInfos, | ||
| 1026 | - const char* caller, int maxRetries, bool acceptPartial) | ||
| 1027 | -{ | ||
| 1028 | - constexpr int retryIntervalSec = 2; | ||
| 1029 | - int remainRetry = maxRetries; | ||
| 1030 | - auto ret = ubse::mem::controller::UbseGetNumaMemDebtInfoWithNode(nodeId, debtInfos); | ||
| 1031 | - auto shouldRetry = [acceptPartial](uint32_t r) { | ||
| 1032 | - if (acceptPartial) { | ||
| 1033 | - return r != UBSE_OK && r != UBSE_MEMCONTROLLER_ERROR_PAR_SUCCESS; | ||
| 1034 | - } | ||
| 1035 | - return r != UBSE_OK; | ||
| 1036 | - }; | ||
| 1037 | - while (shouldRetry(ret) && remainRetry > 0) { | ||
| 1038 | - UBSE_LOG_WARN << caller << ": query debts retry, node=" << nodeId << ", ret=" << ret | ||
| 1039 | - << ", remainRetry=" << remainRetry; | ||
| 1040 | - sleep(retryIntervalSec); | ||
| 1041 | - debtInfos.clear(); | ||
| 1042 | - ret = ubse::mem::controller::UbseGetNumaMemDebtInfoWithNode(nodeId, debtInfos); | ||
| 1043 | - --remainRetry; | ||
| 1044 | - } | ||
| 1045 | - return ret; | ||
| 1046 | -} | ||
| 1047 | -} // namespace | ||
| 1048 | - | ||
| 1049 | -uint32_t ProcessMemPidInfoManager::HandleNodeFaultEvent(const std::string& lentNodeId) | ||
| 1050 | -{ | ||
| 1051 | - UBSE_LOG_INFO << "HandleNodeFaultEvent: lentNodeId=" << lentNodeId; | ||
| 1052 | - | ||
| 1053 | - std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo> debtInfos{}; | ||
| 1054 | - auto ret = QueryDebtInfoWithRetry(lentNodeId, debtInfos, "HandleNodeFaultEvent", 3, false); | ||
| 1055 | - if (ret != UBSE_OK) { | ||
| 1056 | - UBSE_LOG_ERROR << "HandleNodeFaultEvent: query debts failed after retry, ret=" << ret; | ||
| 1057 | - return ret; | ||
| 1058 | - } | ||
| 1059 | - UBSE_LOG_INFO << "HandleNodeFaultEvent: query debts done, ret=" << ret << ", total=" << debtInfos.size(); | ||
| 1060 | - | ||
| 1061 | - // 筛选出本插件在该故障借出节点上的受影响的 PID | ||
| 1062 | - std::unordered_set<pid_t> affectedPids; | ||
| 1063 | - for (const auto& debtInfo : debtInfos) { | ||
| 1064 | - if (debtInfo.lentNodeId != lentNodeId || !IsProcessMemDebt(debtInfo)) { | ||
| 1065 | - continue; | ||
| 1066 | - } | ||
| 1067 | - def::ProcessMemUsrInfo usrInfo{}; | ||
| 1068 | - if (memcpy_s(&usrInfo, sizeof(usrInfo), debtInfo.usrInfo, sizeof(usrInfo)) != EOK) { | ||
| 1069 | - UBSE_LOG_WARN << "HandleNodeFaultEvent: memcpy_s usrInfo failed for debt " << debtInfo.name; | ||
| 1070 | - continue; | ||
| 1071 | - } | ||
| 1072 | - if (usrInfo.pid > 0) { | ||
| 1073 | - affectedPids.insert(usrInfo.pid); | ||
| 1074 | - } | ||
| 1075 | - } | ||
| 1076 | - | ||
| 1077 | - if (affectedPids.empty()) { | ||
| 1078 | - UBSE_LOG_INFO << "HandleNodeFaultEvent: no affected PIDs for lentNodeId=" << lentNodeId; | ||
| 1079 | - return UBSE_OK; | ||
| 1080 | - } | ||
| 1081 | - | ||
| 1082 | - // 标记受影响的 PID 为 FAULT 状态 | ||
| 1083 | - { | ||
| 1084 | - std::unique_lock<std::shared_mutex> lock(pidInfoMutex); | ||
| 1085 | - for (auto pid : affectedPids) { | ||
| 1086 | - auto pidIt = pidInfoMap.find(pid); | ||
| 1087 | - if (pidIt != pidInfoMap.end()) { | ||
| 1088 | - pidIt->second.processStatus = def::ProcessStatus::FAULT; | ||
| 1089 | - } else { | ||
| 1090 | - UBSE_LOG_WARN << "HandleNodeFaultEvent: pid=" << pid << " not found in pidInfoMap"; | ||
| 1091 | - } | ||
| 1092 | - } | ||
| 1093 | - faultedLentNodes_[lentNodeId] = std::move(affectedPids); | ||
| 1094 | - } | ||
| 1095 | - return UBSE_OK; | ||
| 1096 | -} | ||
| 1097 | - | ||
| 1098 | -bool IsNodeRecovered(const std::string& lentNodeId) | ||
| 1099 | -{ | ||
| 1100 | - // 条件1: 账本中已无该节点的 ProcessMem 债务 | ||
| 1101 | - std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo> debtInfos{}; | ||
| 1102 | - auto ret = QueryDebtInfoWithRetry(lentNodeId, debtInfos, "IsNodeRecovered", 2, true); | ||
| 1103 | - if (ret == UBSE_OK) { | ||
| 1104 | - bool hasDebt = false; | ||
| 1105 | - for (const auto& debtInfo : debtInfos) { | ||
| 1106 | - if (debtInfo.lentNodeId == lentNodeId && IsProcessMemDebt(debtInfo)) { | ||
| 1107 | - hasDebt = true; | ||
| 1108 | - break; | ||
| 1109 | - } | ||
| 1110 | - } | ||
| 1111 | - if (!hasDebt) { | ||
| 1112 | return true; | 160 | return true; |
| 1113 | } | 161 | } |
| 1114 | } | 162 | } |
| 1115 | - | 163 | + return false; |
| 1116 | - // 条件2: 节点集群状态已恢复为 WORKING | ||
| 1117 | - auto nodeInfo = ubse::nodeController::UbseNodeController::GetInstance().GetNodeById(lentNodeId); | ||
| 1118 | - return nodeInfo.clusterState == ubse::nodeController::UbseNodeClusterState::UBSE_NODE_WORKING; | ||
| 1119 | } | 164 | } |
| 1120 | 165 | ||
| 1121 | -void RestoreFaultPids(const std::vector<std::string>& recoveredNodes, | 166 | +void ProcessMemPidInfoManager::CleanupStalePidConfigs() |
| 1122 | - std::unordered_map<std::string, std::unordered_set<pid_t>>& faultedLentNodes, | ||
| 1123 | - std::unordered_map<pid_t, def::ProcessMemPidInfo>& pidInfoMap) | ||
| 1124 | { | 167 | { |
| 1125 | - for (const auto& nodeId : recoveredNodes) { | 168 | + std::vector<def::ProcessMemNewConfigInfo> snapshot; |
| 1126 | - auto nodeIt = faultedLentNodes.find(nodeId); | 169 | + { |
| 1127 | - if (nodeIt == faultedLentNodes.end()) { | 170 | + std::shared_lock<std::shared_mutex> lock(procMemConfigMutex_); |
| 1128 | - continue; | 171 | + snapshot = procMemConfigCache_; |
| 172 | + } | ||
| 173 | + | ||
| 174 | + size_t removed = 0; | ||
| 175 | + for (const auto& cfg : snapshot) { | ||
| 176 | + if (CleanupStalePidConfig(cfg)) { | ||
| 177 | + ++removed; | ||
| 1129 | } | 178 | } |
| 1130 | - for (auto pid : nodeIt->second) { | 179 | + } |
| 1131 | - auto pidIt = pidInfoMap.find(pid); | 180 | + |
| 1132 | - if (pidIt != pidInfoMap.end() && pidIt->second.processStatus == def::ProcessStatus::FAULT) { | 181 | + std::vector<std::pair<pid_t, def::FossilPidConfigInfo>> fossils; |
| 1133 | - pidIt->second.processStatus = def::ProcessStatus::IDLE; | 182 | + ProcessMemPidConfigManager::GetAllFossilConfigs(fossils); |
| 1134 | - UBSE_LOG_INFO << "CheckFaultNodesRecovery: pid=" << pid << " restored to IDLE"; | 183 | + size_t fossilRemoved = 0; |
| 184 | + for (const auto& [pid, fossil] : fossils) { | ||
| 185 | + if (CleanupStaleFossil(pid, fossil)) { | ||
| 186 | + ++fossilRemoved; | ||
| 187 | + } | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + if (removed > 0 || fossilRemoved > 0) { | ||
| 191 | + RebuildManagedPidCache(); | ||
| 192 | + } | ||
| 193 | +} | ||
| 194 | + | ||
| 195 | +bool ProcessMemPidInfoManager::CleanupStalePidConfig(const def::ProcessMemNewConfigInfo& cfg) | ||
| 196 | +{ | ||
| 197 | + if (!cfg.isPid) { | ||
| 198 | + return false; | ||
| 199 | + } | ||
| 200 | + auto pidOpt = def::ParsePidFromIdentifier(cfg.identifier); | ||
| 201 | + if (!pidOpt.has_value()) { | ||
| 202 | + return false; | ||
| 203 | + } | ||
| 204 | + pid_t pid = pidOpt.value(); | ||
| 205 | + if (IsRootPid(pid)) { | ||
| 206 | + (void)ProcessMemPidConfigManager::DeleteProcMemConfig(true, cfg.identifier); | ||
| 207 | + EraseConfigFromCache(true, cfg.identifier); | ||
| 208 | + UBSE_LOG_INFO << "[process_mem] init restore: pid=" << pid << " root process (not managed), config removed"; | ||
| 209 | + return true; | ||
| 210 | + } | ||
| 211 | + auto curStartTime = ProcessMemPidConfigManager::GetExactStartTime(pid); | ||
| 212 | + if (curStartTime == 0 || static_cast<uint64_t>(curStartTime) != cfg.startTime) { | ||
| 213 | + (void)ProcessMemPidConfigManager::DeleteProcMemConfig(true, cfg.identifier); | ||
| 214 | + EraseConfigFromCache(true, cfg.identifier); | ||
| 215 | + UBSE_LOG_INFO << "[process_mem] init restore: pid=" << pid | ||
| 216 | + << " abandoned (startTime mismatch: persisted=" << cfg.startTime << " actual=" << curStartTime | ||
| 217 | + << "), config removed"; | ||
| 218 | + return true; | ||
| 219 | + } | ||
| 220 | + return false; | ||
| 221 | +} | ||
| 222 | + | ||
| 223 | +bool ProcessMemPidInfoManager::CleanupStaleFossil(pid_t pid, const def::FossilPidConfigInfo& fossil) | ||
| 224 | +{ | ||
| 225 | + if (IsRootPid(pid)) { | ||
| 226 | + (void)ProcessMemPidConfigManager::DeleteFossilConfig(pid); | ||
| 227 | + UBSE_LOG_INFO << "[process_mem] init restore: fossil pid=" << pid << " root process, config removed"; | ||
| 228 | + return true; | ||
| 229 | + } | ||
| 230 | + auto curStartTime = ProcessMemPidConfigManager::GetExactStartTime(pid); | ||
| 231 | + if (curStartTime == 0 || static_cast<uint64_t>(curStartTime) != fossil.startTime) { | ||
| 232 | + (void)ProcessMemPidConfigManager::DeleteFossilConfig(pid); | ||
| 233 | + UBSE_LOG_INFO << "[process_mem] init restore: fossil pid=" << pid | ||
| 234 | + << " abandoned (startTime mismatch: persisted=" << fossil.startTime << " actual=" << curStartTime | ||
| 235 | + << "), config removed"; | ||
| 236 | + return true; | ||
| 237 | + } | ||
| 238 | + return false; | ||
| 239 | +} | ||
| 240 | + | ||
| 241 | +uint32_t ProcessMemPidInfoManager::ValidateProcMemTarget(const def::ProcessMemNewConfigInfo& config, | ||
| 242 | + uint64_t& outStartTime) | ||
| 243 | +{ | ||
| 244 | + static constexpr uint64_t kProcMemSizeMin = 128ULL * 1024 * 1024; | ||
| 245 | + static constexpr uint64_t kProcMemSizeMax = 32ULL * 1024 * 1024 * 1024; | ||
| 246 | + static constexpr size_t kProcNameMaxLen = 15; | ||
| 247 | + | ||
| 248 | + if (config.maxMemory < kProcMemSizeMin || config.maxMemory > kProcMemSizeMax) { | ||
| 249 | + UBSE_LOG_ERROR << "SetProcMemConfig: maxMemory out of range [128M, 32G]: " << config.maxMemory; | ||
| 250 | + return UBSE_ERR_INVALID_ARG; | ||
| 251 | + } | ||
| 252 | + if (!std::isfinite(config.remoteRatio) || config.remoteRatio < 0.0 || config.remoteRatio > 1.0) { | ||
| 253 | + UBSE_LOG_ERROR << "SetProcMemConfig: remoteRatio out of range [0.0, 1.0]: " << config.remoteRatio; | ||
| 254 | + return UBSE_ERR_INVALID_ARG; | ||
| 255 | + } | ||
| 256 | + if (config.identifier.size() > kProcNameMaxLen) { | ||
| 257 | + UBSE_LOG_ERROR << "SetProcMemConfig: identifier too long (" << config.identifier.size() << " > " | ||
| 258 | + << kProcNameMaxLen << "): " << config.identifier; | ||
| 259 | + return UBSE_ERR_INVALID_ARG; | ||
| 260 | + } | ||
| 261 | + if (config.identifier.find_first_of("/\\\n\r\t") != std::string::npos) { | ||
| 262 | + UBSE_LOG_ERROR << "SetProcMemConfig: identifier contains illegal chars: " << config.identifier; | ||
| 263 | + return UBSE_ERR_INVALID_ARG; | ||
| 264 | + } | ||
| 265 | + if (config.isPid) { | ||
| 266 | + auto pidOpt = def::ParsePidFromIdentifier(config.identifier); | ||
| 267 | + if (!pidOpt.has_value()) { | ||
| 268 | + UBSE_LOG_ERROR << "SetProcMemConfig: invalid PID format: " << config.identifier; | ||
| 269 | + return UBSE_ERR_INVALID_ARG; | ||
| 270 | + } | ||
| 271 | + pid_t pid = pidOpt.value(); | ||
| 272 | + auto startTime = ProcessMemPidConfigManager::GetExactStartTime(pid); | ||
| 273 | + if (startTime == 0) { | ||
| 274 | + UBSE_LOG_ERROR << "SetProcMemConfig: PID " << pid << " does not exist"; | ||
| 275 | + return UBSE_ERR_NOT_EXIST; | ||
| 276 | + } | ||
| 277 | + | ||
| 278 | + if (IsRootPid(pid)) { | ||
| 279 | + UBSE_LOG_ERROR << "SetProcMemConfig: PID " << pid | ||
| 280 | + << " is a root process, not allowed (filter_root_process=true)"; | ||
| 281 | + return UBSE_ERR_INVALID_ARG; | ||
| 282 | + } | ||
| 283 | + | ||
| 284 | + outStartTime = startTime; | ||
| 285 | + } | ||
| 286 | + return UBSE_OK; | ||
| 287 | +} | ||
| 288 | + | ||
| 289 | +uint32_t ProcessMemPidInfoManager::SetProcMemConfig(const def::ProcessMemNewConfigInfo& config) | ||
| 290 | +{ | ||
| 291 | + def::ProcessMemNewConfigInfo mutableConfig = config; | ||
| 292 | + uint64_t startTime = 0; | ||
| 293 | + auto valRet = ValidateProcMemTarget(config, startTime); | ||
| 294 | + if (valRet != UBSE_OK) { | ||
| 295 | + return valRet; | ||
| 296 | + } | ||
| 297 | + if (config.isPid) { | ||
| 298 | + mutableConfig.startTime = startTime; | ||
| 299 | + } | ||
| 300 | + | ||
| 301 | + std::string key = MakeProcMemKey(mutableConfig.isPid, mutableConfig.identifier); | ||
| 302 | + | ||
| 303 | + auto persistRet = ProcessMemPidConfigManager::PersistProcMemConfig(mutableConfig); | ||
| 304 | + if (persistRet != UBSE_OK) { | ||
| 305 | + UBSE_LOG_ERROR << "SetProcMemConfig: persist failed for " << key | ||
| 306 | + << ", ret=" << ubse::log::FormatRetCode(persistRet); | ||
| 307 | + return UBSE_ERROR; | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + bool updated = false; | ||
| 311 | + { | ||
| 312 | + std::unique_lock<std::shared_mutex> lock(procMemConfigMutex_); | ||
| 313 | + for (auto& existing : procMemConfigCache_) { | ||
| 314 | + if (existing.isPid == mutableConfig.isPid && existing.identifier == mutableConfig.identifier) { | ||
| 315 | + existing = mutableConfig; | ||
| 316 | + updated = true; | ||
| 317 | + UBSE_LOG_INFO << "SetProcMemConfig: updated config for " << key | ||
| 318 | + << ", maxMemory=" << mutableConfig.maxMemory | ||
| 319 | + << ", remoteRatio=" << mutableConfig.remoteRatio; | ||
| 320 | + break; | ||
| 1135 | } | 321 | } |
| 1136 | } | 322 | } |
| 1137 | - UBSE_LOG_INFO << "CheckFaultNodesRecovery: fault node " << nodeId << " removed, " << nodeIt->second.size() | 323 | + if (!updated) { |
| 1138 | - << " PIDs recovered"; | 324 | + procMemConfigCache_.push_back(mutableConfig); |
| 1139 | - faultedLentNodes.erase(nodeIt); | 325 | + UBSE_LOG_INFO << "SetProcMemConfig: inserted new config for " << key |
| 326 | + << ", maxMemory=" << mutableConfig.maxMemory << ", remoteRatio=" << mutableConfig.remoteRatio; | ||
| 327 | + } | ||
| 328 | + } | ||
| 329 | + | ||
| 330 | + RebuildManagedPidCache(); | ||
| 331 | + return UBSE_OK; | ||
| 332 | +} | ||
| 333 | + | ||
| 334 | +uint32_t ProcessMemPidInfoManager::RemoveProcMemConfig(bool isPid, const std::string& identifier) | ||
| 335 | +{ | ||
| 336 | + { | ||
| 337 | + std::shared_lock<std::shared_mutex> lock(procMemConfigMutex_); | ||
| 338 | + bool found = false; | ||
| 339 | + for (const auto& cfg : procMemConfigCache_) { | ||
| 340 | + if (cfg.isPid == isPid && cfg.identifier == identifier) { | ||
| 341 | + found = true; | ||
| 342 | + break; | ||
| 343 | + } | ||
| 344 | + } | ||
| 345 | + if (!found) { | ||
| 346 | + UBSE_LOG_INFO << "RemoveProcMemConfig: config not found for " << (isPid ? "pid=" : "name=") << identifier; | ||
| 347 | + return UBSE_ERR_NOT_EXIST; | ||
| 348 | + } | ||
| 349 | + } | ||
| 350 | + | ||
| 351 | + // 持久层删除失败时保持缓存与磁盘一致, 不擦除缓存 | ||
| 352 | + uint32_t deleteRet = isPid ? RemovePidConfigSideEffects(identifier) : RemoveNameConfigSideEffects(identifier); | ||
| 353 | + if (deleteRet != UBSE_OK) { | ||
| 354 | + UBSE_LOG_ERROR << "RemoveProcMemConfig: persistent delete failed for " << (isPid ? "pid=" : "name=") | ||
| 355 | + << identifier << ", keep cache entry, ret=" << ubse::log::FormatRetCode(deleteRet); | ||
| 356 | + return deleteRet; | ||
| 357 | + } | ||
| 358 | + | ||
| 359 | + EraseConfigFromCache(isPid, identifier); | ||
| 360 | + | ||
| 361 | + RebuildManagedPidCache(); | ||
| 362 | + return UBSE_OK; | ||
| 363 | +} | ||
| 364 | + | ||
| 365 | +uint32_t ProcessMemPidInfoManager::RemovePidConfigSideEffects(const std::string& identifier) | ||
| 366 | +{ | ||
| 367 | + auto pidOpt = def::ParsePidFromIdentifier(identifier); | ||
| 368 | + uint32_t ret = ProcessMemPidConfigManager::DeleteProcMemConfig(true, identifier); | ||
| 369 | + if (pidOpt.has_value() && ret == UBSE_OK) { | ||
| 370 | + ret = ProcessMemPidConfigManager::DeleteFossilConfig(pidOpt.value()); | ||
| 371 | + } | ||
| 372 | + if (pidOpt.has_value() && ret == UBSE_OK) { | ||
| 373 | + (void)decision::ProcessMemPidDecision::GetInstance().EnqueuePidReturn(pidOpt.value(), | ||
| 374 | + decision::ReturnScene::EXITED); | ||
| 375 | + } | ||
| 376 | + return ret; | ||
| 377 | +} | ||
| 378 | + | ||
| 379 | +uint32_t ProcessMemPidInfoManager::RemoveNameConfigSideEffects(const std::string& identifier) | ||
| 380 | +{ | ||
| 381 | + std::vector<std::pair<pid_t, def::FossilPidConfigInfo>> fossils; | ||
| 382 | + ProcessMemPidConfigManager::GetAllFossilConfigs(fossils); | ||
| 383 | + std::set<pid_t> affected; | ||
| 384 | + for (const auto& [pid, fossil] : fossils) { | ||
| 385 | + if (fossil.name == identifier) { | ||
| 386 | + affected.insert(pid); | ||
| 387 | + (void)ProcessMemPidConfigManager::DeleteFossilConfig(pid); | ||
| 388 | + } | ||
| 389 | + } | ||
| 390 | + for (const auto& pe : collect::FindPidsByName(identifier)) { | ||
| 391 | + affected.insert(pe.pid); | ||
| 392 | + } | ||
| 393 | + auto ret = ProcessMemPidConfigManager::DeleteProcMemConfig(false, identifier); | ||
| 394 | + if (ret != UBSE_OK) { | ||
| 395 | + return ret; | ||
| 396 | + } | ||
| 397 | + for (pid_t pid : affected) { | ||
| 398 | + if (HasExplicitPidConfig(pid)) { | ||
| 399 | + continue; | ||
| 400 | + } | ||
| 401 | + (void)decision::ProcessMemPidDecision::GetInstance().EnqueuePidReturn(pid, decision::ReturnScene::EXITED); | ||
| 402 | + } | ||
| 403 | + return UBSE_OK; | ||
| 404 | +} | ||
| 405 | + | ||
| 406 | +void ProcessMemPidInfoManager::EraseConfigFromCache(bool isPid, const std::string& identifier) | ||
| 407 | +{ | ||
| 408 | + std::unique_lock<std::shared_mutex> lock(procMemConfigMutex_); | ||
| 409 | + for (auto it = procMemConfigCache_.begin(); it != procMemConfigCache_.end(); ++it) { | ||
| 410 | + if (it->isPid == isPid && it->identifier == identifier) { | ||
| 411 | + procMemConfigCache_.erase(it); | ||
| 412 | + UBSE_LOG_INFO << "RemoveProcMemConfig: removed config for " << (isPid ? "pid=" : "name=") << identifier; | ||
| 413 | + break; | ||
| 414 | + } | ||
| 1140 | } | 415 | } |
| 1141 | } | 416 | } |
| 1142 | 417 | ||
| 1143 | -void ProcessMemPidInfoManager::CheckFaultNodesRecovery() | 418 | +void ProcessMemPidInfoManager::GetAllProcMemConfigs(std::vector<def::ProcessMemNewConfigInfo>& configs) const |
| 1144 | { | 419 | { |
| 1145 | - // 无锁复制故障节点列表,避免在持有锁时调用 Ubse 接口 | 420 | + std::shared_lock<std::shared_mutex> lock(procMemConfigMutex_); |
| 1146 | - std::unordered_map<std::string, std::unordered_set<pid_t>> faultedCopy; | 421 | + configs = procMemConfigCache_; |
| 422 | +} | ||
| 423 | + | ||
| 424 | +def::ProcessMemNewConfigInfo ProcessMemPidInfoManager::GetProcMemConfig(bool isPid, const std::string& identifier) const | ||
| 425 | +{ | ||
| 426 | + std::shared_lock<std::shared_mutex> lock(procMemConfigMutex_); | ||
| 427 | + for (const auto& cfg : procMemConfigCache_) { | ||
| 428 | + if (cfg.isPid == isPid && cfg.identifier == identifier) { | ||
| 429 | + return cfg; | ||
| 430 | + } | ||
| 431 | + } | ||
| 432 | + def::ProcessMemNewConfigInfo emptyCfg{}; | ||
| 433 | + emptyCfg.maxMemory = 0; | ||
| 434 | + return emptyCfg; | ||
| 435 | +} | ||
| 436 | + | ||
| 437 | +void ProcessMemPidInfoManager::RebuildManagedPidCache() | ||
| 438 | +{ | ||
| 439 | + std::vector<def::ProcessMemNewConfigInfo> configSnapshot; | ||
| 1147 | { | 440 | { |
| 1148 | - std::shared_lock<std::shared_mutex> lock(pidInfoMutex); | 441 | + std::shared_lock<std::shared_mutex> configLock(procMemConfigMutex_); |
| 1149 | - if (faultedLentNodes_.empty()) { | 442 | + configSnapshot = procMemConfigCache_; |
| 1150 | - return; | ||
| 1151 | - } | ||
| 1152 | - faultedCopy = faultedLentNodes_; | ||
| 1153 | } | 443 | } |
| 1154 | 444 | ||
| 1155 | - // 逐个检查故障节点是否已恢复 | 445 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); |
| 1156 | - std::vector<std::string> recoveredNodes; | 446 | + std::map<pid_t, def::ManagedPidEntry> oldCache = std::move(managedPidCache_); |
| 1157 | - for (const auto& [lentNodeId, pids] : faultedCopy) { | 447 | + managedPidCache_.clear(); |
| 1158 | - if (pids.empty() || IsNodeRecovered(lentNodeId)) { | 448 | + |
| 1159 | - recoveredNodes.push_back(lentNodeId); | 449 | + RebuildMergePidConfigs(configSnapshot); |
| 1160 | - } | 450 | + std::vector<std::pair<pid_t, const def::ProcessMemNewConfigInfo*>> toFossilize; |
| 451 | + RebuildMergeNameConfigs(configSnapshot, toFossilize); | ||
| 452 | + | ||
| 453 | + lock.unlock(); | ||
| 454 | + for (const auto& [pid, cfg] : toFossilize) { | ||
| 455 | + (void)FossilizePidConfig(pid, cfg->identifier, cfg->maxMemory, cfg->remoteRatio, false); | ||
| 1161 | } | 456 | } |
| 1162 | 457 | ||
| 1163 | - if (recoveredNodes.empty()) { | 458 | + RebuildMergeFossils(); |
| 459 | + std::unique_lock<std::shared_mutex> finalLock(managedPidCacheMutex_); | ||
| 460 | + for (auto& [pid, entry] : managedPidCache_) { | ||
| 461 | + auto it = oldCache.find(pid); | ||
| 462 | + if (it != oldCache.end()) { | ||
| 463 | + entry.borrow = std::move(it->second.borrow); | ||
| 464 | + entry.processStatus = it->second.processStatus; | ||
| 465 | + entry.vmRss = it->second.vmRss; | ||
| 466 | + entry.lastMigrateTime = it->second.lastMigrateTime; | ||
| 467 | + } | ||
| 468 | + } | ||
| 469 | + UBSE_LOG_INFO << "RebuildManagedPidCache: built " << managedPidCache_.size() << " entries"; | ||
| 470 | +} | ||
| 471 | + | ||
| 472 | +void ProcessMemPidInfoManager::RebuildMergePidConfigs(const std::vector<def::ProcessMemNewConfigInfo>& configSnapshot) | ||
| 473 | +{ | ||
| 474 | + for (const auto& cfg : configSnapshot) { | ||
| 475 | + if (!cfg.isPid) { | ||
| 476 | + continue; | ||
| 477 | + } | ||
| 478 | + auto pidOpt = def::ParsePidFromIdentifier(cfg.identifier); | ||
| 479 | + if (!pidOpt.has_value()) { | ||
| 480 | + continue; | ||
| 481 | + } | ||
| 482 | + pid_t pid = pidOpt.value(); | ||
| 483 | + if (IsRootPid(pid)) { | ||
| 484 | + UBSE_LOG_DEBUG << "RebuildManagedPidCache: pid=" << pid << " skip (root process)"; | ||
| 485 | + continue; | ||
| 486 | + } | ||
| 487 | + auto& entry = managedPidCache_[pid]; | ||
| 488 | + entry.pid = pid; | ||
| 489 | + entry.sources |= static_cast<uint8_t>(def::ConfigSource::PID_CONFIG); | ||
| 490 | + entry.maxMemory = cfg.maxMemory; | ||
| 491 | + entry.remoteRatio = cfg.remoteRatio; | ||
| 492 | + } | ||
| 493 | +} | ||
| 494 | + | ||
| 495 | +void ProcessMemPidInfoManager::RebuildMergeNameConfigs( | ||
| 496 | + const std::vector<def::ProcessMemNewConfigInfo>& configSnapshot, | ||
| 497 | + std::vector<std::pair<pid_t, const def::ProcessMemNewConfigInfo*>>& toFossilize) | ||
| 498 | +{ | ||
| 499 | + for (const auto& cfg : configSnapshot) { | ||
| 500 | + if (cfg.isPid) { | ||
| 501 | + continue; | ||
| 502 | + } | ||
| 503 | + auto matchedPids = collect::FindPidsByName(cfg.identifier); | ||
| 504 | + for (const auto& pe : matchedPids) { | ||
| 505 | + pid_t pid = pe.pid; | ||
| 506 | + if (IsRootPid(pid)) { | ||
| 507 | + continue; | ||
| 508 | + } | ||
| 509 | + auto& entry = managedPidCache_[pid]; | ||
| 510 | + bool isNew = (entry.pid == 0 && entry.sources == 0); | ||
| 511 | + entry.pid = pid; | ||
| 512 | + entry.sources |= static_cast<uint8_t>(def::ConfigSource::NAME_CONFIG); | ||
| 513 | + entry.nameConfigName = cfg.identifier; | ||
| 514 | + if (!(entry.sources & static_cast<uint8_t>(def::ConfigSource::PID_CONFIG))) { | ||
| 515 | + entry.maxMemory = cfg.maxMemory; | ||
| 516 | + entry.remoteRatio = cfg.remoteRatio; | ||
| 517 | + } | ||
| 518 | + if (isNew) { | ||
| 519 | + toFossilize.emplace_back(pid, &cfg); | ||
| 520 | + } | ||
| 521 | + } | ||
| 522 | + } | ||
| 523 | +} | ||
| 524 | + | ||
| 525 | +void ProcessMemPidInfoManager::RebuildMergeFossils() | ||
| 526 | +{ | ||
| 527 | + std::vector<std::pair<pid_t, def::FossilPidConfigInfo>> fossils; | ||
| 528 | + ProcessMemPidConfigManager::GetAllFossilConfigs(fossils); | ||
| 529 | + for (const auto& [pid, fossil] : fossils) { | ||
| 530 | + std::unique_lock<std::shared_mutex> cacheLock(managedPidCacheMutex_); | ||
| 531 | + if (managedPidCache_.find(pid) != managedPidCache_.end()) { | ||
| 532 | + continue; | ||
| 533 | + } | ||
| 534 | + if (IsRootPid(pid)) { | ||
| 535 | + cacheLock.unlock(); | ||
| 536 | + (void)ProcessMemPidConfigManager::DeleteFossilConfig(pid); | ||
| 537 | + UBSE_LOG_INFO << "[process_mem] init restore: fossil pid=" << pid << " root process, fossil config removed"; | ||
| 538 | + continue; | ||
| 539 | + } | ||
| 540 | + def::ManagedPidEntry entry{}; | ||
| 541 | + entry.pid = pid; | ||
| 542 | + if (!fossil.name.empty()) { | ||
| 543 | + entry.sources = static_cast<uint8_t>(def::ConfigSource::NAME_CONFIG); | ||
| 544 | + entry.nameConfigName = fossil.name; | ||
| 545 | + } else { | ||
| 546 | + entry.sources = static_cast<uint8_t>(def::ConfigSource::PID_CONFIG); | ||
| 547 | + } | ||
| 548 | + entry.maxMemory = fossil.maxMemory; | ||
| 549 | + entry.remoteRatio = fossil.remoteRatio; | ||
| 550 | + managedPidCache_[pid] = entry; | ||
| 551 | + } | ||
| 552 | +} | ||
| 553 | + | ||
| 554 | +void ProcessMemPidInfoManager::AddNameSourceToManagedPid(pid_t pid, const std::string& name, uint64_t maxMemory, | ||
| 555 | + double remoteRatio) | ||
| 556 | +{ | ||
| 557 | + if (IsRootPid(pid)) { | ||
| 1164 | return; | 558 | return; |
| 1165 | } | 559 | } |
| 1166 | - | 560 | + bool isNew = false; |
| 1167 | - // 恢复受影响的 PID 为 IDLE 状态 | ||
| 1168 | { | 561 | { |
| 1169 | - std::unique_lock<std::shared_mutex> lock(pidInfoMutex); | 562 | + std::shared_lock<std::shared_mutex> lock(managedPidCacheMutex_); |
| 1170 | - RestoreFaultPids(recoveredNodes, faultedLentNodes_, pidInfoMap); | 563 | + isNew = (managedPidCache_.find(pid) == managedPidCache_.end()); |
| 564 | + } | ||
| 565 | + if (isNew) { | ||
| 566 | + (void)FossilizePidConfig(pid, name, maxMemory, remoteRatio, false); | ||
| 567 | + } | ||
| 568 | + | ||
| 569 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 570 | + auto it = managedPidCache_.find(pid); | ||
| 571 | + if (it == managedPidCache_.end()) { | ||
| 572 | + def::ManagedPidEntry entry{}; | ||
| 573 | + entry.pid = pid; | ||
| 574 | + entry.sources = static_cast<uint8_t>(def::ConfigSource::NAME_CONFIG); | ||
| 575 | + entry.nameConfigName = name; | ||
| 576 | + entry.maxMemory = maxMemory; | ||
| 577 | + entry.remoteRatio = remoteRatio; | ||
| 578 | + managedPidCache_[pid] = entry; | ||
| 579 | + } else { | ||
| 580 | + it->second.sources |= static_cast<uint8_t>(def::ConfigSource::NAME_CONFIG); | ||
| 581 | + it->second.nameConfigName = name; | ||
| 582 | + if (!(it->second.sources & static_cast<uint8_t>(def::ConfigSource::PID_CONFIG))) { | ||
| 583 | + it->second.maxMemory = maxMemory; | ||
| 584 | + it->second.remoteRatio = remoteRatio; | ||
| 585 | + } | ||
| 1171 | } | 586 | } |
| 1172 | } | 587 | } |
| 1173 | 588 | ||
| 1174 | -} // namespace process_mem::manager | 589 | +void ProcessMemPidInfoManager::AddChildSourceToManagedPid(pid_t childPid, pid_t parentPid, uint64_t maxMemory, |
| 590 | + double remoteRatio) | ||
| 591 | +{ | ||
| 592 | + if (IsRootPid(childPid)) { | ||
| 593 | + return; | ||
| 594 | + } | ||
| 595 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 596 | + auto it = managedPidCache_.find(childPid); | ||
| 597 | + if (it == managedPidCache_.end()) { | ||
| 598 | + lock.unlock(); | ||
| 599 | + uint32_t fosRet = FossilizePidConfig(childPid, "", maxMemory, remoteRatio, true); | ||
| 600 | + lock.lock(); | ||
| 601 | + if (managedPidCache_.find(childPid) != managedPidCache_.end()) { | ||
| 602 | + return; // 并发期间已被其他路径纳入管理,保留既有状态 | ||
| 603 | + } | ||
| 604 | + def::ManagedPidEntry entry{}; | ||
| 605 | + entry.pid = childPid; | ||
| 606 | + entry.parentPid = parentPid; | ||
| 607 | + entry.isChild = true; | ||
| 608 | + if (fosRet == UBSE_OK) { | ||
| 609 | + entry.sources = static_cast<uint8_t>(def::ConfigSource::PID_CONFIG); | ||
| 610 | + entry.maxMemory = maxMemory; | ||
| 611 | + entry.remoteRatio = remoteRatio; | ||
| 612 | + } | ||
| 613 | + managedPidCache_[childPid] = entry; | ||
| 614 | + } else { | ||
| 615 | + bool changed = (it->second.maxMemory != maxMemory || it->second.remoteRatio != remoteRatio); | ||
| 616 | + it->second.parentPid = parentPid; | ||
| 617 | + it->second.isChild = true; | ||
| 618 | + it->second.maxMemory = maxMemory; | ||
| 619 | + it->second.remoteRatio = remoteRatio; | ||
| 620 | + it->second.sources |= static_cast<uint8_t>(def::ConfigSource::PID_CONFIG); | ||
| 621 | + if (changed) { | ||
| 622 | + lock.unlock(); | ||
| 623 | + FossilizePidConfig(childPid, "", maxMemory, remoteRatio, true); | ||
| 624 | + } | ||
| 625 | + } | ||
| 626 | +} | ||
| 627 | + | ||
| 628 | +void ProcessMemPidInfoManager::RemoveManagedPidEntry(pid_t pid) | ||
| 629 | +{ | ||
| 630 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 631 | + managedPidCache_.erase(pid); | ||
| 632 | +} | ||
| 633 | + | ||
| 634 | +void ProcessMemPidInfoManager::RemovePidSourceFromManagedPid(pid_t pid) | ||
| 635 | +{ | ||
| 636 | + std::string fallbackName; | ||
| 637 | + { | ||
| 638 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 639 | + auto it = managedPidCache_.find(pid); | ||
| 640 | + if (it == managedPidCache_.end()) { | ||
| 641 | + return; | ||
| 642 | + } | ||
| 643 | + | ||
| 644 | + it->second.sources &= ~static_cast<uint8_t>(def::ConfigSource::PID_CONFIG); | ||
| 645 | + | ||
| 646 | + if (!(it->second.sources & static_cast<uint8_t>(def::ConfigSource::NAME_CONFIG))) { | ||
| 647 | + managedPidCache_.erase(it); | ||
| 648 | + return; | ||
| 649 | + } | ||
| 650 | + fallbackName = it->second.nameConfigName; | ||
| 651 | + } | ||
| 652 | + | ||
| 653 | + std::vector<def::ProcessMemNewConfigInfo> configSnapshot; | ||
| 654 | + { | ||
| 655 | + std::shared_lock<std::shared_mutex> configLock(procMemConfigMutex_); | ||
| 656 | + configSnapshot = procMemConfigCache_; | ||
| 657 | + } | ||
| 658 | + for (const auto& cfg : configSnapshot) { | ||
| 659 | + if (!cfg.isPid && cfg.identifier == fallbackName) { | ||
| 660 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 661 | + auto it = managedPidCache_.find(pid); | ||
| 662 | + if (it != managedPidCache_.end()) { | ||
| 663 | + it->second.maxMemory = cfg.maxMemory; | ||
| 664 | + it->second.remoteRatio = cfg.remoteRatio; | ||
| 665 | + } | ||
| 666 | + break; | ||
| 667 | + } | ||
| 668 | + } | ||
| 669 | +} | ||
| 670 | + | ||
| 671 | +void ProcessMemPidInfoManager::UpdateManagedPidVmRssBatch(const collect::PidCollectInfoMap& collectInfo) | ||
| 672 | +{ | ||
| 673 | + std::vector<pid_t> reusedPids; | ||
| 674 | + { | ||
| 675 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 676 | + for (const auto& [pid, entry] : collectInfo.entries) { | ||
| 677 | + auto it = managedPidCache_.find(pid); | ||
| 678 | + if (it == managedPidCache_.end()) { | ||
| 679 | + continue; | ||
| 680 | + } | ||
| 681 | + it->second.vmRss = entry.vmRssKb * 1024; | ||
| 682 | + if (it->second.startTime == 0) { | ||
| 683 | + it->second.startTime = ProcessMemPidConfigManager::GetExactStartTime(pid); | ||
| 684 | + continue; | ||
| 685 | + } | ||
| 686 | + const auto& borrow = it->second.borrow; | ||
| 687 | + if (it->second.processStatus == def::ProcessStatus::IDLE && borrow.slots.empty() && | ||
| 688 | + borrow.currentRemote == 0 && borrow.remoteNumaMigrated.empty()) { | ||
| 689 | + continue; | ||
| 690 | + } | ||
| 691 | + uint64_t curStart = ProcessMemPidConfigManager::GetExactStartTime(pid); | ||
| 692 | + if (curStart == 0 || curStart == it->second.startTime) { | ||
| 693 | + continue; | ||
| 694 | + } | ||
| 695 | + reusedPids.push_back(pid); | ||
| 696 | + UBSE_LOG_WARN << "[process_mem] pid=" << pid << " reused (startTime " << it->second.startTime << " -> " | ||
| 697 | + << curStart << "), reset stale borrow ledger for new process"; | ||
| 698 | + it->second.borrow = def::BorrowState{}; | ||
| 699 | + it->second.processStatus = def::ProcessStatus::IDLE; | ||
| 700 | + it->second.lastMigrateTime = {}; | ||
| 701 | + it->second.startTime = curStart; | ||
| 702 | + } | ||
| 703 | + } | ||
| 704 | + for (pid_t pid : reusedPids) { | ||
| 705 | + (void)decision::ProcessMemPidDecision::GetInstance().EnqueuePidReturn(pid, decision::ReturnScene::EXITED); | ||
| 706 | + } | ||
| 707 | +} | ||
| 708 | + | ||
| 709 | +void RebuildRemoteNumaMigrated(def::BorrowState& borrow) | ||
| 710 | +{ | ||
| 711 | + borrow.remoteNumaMigrated.clear(); | ||
| 712 | + for (const auto& s : borrow.slots) { | ||
| 713 | + if (s.capacity == 0 || s.status != def::BorrowSlotStatus::COMPLETED || | ||
| 714 | + s.returnStatus == def::ReturnStatus::RETURNING || s.remoteNumaId < 0) { | ||
| 715 | + continue; | ||
| 716 | + } | ||
| 717 | + borrow.remoteNumaMigrated[s.remoteNumaId] += s.migratedBytes; | ||
| 718 | + } | ||
| 719 | +} | ||
| 720 | + | ||
| 721 | +void ProcessMemPidInfoManager::UpdateManagedPidBorrowState(pid_t pid, const def::BorrowState& borrow, | ||
| 722 | + def::ProcessStatus status) | ||
| 723 | +{ | ||
| 724 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 725 | + auto it = managedPidCache_.find(pid); | ||
| 726 | + if (it == managedPidCache_.end()) { | ||
| 727 | + return; | ||
| 728 | + } | ||
| 729 | + it->second.borrow = borrow; | ||
| 730 | + it->second.processStatus = status; | ||
| 731 | + RebuildRemoteNumaMigrated(it->second.borrow); | ||
| 732 | +} | ||
| 733 | + | ||
| 734 | +void ProcessMemPidInfoManager::UpdateManagedPidBorrowStateAtomic( | ||
| 735 | + pid_t pid, const std::function<void(def::BorrowState&, def::ProcessStatus&)>& mutate) | ||
| 736 | +{ | ||
| 737 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 738 | + auto it = managedPidCache_.find(pid); | ||
| 739 | + if (it == managedPidCache_.end()) { | ||
| 740 | + return; | ||
| 741 | + } | ||
| 742 | + def::BorrowState updated = it->second.borrow; | ||
| 743 | + def::ProcessStatus newStatus = it->second.processStatus; | ||
| 744 | + mutate(updated, newStatus); | ||
| 745 | + it->second.borrow = updated; | ||
| 746 | + it->second.processStatus = newStatus; | ||
| 747 | + RebuildRemoteNumaMigrated(it->second.borrow); | ||
| 748 | +} | ||
| 749 | + | ||
| 750 | +uint32_t ProcessMemPidInfoManager::UpdateManagedPidSlotReturned(pid_t pid, const std::string& debtId, uint64_t amount) | ||
| 751 | +{ | ||
| 752 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 753 | + auto it = managedPidCache_.find(pid); | ||
| 754 | + if (it == managedPidCache_.end()) { | ||
| 755 | + return UBSE_ERR_NOT_EXIST; | ||
| 756 | + } | ||
| 757 | + | ||
| 758 | + auto& borrow = it->second.borrow; | ||
| 759 | + auto slotIt = std::find_if(borrow.slots.begin(), borrow.slots.end(), | ||
| 760 | + [&debtId](const def::BorrowSlot& s) { return s.debtId == debtId; }); | ||
| 761 | + if (slotIt == borrow.slots.end()) { | ||
| 762 | + return UBSE_ERR_NOT_EXIST; | ||
| 763 | + } | ||
| 764 | + | ||
| 765 | + borrow.currentRemote = (borrow.currentRemote >= amount) ? (borrow.currentRemote - amount) : 0; | ||
| 766 | + borrow.slots.erase(slotIt); | ||
| 767 | + | ||
| 768 | + if (borrow.slots.empty()) { | ||
| 769 | + it->second.processStatus = def::ProcessStatus::IDLE; | ||
| 770 | + } | ||
| 771 | + return UBSE_OK; | ||
| 772 | +} | ||
| 773 | + | ||
| 774 | +uint32_t ProcessMemPidInfoManager::UpdateManagedPidSlotReturnStatus(pid_t pid, const std::string& debtId, | ||
| 775 | + def::ReturnStatus status) | ||
| 776 | +{ | ||
| 777 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 778 | + auto it = managedPidCache_.find(pid); | ||
| 779 | + if (it == managedPidCache_.end()) { | ||
| 780 | + return UBSE_ERR_NOT_EXIST; | ||
| 781 | + } | ||
| 782 | + | ||
| 783 | + auto slotIt = std::find_if(it->second.borrow.slots.begin(), it->second.borrow.slots.end(), | ||
| 784 | + [&debtId](const def::BorrowSlot& s) { return s.debtId == debtId; }); | ||
| 785 | + if (slotIt == it->second.borrow.slots.end()) { | ||
| 786 | + return UBSE_ERR_NOT_EXIST; | ||
| 787 | + } | ||
| 788 | + | ||
| 789 | + slotIt->returnStatus = status; | ||
| 790 | + return UBSE_OK; | ||
| 791 | +} | ||
| 792 | + | ||
| 793 | +void ProcessMemPidInfoManager::ResetSlotByDebtName(const std::string& debtId) | ||
| 794 | +{ | ||
| 795 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 796 | + for (auto& [pid, entry] : managedPidCache_) { | ||
| 797 | + auto slotIt = std::find_if(entry.borrow.slots.begin(), entry.borrow.slots.end(), | ||
| 798 | + [&debtId](const def::BorrowSlot& s) { return s.debtId == debtId; }); | ||
| 799 | + if (slotIt != entry.borrow.slots.end()) { | ||
| 800 | + slotIt->returnStatus = def::ReturnStatus::NONE; | ||
| 801 | + UBSE_LOG_INFO << "[process_mem] reset slot pid=" << pid << " debt_id=" << debtId | ||
| 802 | + << " (debt vanished, clear RETURNING)"; | ||
| 803 | + } | ||
| 804 | + } | ||
| 805 | +} | ||
| 806 | + | ||
| 807 | +void ProcessMemPidInfoManager::UpdateManagedPidLastMigrateTime(pid_t pid) | ||
| 808 | +{ | ||
| 809 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 810 | + auto it = managedPidCache_.find(pid); | ||
| 811 | + if (it == managedPidCache_.end()) { | ||
| 812 | + return; | ||
| 813 | + } | ||
| 814 | + it->second.lastMigrateTime = std::chrono::steady_clock::now(); | ||
| 815 | +} | ||
| 816 | + | ||
| 817 | +void ProcessMemPidInfoManager::UpdateManagedPidStatus(pid_t pid, def::ProcessStatus status) | ||
| 818 | +{ | ||
| 819 | + std::unique_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 820 | + auto it = managedPidCache_.find(pid); | ||
| 821 | + if (it == managedPidCache_.end()) { | ||
| 822 | + return; | ||
| 823 | + } | ||
| 824 | + it->second.processStatus = status; | ||
| 825 | +} | ||
| 826 | + | ||
| 827 | +std::map<pid_t, def::ManagedPidEntry> ProcessMemPidInfoManager::GetManagedPidCacheSnapshot() const | ||
| 828 | +{ | ||
| 829 | + std::shared_lock<std::shared_mutex> lock(managedPidCacheMutex_); | ||
| 830 | + return managedPidCache_; | ||
| 831 | +} | ||
| 832 | + | ||
| 833 | +void ProcessMemPidInfoManager::RebalanceRemoteCheck() | ||
| 834 | +{ | ||
| 835 | + auto snapshot = GetManagedPidCacheSnapshot(); | ||
| 836 | + size_t migratedBack = 0; | ||
| 837 | + | ||
| 838 | + for (const auto& [pid, entry] : snapshot) { | ||
| 839 | + if (RebalancePidRemote(pid, entry)) { | ||
| 840 | + ++migratedBack; | ||
| 841 | + } | ||
| 842 | + } | ||
| 843 | + | ||
| 844 | + if (migratedBack > 0) { | ||
| 845 | + UBSE_LOG_INFO << "[process_mem] rebalance done, back=" << migratedBack; | ||
| 846 | + } | ||
| 847 | +} | ||
| 848 | + | ||
| 849 | +uint64_t RebalanceSlotTargetKb(const def::BorrowSlot& slot, uint64_t overage) | ||
| 850 | +{ | ||
| 851 | + if (slot.returnStatus == def::ReturnStatus::RETURNING || slot.remoteNumaId < 0) { | ||
| 852 | + return 0; | ||
| 853 | + } | ||
| 854 | + uint64_t newSize = (slot.migratedBytes > overage) ? (slot.migratedBytes - overage) : 0; | ||
| 855 | + constexpr uint64_t pageSizeKb = 4; | ||
| 856 | + return newSize / 1024 / pageSizeKb * pageSizeKb; | ||
| 857 | +} | ||
| 858 | + | ||
| 859 | +uint64_t RebalanceAggregateTargets(const def::BorrowState& borrow, uint64_t overage, | ||
| 860 | + std::map<int, uint64_t>& numaTargets) | ||
| 861 | +{ | ||
| 862 | + numaTargets.clear(); | ||
| 863 | + uint64_t settled = 0; | ||
| 864 | + uint64_t rest = overage; | ||
| 865 | + for (const auto& slot : borrow.slots) { | ||
| 866 | + if (slot.status != def::BorrowSlotStatus::COMPLETED || slot.returnStatus == def::ReturnStatus::RETURNING || | ||
| 867 | + slot.remoteNumaId < 0) { | ||
| 868 | + continue; | ||
| 869 | + } | ||
| 870 | + uint64_t targetKb = RebalanceSlotTargetKb(slot, rest); | ||
| 871 | + uint64_t reduce = slot.migratedBytes - targetKb * 1024; | ||
| 872 | + if (reduce > rest) { | ||
| 873 | + reduce = rest; | ||
| 874 | + } | ||
| 875 | + settled += reduce; | ||
| 876 | + rest -= reduce; | ||
| 877 | + if (targetKb > 0) { | ||
| 878 | + numaTargets[slot.remoteNumaId] += targetKb; | ||
| 879 | + } | ||
| 880 | + } | ||
| 881 | + for (const auto& slot : borrow.slots) { | ||
| 882 | + if (slot.status == def::BorrowSlotStatus::BORROWING && slot.capacity > 0 && | ||
| 883 | + slot.returnStatus != def::ReturnStatus::RETURNING && slot.remoteNumaId >= 0) { | ||
| 884 | + numaTargets[slot.remoteNumaId] += slot.capacity / 1024; | ||
| 885 | + } | ||
| 886 | + } | ||
| 887 | + return settled; | ||
| 888 | +} | ||
| 889 | + | ||
| 890 | +uint64_t RebalanceApplyTargets(def::BorrowState& borrow, uint64_t overage) | ||
| 891 | +{ | ||
| 892 | + uint64_t settled = 0; | ||
| 893 | + uint64_t rest = overage; | ||
| 894 | + for (auto& slot : borrow.slots) { | ||
| 895 | + if (slot.status != def::BorrowSlotStatus::COMPLETED || slot.returnStatus == def::ReturnStatus::RETURNING || | ||
| 896 | + slot.remoteNumaId < 0) { | ||
| 897 | + continue; | ||
| 898 | + } | ||
| 899 | + uint64_t targetKb = RebalanceSlotTargetKb(slot, rest); | ||
| 900 | + uint64_t reduce = slot.migratedBytes - targetKb * 1024; | ||
| 901 | + slot.migratedBytes = targetKb * 1024; | ||
| 902 | + if (reduce > rest) { | ||
| 903 | + reduce = rest; | ||
| 904 | + } | ||
| 905 | + settled += reduce; | ||
| 906 | + rest -= reduce; | ||
| 907 | + } | ||
| 908 | + return settled; | ||
| 909 | +} | ||
| 910 | + | ||
| 911 | +bool ProcessMemPidInfoManager::RebalancePidRemote(pid_t pid, const def::ManagedPidEntry& entry) | ||
| 912 | +{ | ||
| 913 | + if (entry.remoteRatio <= 0.0 || entry.borrow.currentRemote == 0) { | ||
| 914 | + return false; | ||
| 915 | + } | ||
| 916 | + | ||
| 917 | + uint64_t expectedRemote = static_cast<uint64_t>(entry.vmRss * entry.remoteRatio); | ||
| 918 | + if (entry.borrow.currentRemote <= expectedRemote) { | ||
| 919 | + return false; | ||
| 920 | + } | ||
| 921 | + uint64_t overage = (entry.borrow.currentRemote - expectedRemote) / 4096 * 4096; | ||
| 922 | + if (overage == 0) { | ||
| 923 | + return false; | ||
| 924 | + } | ||
| 925 | + | ||
| 926 | + std::map<int, uint64_t> numaTargets; | ||
| 927 | + uint64_t settled = RebalanceAggregateTargets(entry.borrow, overage, numaTargets); | ||
| 928 | + if (numaTargets.empty() || settled == 0) { | ||
| 929 | + return false; | ||
| 930 | + } | ||
| 931 | + | ||
| 932 | + int ret = DoMigrateOut(pid, numaTargets); | ||
| 933 | + if (ret != 0) { | ||
| 934 | + UBSE_LOG_ERROR << "[process_mem] rebalance pid=" << pid << " failed ret=" << ret | ||
| 935 | + << " expected_remote=" << expectedRemote; | ||
| 936 | + return false; | ||
| 937 | + } | ||
| 938 | + | ||
| 939 | + uint64_t applied = 0; | ||
| 940 | + UpdateManagedPidBorrowStateAtomic(pid, [&](def::BorrowState& newBorrow, def::ProcessStatus& newStatus) { | ||
| 941 | + applied = RebalanceApplyTargets(newBorrow, overage); | ||
| 942 | + newBorrow.currentRemote = (newBorrow.currentRemote >= applied) ? (newBorrow.currentRemote - applied) : 0; | ||
| 943 | + }); | ||
| 944 | + if (applied == 0) { | ||
| 945 | + return false; | ||
| 946 | + } | ||
| 947 | + UBSE_LOG_INFO << "[process_mem] rebalance pid=" << pid << " dir=back old_remote=" << entry.borrow.currentRemote | ||
| 948 | + << " applied_bytes=" << applied; | ||
| 949 | + return true; | ||
| 950 | +} | ||
| 951 | + | ||
| 952 | +void ProcessMemPidInfoManager::VmRssCheckCallBack(const collect::PidCollectInfoMap& collectInfo) | ||
| 953 | +{ | ||
| 954 | + size_t totalEntries = collectInfo.entries.size(); | ||
| 955 | + size_t rootFilteredCount = 0; | ||
| 956 | + | ||
| 957 | + for (const auto& [pid, entry] : collectInfo.entries) { | ||
| 958 | + if (entry.rootFiltered) { | ||
| 959 | + ++rootFilteredCount; | ||
| 960 | + } | ||
| 961 | + } | ||
| 962 | + | ||
| 963 | + UBSE_LOG_DEBUG << "[process_mem] VmRssCheckCallBack: received " << totalEntries | ||
| 964 | + << " entries, root_filtered=" << rootFilteredCount; | ||
| 965 | + | ||
| 966 | + UpdateManagedPidVmRssBatch(collectInfo); | ||
| 967 | + | ||
| 968 | + RebalanceRemoteCheck(); | ||
| 969 | +} | ||
| 970 | + | ||
| 971 | +} // namespace process_mem::manager | ||
| @@ -11,13 +11,11 @@ | |||
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | -#include <atomic> | 14 | +#include <functional> |
| 15 | + | ||
| 15 | 16 | ||
| 16 | - | ||
| 17 | - | ||
| 18 | 17 | ||
| 19 | 18 | ||
| 20 | - | ||
| 21 | 19 | ||
| 22 | 20 | ||
| 23 | namespace process_mem::manager { | 21 | namespace process_mem::manager { |
| @@ -33,52 +31,63 @@ public: | |||
| 33 | return instance; | 31 | return instance; |
| 34 | } | 32 | } |
| 35 | 33 | ||
| 36 | - uint32_t SetPidInfoMap(const def::ProcessMemPidInfo& pidInfo); | 34 | + void VmRssCheckCallBack(const collect::PidCollectInfoMap& collectInfoMap); |
| 37 | 35 | ||
| 38 | - void GetAllPidInfo(std::vector<def::ProcessMemPidInfo>& pidInfos); | 36 | + void RefreshProcMemConfigCache(); |
| 37 | + uint32_t SetProcMemConfig(const def::ProcessMemNewConfigInfo& config); | ||
| 38 | + uint32_t RemoveProcMemConfig(bool isPid, const std::string& identifier); | ||
| 39 | + void GetAllProcMemConfigs(std::vector<def::ProcessMemNewConfigInfo>& configs) const; | ||
| 40 | + def::ProcessMemNewConfigInfo GetProcMemConfig(bool isPid, const std::string& identifier) const; | ||
| 39 | 41 | ||
| 40 | - def::ProcessMemPidInfo GetPidInfoMap(pid_t pid); | 42 | + uint32_t FossilizePidConfig(pid_t pid, const std::string& name, uint64_t maxMemory, double remoteRatio, bool force); |
| 41 | 43 | ||
| 42 | - void TotalMemoryCheckCallBack(const collect::CollectInfoMap& collectInfoMap); | 44 | + bool HasExplicitPidConfig(pid_t pid) const; |
| 43 | 45 | ||
| 44 | - uint32_t UnsetPidInfo(pid_t pid); | 46 | + void CleanupStalePidConfigs(); |
| 45 | 47 | ||
| 46 | - uint32_t PerPidMemoryCheckCallBack(pid_t pid, const std::unordered_map<uint32_t, size_t>& numaMemory); | 48 | + void RebuildManagedPidCache(); |
| 47 | 49 | ||
| 48 | - bool IsRecoverCompleted() const | 50 | + void AddNameSourceToManagedPid(pid_t pid, const std::string& name, uint64_t maxMemory, double remoteRatio); |
| 49 | - { | 51 | + void AddChildSourceToManagedPid(pid_t childPid, pid_t parentPid, uint64_t maxMemory, double remoteRatio); |
| 50 | - return isRecoverCompleted_; | 52 | + void RemoveManagedPidEntry(pid_t pid); |
| 51 | - } | 53 | + void RemovePidSourceFromManagedPid(pid_t pid); |
| 52 | 54 | ||
| 53 | - void RecoverAllDebtInfoData(); | 55 | + void UpdateManagedPidVmRssBatch(const collect::PidCollectInfoMap& collectInfo); |
| 54 | 56 | ||
| 55 | - uint32_t HandleNodeFaultEvent(const std::string& lentNodeId); | 57 | + void RebalanceRemoteCheck(); |
| 56 | 58 | ||
| 57 | - void CheckFaultNodesRecovery(); | 59 | + void UpdateManagedPidBorrowState(pid_t pid, const def::BorrowState& borrow, def::ProcessStatus status); |
| 60 | + void UpdateManagedPidStatus(pid_t pid, def::ProcessStatus status); | ||
| 61 | + void UpdateManagedPidLastMigrateTime(pid_t pid); | ||
| 58 | 62 | ||
| 59 | - void RefreshBorrowInfo(); | 63 | + void UpdateManagedPidBorrowStateAtomic(pid_t pid, |
| 64 | + const std::function<void(def::BorrowState&, def::ProcessStatus&)>& mutate); | ||
| 60 | 65 | ||
| 61 | - uint32_t UpdatePidMemBorrowInfo(pid_t pid, const def::DebtInfo& debtInfo); | 66 | + uint32_t UpdateManagedPidSlotReturned(pid_t pid, const std::string& debtId, uint64_t amount); |
| 67 | + uint32_t UpdateManagedPidSlotReturnStatus(pid_t pid, const std::string& debtId, def::ReturnStatus status); | ||
| 68 | + void ResetSlotByDebtName(const std::string& debtId); | ||
| 62 | 69 | ||
| 63 | - uint32_t UpdatePidMemBorrowInfo(pid_t pid, const ubse::mem::controller::UbseMemBorrower& borrower, | 70 | + std::map<pid_t, def::ManagedPidEntry> GetManagedPidCacheSnapshot() const; |
| 64 | - const def::DebtInfo& debtInfo); | ||
| 65 | - | ||
| 66 | - void DeletePidMemBorrowInfo(pid_t pid, const std::string& borrowName); | ||
| 67 | - | ||
| 68 | - void ResetPidMemBorrowInfo(pid_t pid); | ||
| 69 | - | ||
| 70 | -public: | ||
| 71 | - ubse::task_executor::UbseTaskExecutorPtr borrowExecutor{}; | ||
| 72 | - ubse::task_executor::UbseTaskExecutorPtr returnExecutor{}; | ||
| 73 | - ubse::task_executor::UbseTaskExecutorPtr exceptionHandleExecutor{}; | ||
| 74 | 71 | ||
| 75 | private: | 72 | private: |
| 76 | - std::atomic<bool> isRecoverCompleted_{false}; | 73 | + static uint32_t ValidateProcMemTarget(const def::ProcessMemNewConfigInfo& config, uint64_t& outStartTime); |
| 77 | - std::shared_mutex pidInfoMutex; | 74 | + |
| 78 | - // 异常线程池,用于处理异常任务 | 75 | + bool CleanupStalePidConfig(const def::ProcessMemNewConfigInfo& cfg); |
| 79 | - std::unordered_map<pid_t, def::ProcessMemPidInfo> pidInfoMap; | 76 | + bool CleanupStaleFossil(pid_t pid, const def::FossilPidConfigInfo& fossil); |
| 80 | - // 故障节点映射: lentNodeId -> 受影响的 PID 集合 | 77 | + uint32_t RemovePidConfigSideEffects(const std::string& identifier); |
| 81 | - std::unordered_map<std::string, std::unordered_set<pid_t>> faultedLentNodes_; | 78 | + uint32_t RemoveNameConfigSideEffects(const std::string& identifier); |
| 79 | + void EraseConfigFromCache(bool isPid, const std::string& identifier); | ||
| 80 | + void RebuildMergePidConfigs(const std::vector<def::ProcessMemNewConfigInfo>& configSnapshot); | ||
| 81 | + void RebuildMergeNameConfigs(const std::vector<def::ProcessMemNewConfigInfo>& configSnapshot, | ||
| 82 | + std::vector<std::pair<pid_t, const def::ProcessMemNewConfigInfo*>>& toFossilize); | ||
| 83 | + void RebuildMergeFossils(); | ||
| 84 | + bool RebalancePidRemote(pid_t pid, const def::ManagedPidEntry& entry); | ||
| 85 | + | ||
| 86 | + mutable std::shared_mutex procMemConfigMutex_; | ||
| 87 | + std::vector<def::ProcessMemNewConfigInfo> procMemConfigCache_; | ||
| 88 | + | ||
| 89 | + mutable std::shared_mutex managedPidCacheMutex_; | ||
| 90 | + std::map<pid_t, def::ManagedPidEntry> managedPidCache_; | ||
| 82 | }; | 91 | }; |
| 83 | } // namespace process_mem::manager | 92 | } // namespace process_mem::manager |
| 84 | 93 | ||
| @@ -23,126 +23,180 @@ | |||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | namespace process_mem::def { | 25 | namespace process_mem::def { |
| 26 | -const uint32_t PROCESS_MEM_MODULE_CODE = 123; | 26 | +struct ProcessMemNewConfigInfo { |
| 27 | -const uint32_t PROCESS_MEM_SERVICE_ID = 11; | 27 | + bool isPid{true}; |
| 28 | + std::string identifier{}; | ||
| 29 | + uint64_t maxMemory{0}; | ||
| 30 | + double remoteRatio{0.0}; | ||
| 31 | + uint64_t startTime{0}; | ||
| 32 | + | ||
| 33 | + inline ubse::common::def::UbseResult Serialize(ubse::serial::UbseSerialization& serializer) const | ||
| 34 | + { | ||
| 35 | + uint8_t isPidFlag = static_cast<uint8_t>(isPid ? 1 : 0); | ||
| 36 | + serializer << isPidFlag << identifier << maxMemory << remoteRatio << startTime; | ||
| 37 | + return serializer.Check() ? UBSE_OK : UBSE_ERROR; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + inline ubse::common::def::UbseResult Deserialize(ubse::serial::UbseDeSerialization& deserializer) | ||
| 41 | + { | ||
| 42 | + uint8_t isPidFlag = 0; | ||
| 43 | + deserializer >> isPidFlag >> identifier >> maxMemory >> remoteRatio >> startTime; | ||
| 44 | + isPid = (isPidFlag != 0); | ||
| 45 | + return deserializer.Check() ? UBSE_OK : UBSE_ERROR; | ||
| 46 | + } | ||
| 47 | +}; | ||
| 48 | + | ||
| 49 | +struct ProcessMemDisplayEntry { | ||
| 50 | + int32_t pid{0}; | ||
| 51 | + std::string name{}; | ||
| 52 | + uint64_t maxMemory{0}; | ||
| 53 | + double remoteRatio{0.0}; | ||
| 54 | + | ||
| 55 | + inline ubse::common::def::UbseResult Serialize(ubse::serial::UbseSerialization& serializer) const | ||
| 56 | + { | ||
| 57 | + serializer << pid << name << maxMemory << remoteRatio; | ||
| 58 | + return serializer.Check() ? UBSE_OK : UBSE_ERROR; | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + inline ubse::common::def::UbseResult Deserialize(ubse::serial::UbseDeSerialization& deserializer) | ||
| 62 | + { | ||
| 63 | + deserializer >> pid >> name >> maxMemory >> remoteRatio; | ||
| 64 | + return deserializer.Check() ? UBSE_OK : UBSE_ERROR; | ||
| 65 | + } | ||
| 66 | +}; | ||
| 67 | + | ||
| 68 | +inline const std::string PROC_MEM_PID_KEY_PREFIX = "procMem_pid_"; | ||
| 69 | +inline const std::string PROC_MEM_NAME_KEY_PREFIX = "procMem_name_"; | ||
| 70 | +inline const std::string PROC_MEM_FOSSIL_KEY_PREFIX = "procMem_fossil_"; | ||
| 71 | + | ||
| 72 | +struct FossilPidConfigInfo { | ||
| 73 | + std::string name{}; | ||
| 74 | + uint64_t maxMemory{0}; | ||
| 75 | + double remoteRatio{0.0}; | ||
| 76 | + uint64_t startTime{0}; | ||
| 77 | + | ||
| 78 | + inline ubse::common::def::UbseResult Serialize(ubse::serial::UbseSerialization& serializer) const | ||
| 79 | + { | ||
| 80 | + serializer << name << maxMemory << remoteRatio << startTime; | ||
| 81 | + return serializer.Check() ? UBSE_OK : UBSE_ERROR; | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + inline ubse::common::def::UbseResult Deserialize(ubse::serial::UbseDeSerialization& deserializer) | ||
| 85 | + { | ||
| 86 | + deserializer >> name >> maxMemory >> remoteRatio >> startTime; | ||
| 87 | + return deserializer.Check() ? UBSE_OK : UBSE_ERROR; | ||
| 88 | + } | ||
| 89 | +}; | ||
| 90 | + | ||
| 91 | +enum class ConfigSource : uint8_t | ||
| 92 | +{ | ||
| 93 | + PID_CONFIG = 1 << 0, | ||
| 94 | + NAME_CONFIG = 1 << 1, | ||
| 95 | +}; | ||
| 96 | + | ||
| 97 | +enum class BorrowSlotStatus : uint8_t | ||
| 98 | +{ | ||
| 99 | + BORROWING, | ||
| 100 | + COMPLETED, | ||
| 101 | + FAILED, | ||
| 102 | +}; | ||
| 103 | + | ||
| 104 | +enum class AtomicMigrateResult : uint8_t | ||
| 105 | +{ | ||
| 106 | + kOk, | ||
| 107 | + kFail, | ||
| 108 | + kVanish, | ||
| 109 | +}; | ||
| 110 | + | ||
| 111 | +enum class ReturnStatus : uint8_t | ||
| 112 | +{ | ||
| 113 | + NONE, | ||
| 114 | + RETURNING, | ||
| 115 | + RETURNED, | ||
| 116 | +}; | ||
| 117 | + | ||
| 118 | +struct ReturnRequestItem { | ||
| 119 | + std::string name{}; | ||
| 120 | + uint64_t size{0}; | ||
| 121 | +}; | ||
| 122 | + | ||
| 123 | +struct BorrowSlot { | ||
| 124 | + uint64_t capacity{0}; | ||
| 125 | + uint64_t migratedBytes{0}; | ||
| 126 | + int32_t exportSlotId{-1}; | ||
| 127 | + std::string debtId{}; | ||
| 128 | + int srcNumaId{-1}; | ||
| 129 | + int remoteNumaId{-1}; | ||
| 130 | + std::chrono::steady_clock::time_point borrowTime{}; | ||
| 131 | + BorrowSlotStatus status{BorrowSlotStatus::BORROWING}; | ||
| 132 | + ReturnStatus returnStatus{ReturnStatus::NONE}; | ||
| 133 | +}; | ||
| 134 | + | ||
| 135 | +struct BorrowState { | ||
| 136 | + uint64_t currentRemote{0}; | ||
| 137 | + std::vector<BorrowSlot> slots; | ||
| 138 | + std::unordered_map<int, uint64_t> remoteNumaMigrated; | ||
| 139 | +}; | ||
| 140 | + | ||
| 141 | +struct BorrowCandidate { | ||
| 142 | + pid_t pid{0}; | ||
| 143 | + bool isChild{false}; | ||
| 144 | + bool hasActiveBorrow{false}; | ||
| 145 | + std::chrono::steady_clock::time_point lastMigrateTime{}; | ||
| 146 | + uint64_t actual{0}; | ||
| 147 | + uint64_t maxMemory{0}; | ||
| 148 | + double remoteRatio{0.0}; | ||
| 149 | + uint64_t currentRemote{0}; | ||
| 150 | + uint64_t canMigrate{0}; | ||
| 151 | +}; | ||
| 28 | 152 | ||
| 29 | -/** | ||
| 30 | - * @brief 进程运行时状态 | ||
| 31 | - */ | ||
| 32 | enum class ProcessStatus | 153 | enum class ProcessStatus |
| 33 | { | 154 | { |
| 34 | - IDLE, // 无借用 | 155 | + IDLE, |
| 35 | - WAIT_BORROW, // 等待借用 | 156 | + BORROWING, |
| 36 | - BORROWING, // 借用中 | 157 | + BORROWED, |
| 37 | - REPAYING, // 归还中 | ||
| 38 | - INACTIVE, // 已停止 | ||
| 39 | - FAULT, // 故障隔离中(借出节点故障,暂停业务处理) | ||
| 40 | }; | 158 | }; |
| 41 | 159 | ||
| 42 | -struct ProcessMemPidConfigInfo { | 160 | +struct ManagedPidEntry { |
| 43 | - pid_t pid{}; | 161 | + pid_t pid{0}; |
| 44 | - int evictThreshold{}; | 162 | + pid_t parentPid{0}; |
| 45 | - int targetEvictThreshold{}; | 163 | + bool isChild{false}; |
| 46 | - int reclaimThreshold{}; | 164 | + uint8_t sources{0}; |
| 47 | - uint64_t expectedMemoryUsage{}; | 165 | + std::string nameConfigName{}; |
| 48 | - std::optional<uint64_t> srcNumaId; // redis进程存在的numaId,可选 | 166 | + uint64_t maxMemory{0}; |
| 49 | - inline ubse::common::def::UbseResult SerializeConfigInfo(ubse::serial::UbseSerialization& serializer) const | 167 | + double remoteRatio{0.0}; |
| 50 | - { | ||
| 51 | - serializer << pid << evictThreshold << targetEvictThreshold << reclaimThreshold << expectedMemoryUsage; | ||
| 52 | - uint64_t hasSrcNuma = srcNumaId.has_value() ? 1 : 0; | ||
| 53 | - serializer << hasSrcNuma; | ||
| 54 | - if (srcNumaId.has_value()) { | ||
| 55 | - serializer << srcNumaId.value(); | ||
| 56 | - } | ||
| 57 | - return serializer.Check() ? UBSE_OK : UBSE_ERROR; | ||
| 58 | - } | ||
| 59 | 168 | ||
| 60 | - inline ubse::common::def::UbseResult DeserializeConfigInfo(ubse::serial::UbseDeSerialization& deserializer) | 169 | + uint64_t vmRss{0}; |
| 61 | - { | 170 | + |
| 62 | - deserializer >> pid >> evictThreshold >> targetEvictThreshold >> reclaimThreshold >> expectedMemoryUsage; | 171 | + // 进程启动时刻(/proc/<pid>/stat 第22字段),用于识别退出后 pid 被复用 |
| 63 | - uint64_t hasSrcNuma = 0; | 172 | + uint64_t startTime{0}; |
| 64 | - deserializer >> hasSrcNuma; | 173 | + |
| 65 | - if (hasSrcNuma != 0) { | 174 | + std::chrono::steady_clock::time_point lastMigrateTime{}; |
| 66 | - uint64_t val = 0; | 175 | + |
| 67 | - deserializer >> val; | 176 | + ProcessStatus processStatus{ProcessStatus::IDLE}; |
| 68 | - srcNumaId = val; | 177 | + BorrowState borrow{}; |
| 69 | - } else { | ||
| 70 | - srcNumaId = std::nullopt; | ||
| 71 | - } | ||
| 72 | - return deserializer.Check() ? UBSE_OK : UBSE_ERROR; | ||
| 73 | - } | ||
| 74 | }; | 178 | }; |
| 75 | 179 | ||
| 76 | -enum class BorrowStatus | 180 | +inline std::optional<pid_t> ParsePidFromIdentifier(const std::string& identifier) |
| 77 | { | 181 | { |
| 78 | - COMPLETED, | 182 | + if (identifier.empty() || identifier.find_first_not_of("0123456789") != std::string::npos) { |
| 79 | - CREATING, | 183 | + return std::nullopt; |
| 80 | -}; | ||
| 81 | - | ||
| 82 | -struct DebtInfo { | ||
| 83 | - std::chrono::steady_clock::time_point borrowStartTime{}; // 借用开始时间,自定义超时时间,借用中的账本需要关注 | ||
| 84 | - BorrowStatus status{BorrowStatus::COMPLETED}; | ||
| 85 | - ubse::mem::controller::UbseMemNumaDesc numaDesc{}; | ||
| 86 | -}; | ||
| 87 | - | ||
| 88 | -constexpr uint16_t INVALID_REMOTE_NUMA = 0; | ||
| 89 | - | ||
| 90 | -struct BorrowInfo { | ||
| 91 | - int32_t remoteNumaId{-1}; | ||
| 92 | - int32_t exportSlotId{-1}; // 导出的节点Id | ||
| 93 | - int32_t importSocketId{-1}; // 导入的socketId | ||
| 94 | - std::unordered_map<std::string, DebtInfo> debtInfos; // key-value: name-debtInfo | ||
| 95 | -}; | ||
| 96 | - | ||
| 97 | -struct ProcessMemPidInfo { | ||
| 98 | - pid_t ppid{0}; // 父进程 ID(0 表示无父进程) | ||
| 99 | - long startTime{0}; // 进程启动时间 | ||
| 100 | - | ||
| 101 | - // ========== 配置信息(持久化) ========== | ||
| 102 | - ProcessMemPidConfigInfo configInfo{}; // 配置信息 | ||
| 103 | - | ||
| 104 | - // ========== 运行时状态(不持久化) ========== | ||
| 105 | - ProcessStatus processStatus{ProcessStatus::IDLE}; // 进程状态 | ||
| 106 | - | ||
| 107 | - // ========== 内存借用信息(持久化于ubse) ========== | ||
| 108 | - BorrowInfo memBorrowInfo; // 借用内存的信息 | ||
| 109 | - | ||
| 110 | - // ========== 子进程信息 (不持久化)========== | ||
| 111 | - // std::unordered_map<pid_t, ProcessMemPidInfo> childrenInfo; // 子进程信息映射 | ||
| 112 | - | ||
| 113 | - inline ubse::common::def::UbseResult SerializePidInfo(ubse::serial::UbseSerialization& serializer) const | ||
| 114 | - { | ||
| 115 | - auto ret = configInfo.SerializeConfigInfo(serializer); | ||
| 116 | - if (ret != UBSE_OK) { | ||
| 117 | - return ret; | ||
| 118 | - } | ||
| 119 | - serializer << startTime; | ||
| 120 | - return serializer.Check() ? UBSE_OK : UBSE_ERROR; | ||
| 121 | } | 184 | } |
| 122 | - | 185 | + try { |
| 123 | - inline ubse::common::def::UbseResult DeserializePidInfo(ubse::serial::UbseDeSerialization& deserializer) | 186 | + pid_t pid = static_cast<pid_t>(std::stoi(identifier)); |
| 124 | - { | 187 | + if (pid > 0) { |
| 125 | - auto ret = configInfo.DeserializeConfigInfo(deserializer); | 188 | + return pid; |
| 126 | - if (ret != UBSE_OK) { | ||
| 127 | - return ret; | ||
| 128 | } | 189 | } |
| 129 | - deserializer >> startTime; | 190 | + } catch (...) { |
| 130 | - return deserializer.Check() ? UBSE_OK : UBSE_ERROR; | ||
| 131 | } | 191 | } |
| 132 | -}; | 192 | + return std::nullopt; |
| 133 | - | 193 | +} |
| 134 | -struct PidCollectInfo { | ||
| 135 | - std::unordered_map<uint32_t, size_t> numaMemDistribution{}; // 每个numa上内存分布单位为 byte. | ||
| 136 | - std::vector<pid_t> childrenInfo{}; // 子进程信息 | ||
| 137 | -}; | ||
| 138 | 194 | ||
| 139 | enum class UsrInfoPluginType : uint32_t | 195 | enum class UsrInfoPluginType : uint32_t |
| 140 | { | 196 | { |
| 141 | PROCESS_MEM = 123, | 197 | PROCESS_MEM = 123, |
| 142 | }; | 198 | }; |
| 143 | 199 | ||
| 144 | -inline const std::string PROCESS_MEM_NAME_PREFIX = "ProcessMem_"; | ||
| 145 | - | ||
| 146 | struct ProcessMemUsrInfo { | 200 | struct ProcessMemUsrInfo { |
| 147 | UsrInfoPluginType pluginId{UsrInfoPluginType::PROCESS_MEM}; | 201 | UsrInfoPluginType pluginId{UsrInfoPluginType::PROCESS_MEM}; |
| 148 | int32_t pid{}; | 202 | int32_t pid{}; |
| @@ -23,4 +23,4 @@ void UbsePluginDeInit() | |||
| 23 | { | 23 | { |
| 24 | process_mem::pid::bridge::ProcessMemPidBridge::UnInit(); | 24 | process_mem::pid::bridge::ProcessMemPidBridge::UnInit(); |
| 25 | return; | 25 | return; |
| 26 | -} | 26 | +} |
| @@ -17,17 +17,8 @@ | |||
| 17 | extern "C" { | 17 | extern "C" { |
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | -/** | ||
| 21 | - * @brief Plugin initialization function; initializes the Ubse plugin | ||
| 22 | - * @param modCode Plugin module code, used to distinguish different plugin instances | ||
| 23 | - * @return Initialization result, 0 for success, non-zero for error | ||
| 24 | - */ | ||
| 25 | uint32_t UbsePluginInit(uint16_t modCode); | 20 | uint32_t UbsePluginInit(uint16_t modCode); |
| 26 | 21 | ||
| 27 | -/** | ||
| 28 | - * @brief Plugin deinitialization function; | ||
| 29 | - * @return void | ||
| 30 | - */ | ||
| 31 | void UbsePluginDeInit(void); | 22 | void UbsePluginDeInit(void); |
| 32 | 23 | ||
| 33 | 24 | ||
| @@ -14,9 +14,8 @@ | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | -// Store constants unrelated to the structure. | ||
| 18 | namespace constant { | 17 | namespace constant { |
| 19 | -constexpr uint32_t REQUEST_BUFFER_CAPACITY = 8; // Default non-empty request size. | 18 | +constexpr uint32_t REQUEST_BUFFER_CAPACITY = 8; |
| 20 | } | 19 | } |
| 21 | 20 | ||
| 22 | namespace systemd::error { | 21 | namespace systemd::error { |
| @@ -45,4 +44,4 @@ namespace node::error { | |||
| 45 | const std::string NODE_ATTRIBUTE_EMPTY = "ERROR: Node attribute information is empty."; | 44 | const std::string NODE_ATTRIBUTE_EMPTY = "ERROR: Node attribute information is empty."; |
| 46 | } | 45 | } |
| 47 | 46 | ||
| 48 | -#endif // UBSE_CLI_CONSTANT_H | 47 | +#endif |
| @@ -12,149 +12,215 @@ | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 15 | 18 | ||
| 16 | 19 | ||
| 17 | 20 | ||
| 18 | 21 | ||
| 19 | 22 | ||
| 23 | + | ||
| 20 | 24 | ||
| 21 | 25 | ||
| 22 | 26 | ||
| 23 | 27 | ||
| 28 | + | ||
| 24 | 29 | ||
| 25 | 30 | ||
| 26 | 31 | ||
| 32 | +UBSE_DEFINE_THIS_MODULE("ubse_cli"); | ||
| 33 | + | ||
| 27 | namespace ubse::cli::reg { | 34 | namespace ubse::cli::reg { |
| 28 | using namespace ubse::cli::framework; | 35 | using namespace ubse::cli::framework; |
| 29 | using namespace ubse::serial; | 36 | using namespace ubse::serial; |
| 30 | 37 | ||
| 31 | namespace { | 38 | namespace { |
| 32 | constexpr uint16_t PID_MODULE_CODE = UBSE_MEM; | 39 | constexpr uint16_t PID_MODULE_CODE = UBSE_MEM; |
| 33 | -constexpr uint16_t PID_SET_THRESHOLD_OP = UBSE_MEM_CLI_PID_SET_THRESHOLD; | ||
| 34 | -constexpr uint16_t PID_PRINT_OP = UBSE_MEM_CLI_PRINT_PID_INFO; | ||
| 35 | -constexpr uint16_t PID_UNSET_OP = UBSE_MEM_CLI_PID_UNSET; | ||
| 36 | 40 | ||
| 37 | -bool PidInfoDeSerialization(UbseDeSerialization& in, std::vector<process_mem::def::ProcessMemPidInfo>& data) | 41 | +std::shared_ptr<UbseCliResultEcho> HandleSimpleIpcResponse(ubse_api_buffer_t& resBuffer, uint32_t invokeRet, |
| 42 | + const char* successMsg) | ||
| 38 | { | 43 | { |
| 39 | - size_t dataSize{}; | 44 | + UbseCliBufferGuard ubseCliBufferGuard(resBuffer); |
| 40 | - in >> dataSize; | 45 | + if (invokeRet != UBSE_OK) { |
| 41 | - if (!in.Check()) { | 46 | + UBSE_LOG_ERROR << "IPC invoke failed, ret=" << invokeRet; |
| 42 | - return false; | 47 | + return UbseCliRegModule::UbseCliStringPromptReply(GetErrorMessage(invokeRet)); |
| 43 | } | 48 | } |
| 44 | - for (size_t i = 0; i < dataSize; ++i) { | 49 | + UbseDeSerialization deserial{resBuffer.buffer, resBuffer.length}; |
| 45 | - process_mem::def::ProcessMemPidInfo tmpInfo{}; | 50 | + int successCode = 0; |
| 46 | - auto ret = tmpInfo.configInfo.DeserializeConfigInfo(in); | 51 | + std::string errMsg; |
| 47 | - if (ret != UBSE_OK) { | 52 | + deserial >> successCode >> errMsg; |
| 48 | - return false; | 53 | + if (deserial.Check() && successCode == 1) { |
| 49 | - } | 54 | + return UbseCliRegModule::UbseCliStringPromptReply(successMsg); |
| 50 | - data.push_back(tmpInfo); | ||
| 51 | } | 55 | } |
| 52 | - return true; | 56 | + if (!deserial.Check()) { |
| 57 | + return UbseCliRegModule::UbseCliStringPromptReply("ERROR: Invalid response from daemon."); | ||
| 58 | + } | ||
| 59 | + UBSE_LOG_ERROR << "Daemon error response: successCode=" << successCode << ", errMsg=" << errMsg; | ||
| 60 | + return UbseCliRegModule::UbseCliStringPromptReply("ERROR: " + errMsg); | ||
| 53 | } | 61 | } |
| 54 | 62 | ||
| 55 | -std::shared_ptr<UbseCliResultEcho> BuildPidInfoTable( | 63 | +std::shared_ptr<UbseCliResultEcho> HandleStructIpcResponse(ubse_api_buffer_t& resBuffer, uint32_t invokeRet, |
| 56 | - const std::vector<process_mem::def::ProcessMemPidInfo>& pidInfoDetail) | 64 | + std::function<std::string()> formatSuccess) |
| 57 | { | 65 | { |
| 58 | - UbseCliResBuilder variableCellBuilder(UBSE_CLI_NUM_6, UBSE_CLI_NUM_30); | 66 | + UbseCliBufferGuard ubseCliBufferGuard(resBuffer); |
| 59 | - size_t row = variableCellBuilder.UbseCliAddRow(); | 67 | + if (invokeRet != UBSE_OK) { |
| 60 | - variableCellBuilder.UbseCliAddlineSeparate(row); | 68 | + UBSE_LOG_ERROR << "IPC invoke failed, ret=" << invokeRet; |
| 61 | - variableCellBuilder.UbseCliSetCellData(row, UBSE_CLI_NUM_1, "pid"); | 69 | + return UbseCliRegModule::UbseCliStringPromptReply(GetErrorMessage(invokeRet)); |
| 62 | - variableCellBuilder.UbseCliSetCellData(row, UBSE_CLI_NUM_2, "evictThreshold"); | ||
| 63 | - variableCellBuilder.UbseCliSetCellData(row, UBSE_CLI_NUM_3, "targetEvictThreshold"); | ||
| 64 | - variableCellBuilder.UbseCliSetCellData(row, UBSE_CLI_NUM_4, "reclaimThreshold"); | ||
| 65 | - variableCellBuilder.UbseCliSetCellData(row, UBSE_CLI_NUM_5, "totalMemoryUsage"); | ||
| 66 | - variableCellBuilder.UbseCliSetCellData(row, UBSE_CLI_NUM_6, "srcNuma"); | ||
| 67 | - variableCellBuilder.UbseCliAddBottomlineSeparate(); | ||
| 68 | - for (const auto& pidInfo : pidInfoDetail) { | ||
| 69 | - row = variableCellBuilder.UbseCliAddRow(); | ||
| 70 | - variableCellBuilder.UbseCliSetCellData(row, UBSE_CLI_NUM_1, std::to_string(pidInfo.configInfo.pid)); | ||
| 71 | - variableCellBuilder.UbseCliSetCellData(row, UBSE_CLI_NUM_2, std::to_string(pidInfo.configInfo.evictThreshold)); | ||
| 72 | - variableCellBuilder.UbseCliSetCellData(row, UBSE_CLI_NUM_3, | ||
| 73 | - std::to_string(pidInfo.configInfo.targetEvictThreshold)); | ||
| 74 | - variableCellBuilder.UbseCliSetCellData(row, UBSE_CLI_NUM_4, | ||
| 75 | - std::to_string(pidInfo.configInfo.reclaimThreshold)); | ||
| 76 | - variableCellBuilder.UbseCliSetCellData(row, UBSE_CLI_NUM_5, | ||
| 77 | - std::to_string(pidInfo.configInfo.expectedMemoryUsage)); | ||
| 78 | - variableCellBuilder.UbseCliSetCellData( | ||
| 79 | - row, UBSE_CLI_NUM_6, | ||
| 80 | - pidInfo.configInfo.srcNumaId.has_value() ? std::to_string(pidInfo.configInfo.srcNumaId.value()) : "N/A"); | ||
| 81 | } | 70 | } |
| 82 | - variableCellBuilder.UbseCliAddBottomlineSeparate(); | 71 | + UbseDeSerialization deserial{resBuffer.buffer, resBuffer.length}; |
| 83 | - return UbseCliRegModule::UbseCliVariableCelReply(variableCellBuilder.UbseCliVariableCellBuild()); | 72 | + int successCode = 0; |
| 73 | + std::string errMsg; | ||
| 74 | + deserial >> successCode >> errMsg; | ||
| 75 | + if (deserial.Check() && successCode == 1) { | ||
| 76 | + return UbseCliRegModule::UbseCliStringPromptReply(formatSuccess()); | ||
| 77 | + } | ||
| 78 | + if (!deserial.Check()) { | ||
| 79 | + return UbseCliRegModule::UbseCliStringPromptReply("ERROR: Invalid response from daemon."); | ||
| 80 | + } | ||
| 81 | + UBSE_LOG_ERROR << "Daemon error response: successCode=" << successCode << ", errMsg=" << errMsg; | ||
| 82 | + return UbseCliRegModule::UbseCliStringPromptReply("ERROR: " + errMsg); | ||
| 84 | } | 83 | } |
| 85 | -} // anonymous namespace | 84 | + |
| 85 | +static constexpr uint64_t kBytesPerKiB = 1024ULL; | ||
| 86 | +static constexpr uint64_t kBytesPerMiB = kBytesPerKiB * kBytesPerKiB; | ||
| 87 | +static constexpr uint64_t kBytesPerGiB = kBytesPerKiB * kBytesPerKiB * kBytesPerKiB; | ||
| 88 | + | ||
| 89 | +static std::string FormatMaxMemoryHuman(uint64_t bytes) | ||
| 90 | +{ | ||
| 91 | + if (bytes >= kBytesPerGiB && bytes % kBytesPerGiB == 0) { | ||
| 92 | + return std::to_string(bytes / kBytesPerGiB) + "G"; | ||
| 93 | + } | ||
| 94 | + if (bytes >= kBytesPerMiB && bytes % kBytesPerMiB == 0) { | ||
| 95 | + return std::to_string(bytes / kBytesPerMiB) + "M"; | ||
| 96 | + } | ||
| 97 | + if (bytes >= kBytesPerKiB && bytes % kBytesPerKiB == 0) { | ||
| 98 | + return std::to_string(bytes / kBytesPerKiB) + "K"; | ||
| 99 | + } | ||
| 100 | + return std::to_string(bytes); | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +static std::string FormatRatioHuman(double ratio) | ||
| 104 | +{ | ||
| 105 | + std::ostringstream oss; | ||
| 106 | + oss << std::fixed << std::setprecision(2) << ratio; | ||
| 107 | + return oss.str(); | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +std::shared_ptr<UbseCliResultEcho> BuildProcessMemConfigTable( | ||
| 111 | + const std::vector<process_mem::def::ProcessMemDisplayEntry>& entries) | ||
| 112 | +{ | ||
| 113 | + if (entries.empty()) { | ||
| 114 | + return UbseCliRegModule::UbseCliStringPromptReply("No process is currently managed by process_mem"); | ||
| 115 | + } | ||
| 116 | + constexpr size_t COL_COUNT = 4; | ||
| 117 | + constexpr size_t COL_WIDTH = 30; | ||
| 118 | + UbseCliResBuilder builder(COL_COUNT, COL_WIDTH); | ||
| 119 | + size_t row = builder.UbseCliAddRow(); | ||
| 120 | + builder.UbseCliAddlineSeparate(row); | ||
| 121 | + builder.UbseCliSetCellData(row, 1, "PID"); | ||
| 122 | + builder.UbseCliSetCellData(row, 2, "Name"); | ||
| 123 | + builder.UbseCliSetCellData(row, 3, "Size"); | ||
| 124 | + builder.UbseCliSetCellData(row, 4, "RemoteRatio"); | ||
| 125 | + builder.UbseCliAddBottomlineSeparate(); | ||
| 126 | + | ||
| 127 | + for (const auto& entry : entries) { | ||
| 128 | + row = builder.UbseCliAddRow(); | ||
| 129 | + builder.UbseCliSetCellData(row, 1, entry.pid > 0 ? std::to_string(entry.pid) : "N/A"); | ||
| 130 | + builder.UbseCliSetCellData(row, 2, entry.name); | ||
| 131 | + builder.UbseCliSetCellData(row, 3, FormatMaxMemoryHuman(entry.maxMemory)); | ||
| 132 | + builder.UbseCliSetCellData(row, 4, FormatRatioHuman(entry.remoteRatio)); | ||
| 133 | + } | ||
| 134 | + builder.UbseCliAddBottomlineSeparate(); | ||
| 135 | + return UbseCliRegModule::UbseCliVariableCelReply(builder.UbseCliVariableCellBuild()); | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | +} // namespace | ||
| 86 | 139 | ||
| 87 | class UbseCliMemPid::UbseCliMemPidImpl { | 140 | class UbseCliMemPid::UbseCliMemPidImpl { |
| 88 | public: | 141 | public: |
| 89 | - std::shared_ptr<framework::UbseCliResultEcho> UbseCliSetPidThresholdImpl( | 142 | + std::shared_ptr<framework::UbseCliResultEcho> UbseCliSetProcessMemImpl( |
| 90 | - const process_mem::def::ProcessMemPidInfo& pidInfo) | 143 | + const process_mem::def::ProcessMemNewConfigInfo& newConfig) |
| 91 | { | 144 | { |
| 92 | UbseSerialization serializer; | 145 | UbseSerialization serializer; |
| 93 | - auto ret = pidInfo.configInfo.SerializeConfigInfo(serializer); | 146 | + auto ret = newConfig.Serialize(serializer); |
| 94 | if (ret != UBSE_OK) { | 147 | if (ret != UBSE_OK) { |
| 95 | return UbseCliRegModule::UbseCliStringPromptReply(data::error::REQUEST_INFO_SER_FAILED); | 148 | return UbseCliRegModule::UbseCliStringPromptReply(data::error::REQUEST_INFO_SER_FAILED); |
| 96 | } | 149 | } |
| 97 | ubse_api_buffer_t reqBuffer{serializer.GetBuffer(), static_cast<uint32_t>(serializer.GetLength())}; | 150 | ubse_api_buffer_t reqBuffer{serializer.GetBuffer(), static_cast<uint32_t>(serializer.GetLength())}; |
| 98 | ubse_api_buffer_t resBuffer{}; | 151 | ubse_api_buffer_t resBuffer{}; |
| 99 | - UbseCliWaitIndicator waitIndicator("Setting pidInfo"); | 152 | + UbseCliWaitIndicator waitIndicator("Setting process-mem config"); |
| 100 | - ret = ubse_invoke_call(PID_MODULE_CODE, PID_SET_THRESHOLD_OP, &reqBuffer, &resBuffer); | 153 | + ret = ubse_invoke_call(PID_MODULE_CODE, UBSE_MEM_CLI_PROC_MEM_SET, &reqBuffer, &resBuffer); |
| 101 | - UbseCliBufferGuard ubseCliBufferGuard(resBuffer); | 154 | + return HandleStructIpcResponse(resBuffer, ret, [&newConfig]() { |
| 102 | - if (ret != UBSE_OK) { | 155 | + std::ostringstream echo; |
| 103 | - return UbseCliRegModule::UbseCliStringPromptReply( | 156 | + if (newConfig.isPid) { |
| 104 | - std::string("ERROR: Internal error with error code " + std::to_string(ret))); | 157 | + echo << "PID: " << newConfig.identifier << "\n"; |
| 105 | - } | 158 | + } else { |
| 106 | - UbseDeSerialization deserial{resBuffer.buffer, resBuffer.length}; | 159 | + echo << "Name: " << newConfig.identifier << "\n"; |
| 107 | - int successCode = 0; | 160 | + } |
| 108 | - std::string errMsg; | 161 | + echo << "Size: " << FormatMaxMemoryHuman(newConfig.maxMemory) << "\n"; |
| 109 | - deserial >> successCode >> errMsg; | 162 | + echo << "RemoteRatio: " << std::fixed << std::setprecision(2) << newConfig.remoteRatio << "\n"; |
| 110 | - if (deserial.Check() && successCode == 1) { | 163 | + echo << "Set process-mem config successfully"; |
| 111 | - return UbseCliRegModule::UbseCliStringPromptReply("Set successfully"); | 164 | + return echo.str(); |
| 112 | - } | 165 | + }); |
| 113 | - return UbseCliRegModule::UbseCliStringPromptReply("ERROR: " + errMsg); | ||
| 114 | } | 166 | } |
| 115 | 167 | ||
| 116 | - std::shared_ptr<framework::UbseCliResultEcho> UbseCliPrintPidInfoImpl() | 168 | + std::shared_ptr<framework::UbseCliResultEcho> UbseCliRemoveProcessMemImpl(bool isPid, const std::string& identifier) |
| 169 | + { | ||
| 170 | + process_mem::def::ProcessMemNewConfigInfo newConfig{}; | ||
| 171 | + newConfig.isPid = isPid; | ||
| 172 | + newConfig.identifier = identifier; | ||
| 173 | + UbseSerialization serializer; | ||
| 174 | + auto serRet = newConfig.Serialize(serializer); | ||
| 175 | + if (serRet != UBSE_OK || !serializer.Check()) { | ||
| 176 | + return UbseCliRegModule::UbseCliStringPromptReply(data::error::REQUEST_INFO_SER_FAILED); | ||
| 177 | + } | ||
| 178 | + ubse_api_buffer_t reqBuffer{serializer.GetBuffer(), static_cast<uint32_t>(serializer.GetLength())}; | ||
| 179 | + ubse_api_buffer_t resBuffer{}; | ||
| 180 | + uint32_t ret = ubse_invoke_call(PID_MODULE_CODE, UBSE_MEM_CLI_PROC_MEM_REMOVE, &reqBuffer, &resBuffer); | ||
| 181 | + std::string successMsg = isPid ? ("pid " + identifier + " removed") : ("name '" + identifier + "' removed"); | ||
| 182 | + return HandleSimpleIpcResponse(resBuffer, ret, successMsg.c_str()); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + std::shared_ptr<framework::UbseCliResultEcho> UbseCliDisplayProcessMemImpl(uint32_t opcode) | ||
| 117 | { | 186 | { |
| 118 | UbseSerialization serial; | 187 | UbseSerialization serial; |
| 119 | ubse_api_buffer_t reqBuffer{serial.GetBuffer(), static_cast<uint32_t>(serial.GetLength())}; | 188 | ubse_api_buffer_t reqBuffer{serial.GetBuffer(), static_cast<uint32_t>(serial.GetLength())}; |
| 120 | ubse_api_buffer_t resBuffer{}; | 189 | ubse_api_buffer_t resBuffer{}; |
| 121 | - uint32_t ret = ubse_invoke_call(PID_MODULE_CODE, PID_PRINT_OP, &reqBuffer, &resBuffer); | 190 | + uint32_t ret = ubse_invoke_call(PID_MODULE_CODE, opcode, &reqBuffer, &resBuffer); |
| 122 | UbseCliBufferGuard ubseCliBufferGuard(resBuffer); | 191 | UbseCliBufferGuard ubseCliBufferGuard(resBuffer); |
| 123 | if (ret != UBSE_OK) { | 192 | if (ret != UBSE_OK) { |
| 124 | - return UbseCliRegModule::UbseCliStringPromptReply( | 193 | + UBSE_LOG_ERROR << "DisplayProcessMem IPC invoke failed, ret=" << ret; |
| 125 | - std::string("ERROR: Internal error with error code " + std::to_string(ret))); | 194 | + return UbseCliRegModule::UbseCliStringPromptReply(GetErrorMessage(ret)); |
| 126 | } | 195 | } |
| 127 | - UbseDeSerialization derial{resBuffer.buffer, resBuffer.length}; | 196 | + |
| 128 | - std::vector<process_mem::def::ProcessMemPidInfo> pidInfo{}; | 197 | + UbseDeSerialization deserial{resBuffer.buffer, resBuffer.length}; |
| 129 | - if (!PidInfoDeSerialization(derial, pidInfo)) { | 198 | + size_t count = 0; |
| 130 | - return UbseCliRegModule::UbseCliStringPromptReply("ERROR"); | 199 | + deserial >> count; |
| 200 | + if (!deserial.Check()) { | ||
| 201 | + return UbseCliRegModule::UbseCliStringPromptReply("ERROR: Deserialization failed."); | ||
| 131 | } | 202 | } |
| 132 | - if (pidInfo.empty()) { | 203 | + |
| 133 | - return UbseCliRegModule::UbseCliStringPromptReply("No process is currently managed by process_mem"); | 204 | + std::vector<process_mem::def::ProcessMemDisplayEntry> entries; |
| 205 | + for (size_t i = 0; i < count; ++i) { | ||
| 206 | + process_mem::def::ProcessMemDisplayEntry entry; | ||
| 207 | + if (entry.Deserialize(deserial) != UBSE_OK) { | ||
| 208 | + return UbseCliRegModule::UbseCliStringPromptReply("ERROR: Deserialization failed."); | ||
| 209 | + } | ||
| 210 | + entries.push_back(std::move(entry)); | ||
| 134 | } | 211 | } |
| 135 | - return BuildPidInfoTable(pidInfo); | 212 | + |
| 213 | + return BuildProcessMemConfigTable(entries); | ||
| 136 | } | 214 | } |
| 137 | 215 | ||
| 138 | - std::shared_ptr<framework::UbseCliResultEcho> UbseCliUnsetPidImpl(pid_t pid) | 216 | + std::shared_ptr<framework::UbseCliResultEcho> UbseCliDisplayProcessMemConfigImpl() |
| 139 | { | 217 | { |
| 140 | - UbseSerialization serial; | 218 | + return UbseCliDisplayProcessMemImpl(UBSE_MEM_CLI_PROC_MEM_DISPLAY_CONFIG); |
| 141 | - serial << pid; | 219 | + } |
| 142 | - ubse_api_buffer_t reqBuffer{serial.GetBuffer(), static_cast<uint32_t>(serial.GetLength())}; | 220 | + |
| 143 | - ubse_api_buffer_t resBuffer{}; | 221 | + std::shared_ptr<framework::UbseCliResultEcho> UbseCliDisplayProcessMemDetailImpl() |
| 144 | - uint32_t ret = ubse_invoke_call(PID_MODULE_CODE, PID_UNSET_OP, &reqBuffer, &resBuffer); | 222 | + { |
| 145 | - UbseCliBufferGuard ubseCliBufferGuard(resBuffer); | 223 | + return UbseCliDisplayProcessMemImpl(UBSE_MEM_CLI_PROC_MEM_DISPLAY_DETAIL); |
| 146 | - if (ret != UBSE_OK) { | ||
| 147 | - return UbseCliRegModule::UbseCliStringPromptReply( | ||
| 148 | - std::string("ERROR: Internal error with error code " + std::to_string(ret))); | ||
| 149 | - } | ||
| 150 | - UbseDeSerialization deserial{resBuffer.buffer, resBuffer.length}; | ||
| 151 | - int successCode = 0; | ||
| 152 | - std::string errMsg; | ||
| 153 | - deserial >> successCode >> errMsg; | ||
| 154 | - if (deserial.Check() && successCode == 1) { | ||
| 155 | - return UbseCliRegModule::UbseCliStringPromptReply("Unset successfully"); | ||
| 156 | - } | ||
| 157 | - return UbseCliRegModule::UbseCliStringPromptReply("ERROR: " + errMsg); | ||
| 158 | } | 224 | } |
| 159 | }; | 225 | }; |
| 160 | 226 | ||
| @@ -165,28 +231,37 @@ UbseCliMemPid::UbseCliMemPid() | |||
| 165 | 231 | ||
| 166 | UbseCliMemPid::~UbseCliMemPid() noexcept = default; | 232 | UbseCliMemPid::~UbseCliMemPid() noexcept = default; |
| 167 | 233 | ||
| 168 | -std::shared_ptr<framework::UbseCliResultEcho> UbseCliMemPid::UbseCliSetPidThreshold( | 234 | +std::shared_ptr<framework::UbseCliResultEcho> UbseCliMemPid::UbseCliSetProcessMem( |
| 169 | - const process_mem::def::ProcessMemPidInfo& pidInfo) | 235 | + const process_mem::def::ProcessMemNewConfigInfo& newConfig) |
| 170 | { | 236 | { |
| 171 | if (pImpl_ == nullptr) { | 237 | if (pImpl_ == nullptr) { |
| 172 | return UbseCliRegModule::UbseCliStringPromptReply(systemd::error::ALLOCATION_ERROR); | 238 | return UbseCliRegModule::UbseCliStringPromptReply(systemd::error::ALLOCATION_ERROR); |
| 173 | } | 239 | } |
| 174 | - return pImpl_->UbseCliSetPidThresholdImpl(pidInfo); | 240 | + return pImpl_->UbseCliSetProcessMemImpl(newConfig); |
| 175 | } | 241 | } |
| 176 | 242 | ||
| 177 | -std::shared_ptr<framework::UbseCliResultEcho> UbseCliMemPid::UbseCliPrintPidInfo() | 243 | +std::shared_ptr<framework::UbseCliResultEcho> UbseCliMemPid::UbseCliRemoveProcessMem(bool isPid, |
| 244 | + const std::string& identifier) | ||
| 178 | { | 245 | { |
| 179 | if (pImpl_ == nullptr) { | 246 | if (pImpl_ == nullptr) { |
| 180 | return UbseCliRegModule::UbseCliStringPromptReply(systemd::error::ALLOCATION_ERROR); | 247 | return UbseCliRegModule::UbseCliStringPromptReply(systemd::error::ALLOCATION_ERROR); |
| 181 | } | 248 | } |
| 182 | - return pImpl_->UbseCliPrintPidInfoImpl(); | 249 | + return pImpl_->UbseCliRemoveProcessMemImpl(isPid, identifier); |
| 183 | } | 250 | } |
| 184 | 251 | ||
| 185 | -std::shared_ptr<framework::UbseCliResultEcho> UbseCliMemPid::UbseCliUnsetPid(pid_t pid) | 252 | +std::shared_ptr<framework::UbseCliResultEcho> UbseCliMemPid::UbseCliDisplayProcessMemConfig() |
| 186 | { | 253 | { |
| 187 | if (pImpl_ == nullptr) { | 254 | if (pImpl_ == nullptr) { |
| 188 | return UbseCliRegModule::UbseCliStringPromptReply(systemd::error::ALLOCATION_ERROR); | 255 | return UbseCliRegModule::UbseCliStringPromptReply(systemd::error::ALLOCATION_ERROR); |
| 189 | } | 256 | } |
| 190 | - return pImpl_->UbseCliUnsetPidImpl(pid); | 257 | + return pImpl_->UbseCliDisplayProcessMemConfigImpl(); |
| 258 | +} | ||
| 259 | + | ||
| 260 | +std::shared_ptr<framework::UbseCliResultEcho> UbseCliMemPid::UbseCliDisplayProcessMemDetail() | ||
| 261 | +{ | ||
| 262 | + if (pImpl_ == nullptr) { | ||
| 263 | + return UbseCliRegModule::UbseCliStringPromptReply(systemd::error::ALLOCATION_ERROR); | ||
| 264 | + } | ||
| 265 | + return pImpl_->UbseCliDisplayProcessMemDetailImpl(); | ||
| 191 | } | 266 | } |
| 192 | } // namespace ubse::cli::reg | 267 | } // namespace ubse::cli::reg |
| @@ -22,10 +22,11 @@ public: | |||
| 22 | UbseCliMemPid(); | 22 | UbseCliMemPid(); |
| 23 | ~UbseCliMemPid() noexcept; | 23 | ~UbseCliMemPid() noexcept; |
| 24 | 24 | ||
| 25 | - std::shared_ptr<framework::UbseCliResultEcho> UbseCliSetPidThreshold( | 25 | + std::shared_ptr<framework::UbseCliResultEcho> UbseCliSetProcessMem( |
| 26 | - const process_mem::def::ProcessMemPidInfo& pidInfo); | 26 | + const process_mem::def::ProcessMemNewConfigInfo& newConfig); |
| 27 | - std::shared_ptr<framework::UbseCliResultEcho> UbseCliPrintPidInfo(); | 27 | + std::shared_ptr<framework::UbseCliResultEcho> UbseCliRemoveProcessMem(bool isPid, const std::string& identifier); |
| 28 | - std::shared_ptr<framework::UbseCliResultEcho> UbseCliUnsetPid(pid_t pid); | 28 | + std::shared_ptr<framework::UbseCliResultEcho> UbseCliDisplayProcessMemConfig(); |
| 29 | + std::shared_ptr<framework::UbseCliResultEcho> UbseCliDisplayProcessMemDetail(); | ||
| 29 | 30 | ||
| 30 | private: | 31 | private: |
| 31 | class UbseCliMemPidImpl; | 32 | class UbseCliMemPidImpl; |
| @@ -21,23 +21,23 @@ | |||
| 21 | 21 | ||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | + | ||
| 24 | 25 | ||
| 25 | 26 | ||
| 26 | 27 | ||
| 27 | 28 | ||
| 29 | +UBSE_DEFINE_THIS_MODULE("ubse_cli"); | ||
| 30 | + | ||
| 28 | namespace ubse::cli::reg { | 31 | namespace ubse::cli::reg { |
| 29 | UBSE_CLI_REGISTER_MODULE("CLI_MEM_MODULE", UbseCliRegMemModule); | 32 | UBSE_CLI_REGISTER_MODULE("CLI_MEM_MODULE", UbseCliRegMemModule); |
| 30 | using namespace ubse::cli::framework; | 33 | using namespace ubse::cli::framework; |
| 31 | using namespace ubse::serial; | 34 | using namespace ubse::serial; |
| 32 | using namespace ubse::mem::controller; | 35 | using namespace ubse::mem::controller; |
| 33 | 36 | ||
| 34 | -// public option reg | ||
| 35 | constexpr const char* PUBLIC_NAME_OPTION = "name"; | 37 | constexpr const char* PUBLIC_NAME_OPTION = "name"; |
| 36 | -// public option desc | ||
| 37 | constexpr const char* PUBLIC_NAME_OPTION_TIP = | 38 | constexpr const char* PUBLIC_NAME_OPTION_TIP = |
| 38 | "Input a unique name. The name must not exceed 47 characters " | 39 | "Input a unique name. The name must not exceed 47 characters " |
| 39 | "and can only include English letters, numbers, dots, colons, underscores, and hyphens."; | 40 | "and can only include English letters, numbers, dots, colons, underscores, and hyphens."; |
| 40 | -// public option input error | ||
| 41 | constexpr const char* PUBLIC_NAME_OPTION_REQUIRED = | 41 | constexpr const char* PUBLIC_NAME_OPTION_REQUIRED = |
| 42 | "ERROR: The request option -n or --name is required, and the supported name must not exceed 47 characters and can " | 42 | "ERROR: The request option -n or --name is required, and the supported name must not exceed 47 characters and can " |
| 43 | "only include English letters, numbers, dots, colons, underscores, and hyphens."; | 43 | "only include English letters, numbers, dots, colons, underscores, and hyphens."; |
| @@ -45,11 +45,9 @@ constexpr const char* PUBLIC_NAME_PARAM_INVALID = | |||
| 45 | "ERROR: Invalid name. The name must not exceed 47 characters and can only include English letters, numbers, dots, " | 45 | "ERROR: Invalid name. The name must not exceed 47 characters and can only include English letters, numbers, dots, " |
| 46 | "colons, underscores, and hyphens."; | 46 | "colons, underscores, and hyphens."; |
| 47 | 47 | ||
| 48 | -// display memory option reg | ||
| 49 | constexpr const char* DISPLAY_MEM_T_OPTION = "type"; | 48 | constexpr const char* DISPLAY_MEM_T_OPTION = "type"; |
| 50 | constexpr const char* DISPLAY_MEM_BT_OPTION = "borrow-type"; | 49 | constexpr const char* DISPLAY_MEM_BT_OPTION = "borrow-type"; |
| 51 | constexpr const char* DISPLAY_MEM_N_OPTION = "name"; | 50 | constexpr const char* DISPLAY_MEM_N_OPTION = "name"; |
| 52 | -// display memory option desc | ||
| 53 | constexpr const char* DISPLAY_MEM_TYPE_OPTION_TIP = | 51 | constexpr const char* DISPLAY_MEM_TYPE_OPTION_TIP = |
| 54 | "Query the memory information of a specified option. The option is as follows: node_borrow, borrow_detail, " | 52 | "Query the memory information of a specified option. The option is as follows: node_borrow, borrow_detail, " |
| 55 | "node_lend, numa_status, config."; | 53 | "node_lend, numa_status, config."; |
| @@ -60,7 +58,6 @@ constexpr const char* DISPLAY_MEM_NAME_OPTION_TIP = | |||
| 60 | "Input a unique name to filter memory account. The name must not exceed 47 characters and can only include English " | 58 | "Input a unique name to filter memory account. The name must not exceed 47 characters and can only include English " |
| 61 | "letters, numbers, dots, colons, underscores, and hyphens. Supported only when the type parameter is " | 59 | "letters, numbers, dots, colons, underscores, and hyphens. Supported only when the type parameter is " |
| 62 | "borrow_detail."; | 60 | "borrow_detail."; |
| 63 | -// display memory option input error | ||
| 64 | constexpr const char* DISPLAY_MEM_TYPE_OPTION_REQUIRED = | 61 | constexpr const char* DISPLAY_MEM_TYPE_OPTION_REQUIRED = |
| 65 | "ERROR: The request option -t or --type is required, and the supported param is as follows: node_borrow, " | 62 | "ERROR: The request option -t or --type is required, and the supported param is as follows: node_borrow, " |
| 66 | "borrow_detail, node_lend, numa_status, config."; | 63 | "borrow_detail, node_lend, numa_status, config."; |
| @@ -73,24 +70,19 @@ constexpr const char* DISPLAY_MEM_NAME_PARAM_INVALID = PUBLIC_NAME_PARAM_INVALID | |||
| 73 | 70 | ||
| 74 | constexpr const char* PID_OPTION = "pid"; | 71 | constexpr const char* PID_OPTION = "pid"; |
| 75 | constexpr const char* PID_OPTION_TIP = "PID of the target process. Range: 1-4194304"; | 72 | constexpr const char* PID_OPTION_TIP = "PID of the target process. Range: 1-4194304"; |
| 76 | -constexpr const char* EVICT_THRESHOLD_OPTION = "evict-thresh"; | 73 | +constexpr const char* NAME_OPTION = "name"; |
| 77 | -constexpr const char* TARGET_EVICT_THRESHOLD_OPTION = "target-evict-thresh"; | 74 | +constexpr const char* NAME_OPTION_TIP = "Process name (comm). Mutually exclusive with --pid. Max 15 chars, " |
| 78 | -constexpr const char* RECLAIM_THRESHOLD_OPTION = "reclaim-thresh"; | 75 | + "letters/numbers/dots/colons/underscores/hyphens."; |
| 79 | -constexpr const char* EVICT_THRESHOLD_OPTION_TIP = | 76 | + |
| 80 | - "Eviction threshold (%). Eviction is triggered when total memory usage exceeds this ratio. " | ||
| 81 | - "Must be at least 5 higher than reclaim-thresh to avoid oscillation. Range: 1-100"; | ||
| 82 | -constexpr const char* TARGET_EVICT_THRESHOLD_OPTION_TIP = | ||
| 83 | - "Target eviction ratio (%). Target proportion of remote memory to total memory after eviction. Range: 1-100"; | ||
| 84 | -constexpr const char* RECLAIM_THRESHOLD_OPTION_TIP = | ||
| 85 | - "Reclaim threshold (%). All remote memory is migrated back and released when total memory usage " | ||
| 86 | - "drops below this ratio. Must be at least 5 lower than evict-thresh to avoid oscillation. Range: 1-100"; | ||
| 87 | -constexpr const char* SRC_NUMAID_OPTION = "src-numa"; | ||
| 88 | -constexpr const char* SRC_NUMAID_OPTION_TIP = | ||
| 89 | - "Local NUMA node ID (optional). The lending socket is selected on the same plane as this NUMA node"; | ||
| 90 | constexpr const char* SIZE_OPTION = "size"; | 77 | constexpr const char* SIZE_OPTION = "size"; |
| 91 | -constexpr const char* SIZE_OPTION_TIP = "Specify the size. The range is from 128M to 32G. " | 78 | +constexpr const char* REMOTE_RATIO_OPTION = "remote-ratio"; |
| 92 | - "Support up to 2 decimal places. Example: 1G, 512M, 1.5G"; | 79 | +constexpr const char* SIZE_OPTION_TIP = |
| 93 | -constexpr const char* INVALID_SIZE_OPTION_TIP = "ERROR: Invalid size param. Please check the form."; | 80 | + "Size of the process memory. The range is from 128M to 32G, support up to 2 " |
| 81 | + "decimal places (e.g., 8G, 512M, 1.5G). Units: B/K/M/G (binary); default unit is " | ||
| 82 | + "MiB when omitted."; | ||
| 83 | +constexpr const char* REMOTE_RATIO_OPTION_TIP = "Max remote memory ratio. Range: 0.0-1.0"; | ||
| 84 | +constexpr const char* SIZE_INVALID_FORMAT = "ERROR: Invalid size format."; | ||
| 85 | +constexpr const char* REMOTE_RATIO_INVALID_FORMAT = "ERROR: Invalid remote-ratio, range 0.0-1.0"; | ||
| 94 | 86 | ||
| 95 | constexpr const char* DISPLAY_MEM_NAME_OPTION_UNSUPPORT = | 87 | constexpr const char* DISPLAY_MEM_NAME_OPTION_UNSUPPORT = |
| 96 | "ERROR: The -n or --name option only supports when the -t or --type parameter is borrow_detail."; | 88 | "ERROR: The -n or --name option only supports when the -t or --type parameter is borrow_detail."; |
| @@ -103,13 +95,11 @@ constexpr const char* DISPLAY_MEM_ALL_OPTION_TIP = | |||
| 103 | constexpr const char* DISPLAY_MEM_ALL_OPTION_UNSUPPORT = | 95 | constexpr const char* DISPLAY_MEM_ALL_OPTION_UNSUPPORT = |
| 104 | "ERROR: The -a or --all option only supports when the -t or --type parameter is numa_status."; | 96 | "ERROR: The -a or --all option only supports when the -t or --type parameter is numa_status."; |
| 105 | 97 | ||
| 106 | -// create memory option reg | ||
| 107 | constexpr const char* CREATE_MEM_T_OPTION = "type"; | 98 | constexpr const char* CREATE_MEM_T_OPTION = "type"; |
| 108 | constexpr const char* CREATE_MEM_L_OPTION = "link-id"; | 99 | constexpr const char* CREATE_MEM_L_OPTION = "link-id"; |
| 109 | constexpr const char* CREATE_MEM_S_OPTION = "size"; | 100 | constexpr const char* CREATE_MEM_S_OPTION = "size"; |
| 110 | constexpr const char* CREATE_MEM_N_OPTION = PUBLIC_NAME_OPTION; | 101 | constexpr const char* CREATE_MEM_N_OPTION = PUBLIC_NAME_OPTION; |
| 111 | constexpr const char* CREATE_MEM_R_OPTION = "region"; | 102 | constexpr const char* CREATE_MEM_R_OPTION = "region"; |
| 112 | -// create memory option desc | ||
| 113 | constexpr const char* CREATE_MEM_TYPE_OPTION_TIP = "Specify the type. The option is as follows: numa, fd, share."; | 103 | constexpr const char* CREATE_MEM_TYPE_OPTION_TIP = "Specify the type. The option is as follows: numa, fd, share."; |
| 114 | constexpr const char* CREATE_MEM_LINK_OPTION_TIP = | 104 | constexpr const char* CREATE_MEM_LINK_OPTION_TIP = |
| 115 | "Specify the link-id. The format is: nodeID/socketID/portID-nodeID/socketID/portID (e.g., 1/36/0-2/36/0). " | 105 | "Specify the link-id. The format is: nodeID/socketID/portID-nodeID/socketID/portID (e.g., 1/36/0-2/36/0). " |
| @@ -119,7 +109,6 @@ constexpr const char* CREATE_MEM_SIZE_OPTION_TIP = "Specify the size. The minimu | |||
| 119 | constexpr const char* CREATE_MEM_NAME_OPTION_TIP = PUBLIC_NAME_OPTION_TIP; | 109 | constexpr const char* CREATE_MEM_NAME_OPTION_TIP = PUBLIC_NAME_OPTION_TIP; |
| 120 | constexpr const char* CREATE_MEM_REGION_OPTION_TIP = "Specify the shared region node IDs. The format is: node1,node2 " | 110 | constexpr const char* CREATE_MEM_REGION_OPTION_TIP = "Specify the shared region node IDs. The format is: node1,node2 " |
| 121 | "(e.g., 1,2). Supported only when the type parameter is share."; | 111 | "(e.g., 1,2). Supported only when the type parameter is share."; |
| 122 | -// create memory option input error | ||
| 123 | constexpr const char* CREATE_MEM_NAME_OPTION_REQUIRED = PUBLIC_NAME_OPTION_REQUIRED; | 112 | constexpr const char* CREATE_MEM_NAME_OPTION_REQUIRED = PUBLIC_NAME_OPTION_REQUIRED; |
| 124 | constexpr const char* CREATE_MEM_NAME_PARAM_INVALID = PUBLIC_NAME_PARAM_INVALID; | 113 | constexpr const char* CREATE_MEM_NAME_PARAM_INVALID = PUBLIC_NAME_PARAM_INVALID; |
| 125 | constexpr const char* CREATE_MEM_TYPE_OPTION_REQUIRED = | 114 | constexpr const char* CREATE_MEM_TYPE_OPTION_REQUIRED = |
| @@ -139,32 +128,23 @@ constexpr const char* CREATE_MEM_LINK_OPTION_UNSUPPORT = | |||
| 139 | constexpr const char* CREATE_MEM_REGION_OPTION_UNSUPPORT = | 128 | constexpr const char* CREATE_MEM_REGION_OPTION_UNSUPPORT = |
| 140 | "ERROR: The -r or --region option only supports when the -t or --type parameter is share."; | 129 | "ERROR: The -r or --region option only supports when the -t or --type parameter is share."; |
| 141 | 130 | ||
| 142 | -// delete memory option reg | ||
| 143 | constexpr const char* DELETE_MEM_N_OPTION = PUBLIC_NAME_OPTION; | 131 | constexpr const char* DELETE_MEM_N_OPTION = PUBLIC_NAME_OPTION; |
| 144 | constexpr const char* DELETE_MEM_T_OPTION = "type"; | 132 | constexpr const char* DELETE_MEM_T_OPTION = "type"; |
| 145 | -// delete memory option desc | ||
| 146 | constexpr const char* DELETE_MEM_NAME_OPTION_TIP = PUBLIC_NAME_OPTION_TIP; | 133 | constexpr const char* DELETE_MEM_NAME_OPTION_TIP = PUBLIC_NAME_OPTION_TIP; |
| 147 | constexpr const char* DELETE_MEM_TYPE_OPTION_TIP = | 134 | constexpr const char* DELETE_MEM_TYPE_OPTION_TIP = |
| 148 | "Input the type to delete memory. The default value is numa. The option is as follows: fd, numa, share, addr."; | 135 | "Input the type to delete memory. The default value is numa. The option is as follows: fd, numa, share, addr."; |
| 149 | -// delete memory option input error | ||
| 150 | constexpr const char* DELETE_MEM_NAME_OPTION_REQUIRED = PUBLIC_NAME_OPTION_REQUIRED; | 136 | constexpr const char* DELETE_MEM_NAME_OPTION_REQUIRED = PUBLIC_NAME_OPTION_REQUIRED; |
| 151 | constexpr const char* DELETE_MEM_NAME_PARAM_INVALID = PUBLIC_NAME_PARAM_INVALID; | 137 | constexpr const char* DELETE_MEM_NAME_PARAM_INVALID = PUBLIC_NAME_PARAM_INVALID; |
| 152 | constexpr const char* DELETE_MEM_TYPE_PARAM_INVALID = | 138 | constexpr const char* DELETE_MEM_TYPE_PARAM_INVALID = |
| 153 | "ERROR: Invalid type. The supported param is as follows: numa, fd, share, addr."; | 139 | "ERROR: Invalid type. The supported param is as follows: numa, fd, share, addr."; |
| 154 | 140 | ||
| 155 | -// attach memory option reg | ||
| 156 | constexpr const char* ATTACH_MEM_N_OPTION = PUBLIC_NAME_OPTION; | 141 | constexpr const char* ATTACH_MEM_N_OPTION = PUBLIC_NAME_OPTION; |
| 157 | -// attach memory option desc | ||
| 158 | constexpr const char* ATTACH_MEM_NAME_OPTION_TIP = PUBLIC_NAME_OPTION_TIP; | 142 | constexpr const char* ATTACH_MEM_NAME_OPTION_TIP = PUBLIC_NAME_OPTION_TIP; |
| 159 | -// attach memory option input error | ||
| 160 | constexpr const char* ATTACH_MEM_NAME_OPTION_REQUIRED = PUBLIC_NAME_OPTION_REQUIRED; | 143 | constexpr const char* ATTACH_MEM_NAME_OPTION_REQUIRED = PUBLIC_NAME_OPTION_REQUIRED; |
| 161 | constexpr const char* ATTACH_MEM_NAME_PARAM_INVALID = PUBLIC_NAME_PARAM_INVALID; | 144 | constexpr const char* ATTACH_MEM_NAME_PARAM_INVALID = PUBLIC_NAME_PARAM_INVALID; |
| 162 | 145 | ||
| 163 | -// detach memory option reg | ||
| 164 | constexpr const char* DETACH_MEM_N_OPTION = PUBLIC_NAME_OPTION; | 146 | constexpr const char* DETACH_MEM_N_OPTION = PUBLIC_NAME_OPTION; |
| 165 | -// detach memory option desc | ||
| 166 | constexpr const char* DETACH_MEM_NAME_OPTION_TIP = PUBLIC_NAME_OPTION_TIP; | 147 | constexpr const char* DETACH_MEM_NAME_OPTION_TIP = PUBLIC_NAME_OPTION_TIP; |
| 167 | -// detach memory option input error | ||
| 168 | constexpr const char* DETACH_MEM_NAME_OPTION_REQUIRED = PUBLIC_NAME_OPTION_REQUIRED; | 148 | constexpr const char* DETACH_MEM_NAME_OPTION_REQUIRED = PUBLIC_NAME_OPTION_REQUIRED; |
| 169 | constexpr const char* DETACH_MEM_NAME_PARAM_INVALID = PUBLIC_NAME_PARAM_INVALID; | 149 | constexpr const char* DETACH_MEM_NAME_PARAM_INVALID = PUBLIC_NAME_PARAM_INVALID; |
| 170 | 150 | ||
| @@ -173,7 +153,6 @@ constexpr const char* BORROW_DETAIL_EMPTY = "INFO: The borrow detail information | |||
| 173 | 153 | ||
| 174 | constexpr const char* SERIALIZATION_ERROR = "ERROR: Serialization failed."; | 154 | constexpr const char* SERIALIZATION_ERROR = "ERROR: Serialization failed."; |
| 175 | constexpr const char* DE_SERIALIZATION_ERROR = "ERROR: Deserialization failed."; | 155 | constexpr const char* DE_SERIALIZATION_ERROR = "ERROR: Deserialization failed."; |
| 176 | -constexpr const char* MEMORY_INTERNAL_ERROR = "ERROR: Internal error with error code "; | ||
| 177 | constexpr const char* MEMORY_EMPTY_ERROR = "ERROR: Failed to obtain memory information"; | 156 | constexpr const char* MEMORY_EMPTY_ERROR = "ERROR: Failed to obtain memory information"; |
| 178 | constexpr const char* SET_TIMER_ERROR = "ERROR: Set timer failed. "; | 157 | constexpr const char* SET_TIMER_ERROR = "ERROR: Set timer failed. "; |
| 179 | 158 | ||
| @@ -185,16 +164,22 @@ const int8_t MEM_SUCCESS_CODE = UBSE_OK; | |||
| 185 | const int MAX_NAME_LENGTH = 47; | 164 | const int MAX_NAME_LENGTH = 47; |
| 186 | const uint32_t REQUEST_BUFFER_CAPACITY = 8; | 165 | const uint32_t REQUEST_BUFFER_CAPACITY = 8; |
| 187 | const int8_t RETRY_WAIT_TIME = 10; | 166 | const int8_t RETRY_WAIT_TIME = 10; |
| 188 | -constexpr size_t NODE_LENGTH = 80; // hostname(slot_id), hostname最长为64 | 167 | +constexpr size_t NODE_LENGTH = 80; |
| 189 | 168 | ||
| 190 | std::string FormatHostnameSlot(const std::string& hostname, uint32_t slotId) | 169 | std::string FormatHostnameSlot(const std::string& hostname, uint32_t slotId) |
| 191 | { | 170 | { |
| 192 | return (hostname.empty() ? "-" : hostname) + "(" + std::to_string(slotId) + ")"; | 171 | return (hostname.empty() ? "-" : hostname) + "(" + std::to_string(slotId) + ")"; |
| 193 | } | 172 | } |
| 194 | 173 | ||
| 174 | +std::string FormatNameParamError(int maxLength) | ||
| 175 | +{ | ||
| 176 | + return "ERROR: Invalid name. The name must not exceed " + std::to_string(maxLength) + | ||
| 177 | + " characters and can only include English letters, numbers, dots, colons, underscores, and hyphens."; | ||
| 178 | +} | ||
| 179 | + | ||
| 195 | std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::UbseCliQueryNodeBorrowInfo() | 180 | std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::UbseCliQueryNodeBorrowInfo() |
| 196 | { | 181 | { |
| 197 | - UbseSerialization ubse_req_serial(REQUEST_BUFFER_CAPACITY); // Create a blank request. | 182 | + UbseSerialization ubse_req_serial(REQUEST_BUFFER_CAPACITY); |
| 198 | ubse_api_buffer_t ubse_req_buffer{ubse_req_serial.GetBuffer(), static_cast<uint32_t>(ubse_req_serial.GetLength())}; | 183 | ubse_api_buffer_t ubse_req_buffer{ubse_req_serial.GetBuffer(), static_cast<uint32_t>(ubse_req_serial.GetLength())}; |
| 199 | ubse_api_buffer_t ubse_res_buffer{}; | 184 | ubse_api_buffer_t ubse_res_buffer{}; |
| 200 | uint32_t ret = ubse_invoke_call(MEM_MODULE_CODE, MEM_NODE_BORROW_OP_CODE, &ubse_req_buffer, &ubse_res_buffer); | 185 | uint32_t ret = ubse_invoke_call(MEM_MODULE_CODE, MEM_NODE_BORROW_OP_CODE, &ubse_req_buffer, &ubse_res_buffer); |
| @@ -327,6 +312,57 @@ std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::UbseCliProcessNumaStatus | |||
| 327 | return UbseCliProcessNumaStatusDataWithHugepages(deSerialization, numaInfoSize); | 312 | return UbseCliProcessNumaStatusDataWithHugepages(deSerialization, numaInfoSize); |
| 328 | } | 313 | } |
| 329 | 314 | ||
| 315 | +static void FillNumaStatusHeader(UbseCliResBuilder& builder, size_t row, bool show2MColumns, | ||
| 316 | + const std::string& largeTotalCol, const std::string& largeFreeCol) | ||
| 317 | +{ | ||
| 318 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_1, "node"); | ||
| 319 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_2, "numa"); | ||
| 320 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_3, "total"); | ||
| 321 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_4, "used"); | ||
| 322 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_5, "free"); | ||
| 323 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_6, "used_percent"); | ||
| 324 | + if (show2MColumns) { | ||
| 325 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_7, "2M_total"); | ||
| 326 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_8, "2M_free"); | ||
| 327 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_9, largeTotalCol); | ||
| 328 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_10, largeFreeCol); | ||
| 329 | + } else { | ||
| 330 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_7, largeTotalCol); | ||
| 331 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_8, largeFreeCol); | ||
| 332 | + } | ||
| 333 | +} | ||
| 334 | + | ||
| 335 | +static uint32_t FillNumaStatusRow(UbseCliResBuilder& builder, size_t row, bool show2MColumns, | ||
| 336 | + UbseDeSerialization& deSerialization) | ||
| 337 | +{ | ||
| 338 | + UbseNumaStatusInfo numaInfo{}; | ||
| 339 | + std::string nr2M; | ||
| 340 | + std::string free2M; | ||
| 341 | + std::string nrLarge; | ||
| 342 | + std::string freeLarge; | ||
| 343 | + deSerialization >> numaInfo.node >> numaInfo.numa >> numaInfo.total >> numaInfo.used >> numaInfo.freeSize >> | ||
| 344 | + numaInfo.used_percent >> nr2M >> free2M >> nrLarge >> freeLarge; | ||
| 345 | + if (!deSerialization.Check()) { | ||
| 346 | + return UBSE_ERROR; | ||
| 347 | + } | ||
| 348 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_1, numaInfo.node); | ||
| 349 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_2, numaInfo.numa); | ||
| 350 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_3, numaInfo.total); | ||
| 351 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_4, numaInfo.used); | ||
| 352 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_5, numaInfo.freeSize); | ||
| 353 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_6, numaInfo.used_percent); | ||
| 354 | + if (show2MColumns) { | ||
| 355 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_7, nr2M); | ||
| 356 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_8, free2M); | ||
| 357 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_9, nrLarge); | ||
| 358 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_10, freeLarge); | ||
| 359 | + } else { | ||
| 360 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_7, nrLarge); | ||
| 361 | + builder.UbseCliSetCellData(row, UBSE_CLI_NUM_8, freeLarge); | ||
| 362 | + } | ||
| 363 | + return UBSE_OK; | ||
| 364 | +} | ||
| 365 | + | ||
| 330 | std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::UbseCliProcessNumaStatusDataWithHugepages( | 366 | std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::UbseCliProcessNumaStatusDataWithHugepages( |
| 331 | UbseDeSerialization& deSerialization, size_t numaInfoSize) | 367 | UbseDeSerialization& deSerialization, size_t numaInfoSize) |
| 332 | { | 368 | { |
| @@ -337,55 +373,18 @@ std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::UbseCliProcessNumaStatus | |||
| 337 | } | 373 | } |
| 338 | std::string largeTotalCol = pageSizeType + "_total"; | 374 | std::string largeTotalCol = pageSizeType + "_total"; |
| 339 | std::string largeFreeCol = pageSizeType + "_free"; | 375 | std::string largeFreeCol = pageSizeType + "_free"; |
| 340 | - // 64k环境(pageSizeType=="512M")下不展示2M大页,仅展示512M大页;其他环境(如4k, 1G)展示全部4列大页 | ||
| 341 | bool show2MColumns = (pageSizeType != "512M"); | 376 | bool show2MColumns = (pageSizeType != "512M"); |
| 342 | size_t colCount = show2MColumns ? UBSE_CLI_NUM_10 : UBSE_CLI_NUM_8; | 377 | size_t colCount = show2MColumns ? UBSE_CLI_NUM_10 : UBSE_CLI_NUM_8; |
| 343 | UbseCliResBuilder variable_cell_builder(colCount, NODE_LENGTH); | 378 | UbseCliResBuilder variable_cell_builder(colCount, NODE_LENGTH); |
| 344 | size_t row = variable_cell_builder.UbseCliAddRow(); | 379 | size_t row = variable_cell_builder.UbseCliAddRow(); |
| 345 | variable_cell_builder.UbseCliAddlineSeparate(row); | 380 | variable_cell_builder.UbseCliAddlineSeparate(row); |
| 346 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_1, "node"); | 381 | + FillNumaStatusHeader(variable_cell_builder, row, show2MColumns, largeTotalCol, largeFreeCol); |
| 347 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_2, "numa"); | ||
| 348 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_3, "total"); | ||
| 349 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_4, "used"); | ||
| 350 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_5, "free"); | ||
| 351 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_6, "used_percent"); | ||
| 352 | - if (show2MColumns) { | ||
| 353 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_7, "2M_total"); | ||
| 354 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_8, "2M_free"); | ||
| 355 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_9, largeTotalCol); | ||
| 356 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_10, largeFreeCol); | ||
| 357 | - } else { | ||
| 358 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_7, largeTotalCol); | ||
| 359 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_8, largeFreeCol); | ||
| 360 | - } | ||
| 361 | variable_cell_builder.UbseCliAddBottomlineSeparate(); | 382 | variable_cell_builder.UbseCliAddBottomlineSeparate(); |
| 362 | for (size_t i = 0; i < numaInfoSize; i++) { | 383 | for (size_t i = 0; i < numaInfoSize; i++) { |
| 363 | row = variable_cell_builder.UbseCliAddRow(); | 384 | row = variable_cell_builder.UbseCliAddRow(); |
| 364 | - UbseNumaStatusInfo numaInfo{}; | 385 | + if (FillNumaStatusRow(variable_cell_builder, row, show2MColumns, deSerialization) != UBSE_OK) { |
| 365 | - std::string nr2M; | ||
| 366 | - std::string free2M; | ||
| 367 | - std::string nrLarge; | ||
| 368 | - std::string freeLarge; | ||
| 369 | - deSerialization >> numaInfo.node >> numaInfo.numa >> numaInfo.total >> numaInfo.used >> numaInfo.freeSize >> | ||
| 370 | - numaInfo.used_percent >> nr2M >> free2M >> nrLarge >> freeLarge; | ||
| 371 | - if (!deSerialization.Check()) { | ||
| 372 | return UbseCliStringPromptReply(DE_SERIALIZATION_ERROR); | 386 | return UbseCliStringPromptReply(DE_SERIALIZATION_ERROR); |
| 373 | } | 387 | } |
| 374 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_1, numaInfo.node); | ||
| 375 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_2, numaInfo.numa); | ||
| 376 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_3, numaInfo.total); | ||
| 377 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_4, numaInfo.used); | ||
| 378 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_5, numaInfo.freeSize); | ||
| 379 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_6, numaInfo.used_percent); | ||
| 380 | - if (show2MColumns) { | ||
| 381 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_7, nr2M); | ||
| 382 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_8, free2M); | ||
| 383 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_9, nrLarge); | ||
| 384 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_10, freeLarge); | ||
| 385 | - } else { | ||
| 386 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_7, nrLarge); | ||
| 387 | - variable_cell_builder.UbseCliSetCellData(row, UBSE_CLI_NUM_8, freeLarge); | ||
| 388 | - } | ||
| 389 | } | 388 | } |
| 390 | variable_cell_builder.UbseCliAddBottomlineSeparate(); | 389 | variable_cell_builder.UbseCliAddBottomlineSeparate(); |
| 391 | return UbseCliVariableCelReply(variable_cell_builder.UbseCliVariableCellBuild()); | 390 | return UbseCliVariableCelReply(variable_cell_builder.UbseCliVariableCellBuild()); |
| @@ -527,14 +526,18 @@ std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::DisplayProcessMemFunc( | |||
| 527 | { | 526 | { |
| 528 | auto it = params.find("type"); | 527 | auto it = params.find("type"); |
| 529 | if (it == params.end()) { | 528 | if (it == params.end()) { |
| 530 | - return UbseCliStringPromptReply( | 529 | + return UbseCliStringPromptReply("ERROR: The request option -t or --type is required, and the supported params " |
| 531 | - "ERROR: The request option -t or --type is required, and the supported param is as follows: config."); | 530 | + "are as follows: config, proc_detail."); |
| 532 | } | 531 | } |
| 533 | - if (it->second != "config") { | 532 | + if (it->second != "config" && it->second != "proc_detail") { |
| 534 | - return UbseCliStringPromptReply("ERROR: Invalid type. The supported param is as follows: config."); | 533 | + return UbseCliStringPromptReply( |
| 534 | + "ERROR: Invalid type. The supported params are as follows: config, proc_detail."); | ||
| 535 | } | 535 | } |
| 536 | UbseCliMemPid memPid{}; | 536 | UbseCliMemPid memPid{}; |
| 537 | - return memPid.UbseCliPrintPidInfo(); | 537 | + if (it->second == "proc_detail") { |
| 538 | + return memPid.UbseCliDisplayProcessMemDetail(); | ||
| 539 | + } | ||
| 540 | + return memPid.UbseCliDisplayProcessMemConfig(); | ||
| 538 | } | 541 | } |
| 539 | 542 | ||
| 540 | std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::UbseCliCheckMemoryStatusFunc( | 543 | std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::UbseCliCheckMemoryStatusFunc( |
| @@ -634,7 +637,7 @@ bool SizeIsMatch(const std::string& str, size_t& size) | |||
| 634 | multiplier = 1024ULL * 1024ULL * 1024ULL; | 637 | multiplier = 1024ULL * 1024ULL * 1024ULL; |
| 635 | } | 638 | } |
| 636 | if (tmpSize > std::numeric_limits<size_t>::max() / multiplier) { | 639 | if (tmpSize > std::numeric_limits<size_t>::max() / multiplier) { |
| 637 | - return false; // 溢出 | 640 | + return false; |
| 638 | } | 641 | } |
| 639 | 642 | ||
| 640 | size = tmpSize * multiplier; | 643 | size = tmpSize * multiplier; |
| @@ -643,17 +646,30 @@ bool SizeIsMatch(const std::string& str, size_t& size) | |||
| 643 | 646 | ||
| 644 | bool CheckName(const std::string& name) | 647 | bool CheckName(const std::string& name) |
| 645 | { | 648 | { |
| 646 | - if (name.length() > MAX_NAME_LENGTH || name.empty()) { | 649 | + return CheckName(name, MAX_NAME_LENGTH); |
| 650 | +} | ||
| 651 | + | ||
| 652 | +bool CheckName(const std::string& name, int maxLength) | ||
| 653 | +{ | ||
| 654 | + if (name.length() > static_cast<size_t>(maxLength) || name.empty()) { | ||
| 647 | return false; | 655 | return false; |
| 648 | } | 656 | } |
| 649 | for (char c : name) { | 657 | for (char c : name) { |
| 650 | - if (!isdigit(c) && !isalpha(c) && c != '_' && c != '-' && c != '.' && c != ':') { | 658 | + const unsigned char uc = static_cast<unsigned char>(c); |
| 659 | + if (!isdigit(uc) && !isalpha(uc) && c != '_' && c != '-' && c != '.' && c != ':') { | ||
| 651 | return false; | 660 | return false; |
| 652 | } | 661 | } |
| 653 | } | 662 | } |
| 654 | return true; | 663 | return true; |
| 655 | } | 664 | } |
| 656 | 665 | ||
| 666 | +constexpr int MAX_PROC_NAME_LENGTH = 15; | ||
| 667 | + | ||
| 668 | +bool CheckProcName(const std::string& name) | ||
| 669 | +{ | ||
| 670 | + return CheckName(name, MAX_PROC_NAME_LENGTH); | ||
| 671 | +} | ||
| 672 | + | ||
| 657 | static std::string FormatMemoryInfoReply(const std::string& name, int64_t numaId, const std::string& importNode, | 673 | static std::string FormatMemoryInfoReply(const std::string& name, int64_t numaId, const std::string& importNode, |
| 658 | const std::string& exportNode) | 674 | const std::string& exportNode) |
| 659 | { | 675 | { |
| @@ -723,7 +739,6 @@ ParsedResponse ParseResponseBuffer(const ubse_api_buffer_t& responseBuffer) | |||
| 723 | return result; | 739 | return result; |
| 724 | } | 740 | } |
| 725 | 741 | ||
| 726 | -// 处理 IPC 超时后的轮询查询逻辑 | ||
| 727 | std::shared_ptr<UbseCliResultEcho> HandleTimeoutRetry(const std::string& name) | 742 | std::shared_ptr<UbseCliResultEcho> HandleTimeoutRetry(const std::string& name) |
| 728 | { | 743 | { |
| 729 | while (true) { | 744 | while (true) { |
| @@ -786,7 +801,6 @@ bool ParseRegionString(const std::string& regionStr, std::vector<uint32_t>& regi | |||
| 786 | try { | 801 | try { |
| 787 | size_t pos = 0; | 802 | size_t pos = 0; |
| 788 | unsigned long value = std::stoul(item, &pos); | 803 | unsigned long value = std::stoul(item, &pos); |
| 789 | - // 检查是否完整解析 | ||
| 790 | if (pos != item.length()) { | 804 | if (pos != item.length()) { |
| 791 | return false; | 805 | return false; |
| 792 | } | 806 | } |
| @@ -795,7 +809,7 @@ bool ParseRegionString(const std::string& regionStr, std::vector<uint32_t>& regi | |||
| 795 | } | 809 | } |
| 796 | regions.push_back(static_cast<uint32_t>(value)); | 810 | regions.push_back(static_cast<uint32_t>(value)); |
| 797 | } catch (...) { | 811 | } catch (...) { |
| 798 | - return false; // 不是有效数字 | 812 | + return false; |
| 799 | } | 813 | } |
| 800 | } | 814 | } |
| 801 | 815 | ||
| @@ -874,22 +888,18 @@ std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::CreateMemoryFunc( | |||
| 874 | if (type != "numa" && type != "share" && type != "fd") { | 888 | if (type != "numa" && type != "share" && type != "fd") { |
| 875 | return UbseCliStringPromptReply(CREATE_MEM_TYPE_PARAM_INVALID); | 889 | return UbseCliStringPromptReply(CREATE_MEM_TYPE_PARAM_INVALID); |
| 876 | } | 890 | } |
| 877 | - // 验证参数白名单 | ||
| 878 | auto allowedParams = GetAllowedParams(type); | 891 | auto allowedParams = GetAllowedParams(type); |
| 879 | std::string errorMsg; | 892 | std::string errorMsg; |
| 880 | if (!ValidateParamsWhitelist(params, allowedParams, errorMsg)) { | 893 | if (!ValidateParamsWhitelist(params, allowedParams, errorMsg)) { |
| 881 | return UbseCliStringPromptReply(errorMsg); | 894 | return UbseCliStringPromptReply(errorMsg); |
| 882 | } | 895 | } |
| 883 | - // 验证公共参数 | ||
| 884 | std::string name; | 896 | std::string name; |
| 885 | size_t size{}; | 897 | size_t size{}; |
| 886 | if (!ValidateCommonParams(params, name, size, errorMsg)) { | 898 | if (!ValidateCommonParams(params, name, size, errorMsg)) { |
| 887 | return UbseCliStringPromptReply(errorMsg); | 899 | return UbseCliStringPromptReply(errorMsg); |
| 888 | } | 900 | } |
| 889 | - // 根据类型创建 | ||
| 890 | UbseCliMemCreate memCreate{}; | 901 | UbseCliMemCreate memCreate{}; |
| 891 | if (type == "numa") { | 902 | if (type == "numa") { |
| 892 | - // NUMA 类型:可以有 link 参数 | ||
| 893 | auto itLink = params.find(CREATE_MEM_L_OPTION); | 903 | auto itLink = params.find(CREATE_MEM_L_OPTION); |
| 894 | if (itLink != params.end() && !LinkIsMatch(itLink->second)) { | 904 | if (itLink != params.end() && !LinkIsMatch(itLink->second)) { |
| 895 | return UbseCliStringPromptReply(CREATE_MEM_LINK_PARAM_INVALID); | 905 | return UbseCliStringPromptReply(CREATE_MEM_LINK_PARAM_INVALID); |
| @@ -897,7 +907,6 @@ std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::CreateMemoryFunc( | |||
| 897 | auto linkValue = (itLink != params.end()) ? itLink->second : ""; | 907 | auto linkValue = (itLink != params.end()) ? itLink->second : ""; |
| 898 | return memCreate.UbseCliCreateNumaMem(name, size, linkValue); | 908 | return memCreate.UbseCliCreateNumaMem(name, size, linkValue); |
| 899 | } else if (type == "share") { | 909 | } else if (type == "share") { |
| 900 | - // SHARE 类型:可以有 region 参数 | ||
| 901 | auto itRegion = params.find(CREATE_MEM_R_OPTION); | 910 | auto itRegion = params.find(CREATE_MEM_R_OPTION); |
| 902 | std::vector<uint32_t> region{}; | 911 | std::vector<uint32_t> region{}; |
| 903 | if (itRegion != params.end() && !ParseRegionString(itRegion->second, region)) { | 912 | if (itRegion != params.end() && !ParseRegionString(itRegion->second, region)) { |
| @@ -905,7 +914,6 @@ std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::CreateMemoryFunc( | |||
| 905 | } | 914 | } |
| 906 | return memCreate.UbseCliCreateShareMem(name, size, region); | 915 | return memCreate.UbseCliCreateShareMem(name, size, region); |
| 907 | } else { | 916 | } else { |
| 908 | - // type == "fd" | ||
| 909 | return memCreate.UbseCliCreateFdMem(name, size); | 917 | return memCreate.UbseCliCreateFdMem(name, size); |
| 910 | } | 918 | } |
| 911 | } | 919 | } |
| @@ -915,7 +923,9 @@ UbseCliCommandInfo UbseCliRegMemModule::DisplayProcessMem() | |||
| 915 | UbseCliRegBuilder builder; | 923 | UbseCliRegBuilder builder; |
| 916 | builder.UbseCliSetCommand("display") | 924 | builder.UbseCliSetCommand("display") |
| 917 | .UbseCliSetType("process-mem") | 925 | .UbseCliSetType("process-mem") |
| 918 | - .UbseCliAddOption("t", "type", "Query the process memory configuration. The option is as follows: config.") | 926 | + .UbseCliAddOption("t", "type", |
| 927 | + "Query the process memory configuration. The option is as follows: config, " | ||
| 928 | + "proc_detail.") | ||
| 919 | .UbseCliSetFunc(DisplayProcessMemFunc); | 929 | .UbseCliSetFunc(DisplayProcessMemFunc); |
| 920 | return builder.UbseCliBuild(); | 930 | return builder.UbseCliBuild(); |
| 921 | } | 931 | } |
| @@ -926,12 +936,10 @@ UbseCliCommandInfo UbseCliRegMemModule::ChangeMemory() | |||
| 926 | builder.UbseCliSetCommand("change") | 936 | builder.UbseCliSetCommand("change") |
| 927 | .UbseCliSetType("process-mem") | 937 | .UbseCliSetType("process-mem") |
| 928 | .UbseCliAddOption("p", PID_OPTION, PID_OPTION_TIP) | 938 | .UbseCliAddOption("p", PID_OPTION, PID_OPTION_TIP) |
| 929 | - .UbseCliAddOption("e", EVICT_THRESHOLD_OPTION, EVICT_THRESHOLD_OPTION_TIP) | 939 | + .UbseCliAddOption("n", NAME_OPTION, NAME_OPTION_TIP) |
| 930 | - .UbseCliAddOption("t", TARGET_EVICT_THRESHOLD_OPTION, TARGET_EVICT_THRESHOLD_OPTION_TIP) | ||
| 931 | - .UbseCliAddOption("r", RECLAIM_THRESHOLD_OPTION, RECLAIM_THRESHOLD_OPTION_TIP) | ||
| 932 | .UbseCliAddOption("s", SIZE_OPTION, SIZE_OPTION_TIP) | 940 | .UbseCliAddOption("s", SIZE_OPTION, SIZE_OPTION_TIP) |
| 933 | - .UbseCliAddOption("sn", SRC_NUMAID_OPTION, SRC_NUMAID_OPTION_TIP) | 941 | + .UbseCliAddOption("r", REMOTE_RATIO_OPTION, REMOTE_RATIO_OPTION_TIP) |
| 934 | - .UbseCliSetFunc(PidSetThresholdFunc); | 942 | + .UbseCliSetFunc(ChangeProcessMemFunc); |
| 935 | return builder.UbseCliBuild(); | 943 | return builder.UbseCliBuild(); |
| 936 | } | 944 | } |
| 937 | 945 | ||
| @@ -965,23 +973,45 @@ static std::string ValidatePid(const std::string& pidStr) | |||
| 965 | return ""; | 973 | return ""; |
| 966 | } | 974 | } |
| 967 | 975 | ||
| 968 | -std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::PidUnSetFunc(const std::map<std::string, std::string>& params) | 976 | +static std::shared_ptr<UbseCliResultEcho> DoRemoveByPid(const std::string& pidStr) |
| 977 | +{ | ||
| 978 | + UbseCliMemPid memPid{}; | ||
| 979 | + auto removeResult = memPid.UbseCliRemoveProcessMem(true, pidStr); | ||
| 980 | + return removeResult; | ||
| 981 | +} | ||
| 982 | + | ||
| 983 | +static std::shared_ptr<UbseCliResultEcho> DoRemoveByName(const std::string& name) | ||
| 984 | +{ | ||
| 985 | + UbseCliMemPid memPid{}; | ||
| 986 | + auto removeResult = memPid.UbseCliRemoveProcessMem(false, name); | ||
| 987 | + return removeResult; | ||
| 988 | +} | ||
| 989 | + | ||
| 990 | +std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::RemoveProcessMemFunc( | ||
| 991 | + const std::map<std::string, std::string>& params) | ||
| 969 | { | 992 | { |
| 970 | if (!UbseCliRegModule::DisableTimeoutTimer()) { | 993 | if (!UbseCliRegModule::DisableTimeoutTimer()) { |
| 971 | return UbseCliStringPromptReply(SET_TIMER_ERROR); | 994 | return UbseCliStringPromptReply(SET_TIMER_ERROR); |
| 972 | } | 995 | } |
| 973 | - auto pid = params.find(PID_OPTION); | 996 | + auto pidIt = params.find(PID_OPTION); |
| 974 | - if (pid == params.end()) { | 997 | + auto nameIt = params.find(NAME_OPTION); |
| 975 | - return UbseCliStringPromptReply("ERROR: The request option -p or --pid is required."); | 998 | + if (pidIt == params.end() && nameIt == params.end()) { |
| 999 | + return UbseCliStringPromptReply("ERROR: The request option -p/--pid or -n/--name is required."); | ||
| 976 | } | 1000 | } |
| 977 | - auto errMsg = ValidatePid(pid->second); | 1001 | + if (pidIt != params.end() && nameIt != params.end()) { |
| 1002 | + return UbseCliStringPromptReply("ERROR: --pid and --name are mutually exclusive."); | ||
| 1003 | + } | ||
| 1004 | + if (nameIt != params.end()) { | ||
| 1005 | + if (!CheckProcName(nameIt->second)) { | ||
| 1006 | + return UbseCliStringPromptReply(FormatNameParamError(MAX_PROC_NAME_LENGTH)); | ||
| 1007 | + } | ||
| 1008 | + return DoRemoveByName(nameIt->second); | ||
| 1009 | + } | ||
| 1010 | + auto errMsg = ValidatePid(pidIt->second); | ||
| 978 | if (!errMsg.empty()) { | 1011 | if (!errMsg.empty()) { |
| 979 | - return UbseCliStringPromptReply(errMsg); | 1012 | + return UbseCliStringPromptReply("ERROR: " + errMsg); |
| 980 | } | 1013 | } |
| 981 | - pid_t tmpPid = 0; | 1014 | + return DoRemoveByPid(pidIt->second); |
| 982 | - utils::ConvertStrToInt(pid->second, tmpPid); | ||
| 983 | - UbseCliMemPid memPid{}; | ||
| 984 | - return memPid.UbseCliUnsetPid(tmpPid); | ||
| 985 | } | 1015 | } |
| 986 | 1016 | ||
| 987 | UbseCliCommandInfo UbseCliRegMemModule::RemoveMemory() | 1017 | UbseCliCommandInfo UbseCliRegMemModule::RemoveMemory() |
| @@ -990,7 +1020,8 @@ UbseCliCommandInfo UbseCliRegMemModule::RemoveMemory() | |||
| 990 | builder.UbseCliSetCommand("remove") | 1020 | builder.UbseCliSetCommand("remove") |
| 991 | .UbseCliSetType("process-mem") | 1021 | .UbseCliSetType("process-mem") |
| 992 | .UbseCliAddOption("p", PID_OPTION, PID_OPTION_TIP) | 1022 | .UbseCliAddOption("p", PID_OPTION, PID_OPTION_TIP) |
| 993 | - .UbseCliSetFunc(PidUnSetFunc); | 1023 | + .UbseCliAddOption("n", NAME_OPTION, NAME_OPTION_TIP) |
| 1024 | + .UbseCliSetFunc(RemoveProcessMemFunc); | ||
| 994 | return builder.UbseCliBuild(); | 1025 | return builder.UbseCliBuild(); |
| 995 | } | 1026 | } |
| 996 | 1027 | ||
| @@ -1019,7 +1050,7 @@ std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::DeleteMemoryFunc( | |||
| 1019 | if (!CheckName(name->second)) { | 1050 | if (!CheckName(name->second)) { |
| 1020 | return UbseCliStringPromptReply(DELETE_MEM_NAME_PARAM_INVALID); | 1051 | return UbseCliStringPromptReply(DELETE_MEM_NAME_PARAM_INVALID); |
| 1021 | } | 1052 | } |
| 1022 | - std::string deleteType = "numa"; // 默认使用numa | 1053 | + std::string deleteType = "numa"; |
| 1023 | auto type = params.find(DELETE_MEM_T_OPTION); | 1054 | auto type = params.find(DELETE_MEM_T_OPTION); |
| 1024 | if (type != params.end()) { | 1055 | if (type != params.end()) { |
| 1025 | if (!CheckDeleteType(type->second)) { | 1056 | if (!CheckDeleteType(type->second)) { |
| @@ -1037,7 +1068,6 @@ std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::DeleteMemoryFunc( | |||
| 1037 | UbseCliWaitIndicator waitIndicator("Deleting memory"); | 1068 | UbseCliWaitIndicator waitIndicator("Deleting memory"); |
| 1038 | uint32_t ret = ubse_invoke_call(MEM_MODULE_CODE, MEMORY_DELETE_OP_CODE, &reqBuffer, &resBuffer); | 1069 | uint32_t ret = ubse_invoke_call(MEM_MODULE_CODE, MEMORY_DELETE_OP_CODE, &reqBuffer, &resBuffer); |
| 1039 | UbseCliBufferGuard ubseCliBufferGuard(resBuffer); | 1070 | UbseCliBufferGuard ubseCliBufferGuard(resBuffer); |
| 1040 | - // 处理超时错误 | ||
| 1041 | if (ret == UBSE_ERR_TIMED_OUT) { | 1071 | if (ret == UBSE_ERR_TIMED_OUT) { |
| 1042 | return HandleTimeoutRetry(name->second); | 1072 | return HandleTimeoutRetry(name->second); |
| 1043 | } | 1073 | } |
| @@ -1045,14 +1075,12 @@ std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::DeleteMemoryFunc( | |||
| 1045 | return UbseCliStringPromptReply(GetErrorMessage(ret)); | 1075 | return UbseCliStringPromptReply(GetErrorMessage(ret)); |
| 1046 | } | 1076 | } |
| 1047 | UbseDeSerialization deserial{resBuffer.buffer, resBuffer.length}; | 1077 | UbseDeSerialization deserial{resBuffer.buffer, resBuffer.length}; |
| 1048 | - // 成功回显 | ||
| 1049 | uint32_t errorCode{0}; | 1078 | uint32_t errorCode{0}; |
| 1050 | std::string errMsg; | 1079 | std::string errMsg; |
| 1051 | deserial >> errorCode >> errMsg; | 1080 | deserial >> errorCode >> errMsg; |
| 1052 | if (deserial.Check() && errorCode == MEM_SUCCESS_CODE) { | 1081 | if (deserial.Check() && errorCode == MEM_SUCCESS_CODE) { |
| 1053 | return UbseCliStringPromptReply("Delete successfully"); | 1082 | return UbseCliStringPromptReply("Delete successfully"); |
| 1054 | } | 1083 | } |
| 1055 | - // 失败回显 | ||
| 1056 | if (deserial.Check() && errorCode == UBSE_ERR_NOT_SUPPORTED) { | 1084 | if (deserial.Check() && errorCode == UBSE_ERR_NOT_SUPPORTED) { |
| 1057 | return UbseCliStringPromptReply(GetErrorMessage(errorCode)); | 1085 | return UbseCliStringPromptReply(GetErrorMessage(errorCode)); |
| 1058 | } | 1086 | } |
| @@ -1152,57 +1180,15 @@ UbseCliCommandInfo UbseCliRegMemModule::ShmMemoryDetach() | |||
| 1152 | return builder.UbseCliBuild(); | 1180 | return builder.UbseCliBuild(); |
| 1153 | } | 1181 | } |
| 1154 | 1182 | ||
| 1155 | -// Threshold limits | 1183 | +static constexpr uint64_t PROCESS_MEM_SIZE_MIN = 128ULL * 1024 * 1024; |
| 1156 | -static constexpr int THRESHOLD_PERCENT_MAX = 100; | 1184 | +static constexpr uint64_t PROCESS_MEM_SIZE_MAX = 32ULL * 1024 * 1024 * 1024; |
| 1157 | -static constexpr int MIN_EVICT_RECLAIM_DELTA = 5; | ||
| 1158 | 1185 | ||
| 1159 | -// process_mem size range: 128M ~ 32G | ||
| 1160 | -static constexpr uint64_t PROCESS_MEM_SIZE_MIN = 128ULL * 1024 * 1024; // 128M | ||
| 1161 | -static constexpr uint64_t PROCESS_MEM_SIZE_MAX = 32ULL * 1024 * 1024 * 1024; // 32G | ||
| 1162 | - | ||
| 1163 | -// Size conversion constants | ||
| 1164 | static constexpr uint64_t BYTES_PER_KIB = 1024ULL; | 1186 | static constexpr uint64_t BYTES_PER_KIB = 1024ULL; |
| 1165 | static constexpr uint64_t BYTES_PER_MIB = BYTES_PER_KIB * BYTES_PER_KIB; | 1187 | static constexpr uint64_t BYTES_PER_MIB = BYTES_PER_KIB * BYTES_PER_KIB; |
| 1166 | static constexpr uint64_t BYTES_PER_GIB = BYTES_PER_KIB * BYTES_PER_KIB * BYTES_PER_KIB; | 1188 | static constexpr uint64_t BYTES_PER_GIB = BYTES_PER_KIB * BYTES_PER_KIB * BYTES_PER_KIB; |
| 1167 | static constexpr int DECIMAL_BASE = 10; | 1189 | static constexpr int DECIMAL_BASE = 10; |
| 1168 | static constexpr int DECIMAL_DIGITS_GROUP = 3; | 1190 | static constexpr int DECIMAL_DIGITS_GROUP = 3; |
| 1169 | 1191 | ||
| 1170 | -static std::string JoinStrings(const std::vector<std::string>& parts, const std::string& delim) | ||
| 1171 | -{ | ||
| 1172 | - std::string result; | ||
| 1173 | - for (size_t i = 0; i < parts.size(); ++i) { | ||
| 1174 | - if (i > 0) { | ||
| 1175 | - result += delim; | ||
| 1176 | - } | ||
| 1177 | - result += parts[i]; | ||
| 1178 | - } | ||
| 1179 | - return result; | ||
| 1180 | -} | ||
| 1181 | - | ||
| 1182 | -static std::string ValidateThreshold(const std::string& valStr, const std::string& paramName) | ||
| 1183 | -{ | ||
| 1184 | - if (!IsValidIntegerString(valStr)) { | ||
| 1185 | - return paramName + " must be an integer, range 1~" + std::to_string(THRESHOLD_PERCENT_MAX); | ||
| 1186 | - } | ||
| 1187 | - int val = 0; | ||
| 1188 | - if (utils::ConvertStrToInt(valStr, val) != UBSE_OK || val <= 0 || val > THRESHOLD_PERCENT_MAX) { | ||
| 1189 | - return paramName + " must be an integer, range 1~" + std::to_string(THRESHOLD_PERCENT_MAX); | ||
| 1190 | - } | ||
| 1191 | - return ""; | ||
| 1192 | -} | ||
| 1193 | - | ||
| 1194 | -static std::string ValidateEvictReclaimDelta(const std::string& evictThreshold, const std::string& reclaimThreshold) | ||
| 1195 | -{ | ||
| 1196 | - int evict = 0; | ||
| 1197 | - int reclaim = 0; | ||
| 1198 | - if (utils::ConvertStrToInt(evictThreshold, evict) != UBSE_OK || | ||
| 1199 | - utils::ConvertStrToInt(reclaimThreshold, reclaim) != UBSE_OK || (evict - reclaim) < MIN_EVICT_RECLAIM_DELTA) { | ||
| 1200 | - return "--evict-thresh must be at least " + std::to_string(MIN_EVICT_RECLAIM_DELTA) + | ||
| 1201 | - " higher than --reclaim-thresh to avoid oscillation"; | ||
| 1202 | - } | ||
| 1203 | - return ""; | ||
| 1204 | -} | ||
| 1205 | - | ||
| 1206 | static bool ConvertIntegerPart(uint64_t intPart, uint64_t unitMultiplier, uint64_t& size) | 1192 | static bool ConvertIntegerPart(uint64_t intPart, uint64_t unitMultiplier, uint64_t& size) |
| 1207 | { | 1193 | { |
| 1208 | if (unitMultiplier == 0) { | 1194 | if (unitMultiplier == 0) { |
| @@ -1251,7 +1237,6 @@ static bool ConvertDecimalPart(uint64_t intPart, const std::sub_match<std::strin | |||
| 1251 | 1237 | ||
| 1252 | bool SizeConversion(const std::string& str, uint64_t& size) | 1238 | bool SizeConversion(const std::string& str, uint64_t& size) |
| 1253 | { | 1239 | { |
| 1254 | - // 支持整数 (128M, 1G) 和小数 (1.5G, 0.25M),小数最多2位 | ||
| 1255 | std::regex pattern(R"(^(\d+)(\.(\d{1,2}))?([BKMG])$)"); | 1240 | std::regex pattern(R"(^(\d+)(\.(\d{1,2}))?([BKMG])$)"); |
| 1256 | std::smatch match; | 1241 | std::smatch match; |
| 1257 | if (!std::regex_match(str, match, pattern)) { | 1242 | if (!std::regex_match(str, match, pattern)) { |
| @@ -1283,125 +1268,139 @@ bool SizeConversion(const std::string& str, uint64_t& size) | |||
| 1283 | return ConvertDecimalPart(intPart, match[DECIMAL_DIGITS_GROUP], unitMultiplier, size); | 1268 | return ConvertDecimalPart(intPart, match[DECIMAL_DIGITS_GROUP], unitMultiplier, size); |
| 1284 | } | 1269 | } |
| 1285 | 1270 | ||
| 1286 | -static std::string ValidateSize(const std::string& sizeStr, uint64_t& size) | 1271 | +static std::string ConvertSizeToBytes(const std::string& valStr, uint64_t& outBytes) |
| 1287 | { | 1272 | { |
| 1288 | - if (!SizeConversion(sizeStr, size)) { | 1273 | + if (SizeConversion(valStr, outBytes)) { |
| 1289 | - return "--size: invalid format, supported range is 128M~32G (e.g., 128M, 1G, 1.5G)"; | 1274 | + return ""; |
| 1290 | } | 1275 | } |
| 1291 | - if (size < PROCESS_MEM_SIZE_MIN || size > PROCESS_MEM_SIZE_MAX) { | 1276 | + std::regex pattern(R"(^(\d+)(\.(\d{1,2}))?$)"); |
| 1292 | - return "--size: out of range, supported range is 128M~32G"; | 1277 | + std::smatch match; |
| 1278 | + if (!std::regex_match(valStr, match, pattern)) { | ||
| 1279 | + return SIZE_INVALID_FORMAT; | ||
| 1280 | + } | ||
| 1281 | + uint64_t intPart{0}; | ||
| 1282 | + if (ubse::utils::ConvertStrToUint64(match[1], intPart) != UBSE_OK) { | ||
| 1283 | + return SIZE_INVALID_FORMAT; | ||
| 1284 | + } | ||
| 1285 | + if (match[DECIMAL_DIGITS_GROUP].matched) { | ||
| 1286 | + if (!ConvertDecimalPart(intPart, match[DECIMAL_DIGITS_GROUP], BYTES_PER_MIB, outBytes)) { | ||
| 1287 | + return SIZE_INVALID_FORMAT; | ||
| 1288 | + } | ||
| 1289 | + } else { | ||
| 1290 | + if (!ConvertIntegerPart(intPart, BYTES_PER_MIB, outBytes)) { | ||
| 1291 | + return SIZE_INVALID_FORMAT; | ||
| 1292 | + } | ||
| 1293 | } | 1293 | } |
| 1294 | return ""; | 1294 | return ""; |
| 1295 | } | 1295 | } |
| 1296 | 1296 | ||
| 1297 | -static auto FindRequiredParam(const std::map<std::string, std::string>& params, const std::string& key, | 1297 | +static std::string ValidateRemoteRatio(const std::string& valStr) |
| 1298 | - const std::string& displayName, std::vector<std::string>& missingParams) | ||
| 1299 | - -> std::map<std::string, std::string>::const_iterator | ||
| 1300 | { | 1298 | { |
| 1301 | - auto it = params.find(key); | 1299 | + if (valStr.empty()) { |
| 1302 | - if (it == params.end()) { | 1300 | + return REMOTE_RATIO_INVALID_FORMAT; |
| 1303 | - missingParams.push_back(displayName); | ||
| 1304 | } | 1301 | } |
| 1305 | - return it; | 1302 | + std::regex pattern(R"(^\d+(\.\d+)?$)"); |
| 1306 | -} | 1303 | + if (!std::regex_match(valStr, pattern)) { |
| 1307 | - | 1304 | + return REMOTE_RATIO_INVALID_FORMAT; |
| 1308 | -static void CheckAndPushError(const std::string& err, std::vector<std::string>& errors) | ||
| 1309 | -{ | ||
| 1310 | - if (!err.empty()) { | ||
| 1311 | - errors.push_back(err); | ||
| 1312 | } | 1305 | } |
| 1313 | -} | 1306 | + try { |
| 1314 | - | 1307 | + double ratio = std::stod(valStr); |
| 1315 | -struct ValidatedPidThresholdResult { | 1308 | + if (ratio < 0.0 || ratio > 1.0) { |
| 1316 | - std::string pidVal; | 1309 | + return REMOTE_RATIO_INVALID_FORMAT; |
| 1317 | - std::string evictThreshold; | 1310 | + } |
| 1318 | - std::string targetEvictThreshold; | 1311 | + } catch (...) { |
| 1319 | - std::string reclaimThreshold; | 1312 | + return REMOTE_RATIO_INVALID_FORMAT; |
| 1320 | - uint64_t size{}; | ||
| 1321 | -}; | ||
| 1322 | - | ||
| 1323 | -static std::string ValidatePidSetThresholdParams(const std::map<std::string, std::string>& params, | ||
| 1324 | - ValidatedPidThresholdResult& out) | ||
| 1325 | -{ | ||
| 1326 | - std::vector<std::string> missingParams; | ||
| 1327 | - auto pid = FindRequiredParam(params, PID_OPTION, "--pid", missingParams); | ||
| 1328 | - auto evictIt = | ||
| 1329 | - FindRequiredParam(params, EVICT_THRESHOLD_OPTION, "--" + std::string(EVICT_THRESHOLD_OPTION), missingParams); | ||
| 1330 | - auto targetEvictIt = FindRequiredParam(params, TARGET_EVICT_THRESHOLD_OPTION, | ||
| 1331 | - "--" + std::string(TARGET_EVICT_THRESHOLD_OPTION), missingParams); | ||
| 1332 | - auto reclaimIt = FindRequiredParam(params, RECLAIM_THRESHOLD_OPTION, "--" + std::string(RECLAIM_THRESHOLD_OPTION), | ||
| 1333 | - missingParams); | ||
| 1334 | - auto sizeIt = FindRequiredParam(params, SIZE_OPTION, "--" + std::string(SIZE_OPTION), missingParams); | ||
| 1335 | - if (!missingParams.empty()) { | ||
| 1336 | - return "ERROR: missing required parameter(s): " + JoinStrings(missingParams, ", "); | ||
| 1337 | - } | ||
| 1338 | - | ||
| 1339 | - out.pidVal = pid->second; | ||
| 1340 | - out.evictThreshold = evictIt->second; | ||
| 1341 | - out.targetEvictThreshold = targetEvictIt->second; | ||
| 1342 | - out.reclaimThreshold = reclaimIt->second; | ||
| 1343 | - | ||
| 1344 | - std::vector<std::string> errors; | ||
| 1345 | - CheckAndPushError(ValidatePid(out.pidVal), errors); | ||
| 1346 | - auto evictErr = ValidateThreshold(out.evictThreshold, "--evict-thresh"); | ||
| 1347 | - CheckAndPushError(evictErr, errors); | ||
| 1348 | - CheckAndPushError(ValidateThreshold(out.targetEvictThreshold, "--target-evict-thresh"), errors); | ||
| 1349 | - auto reclaimErr = ValidateThreshold(out.reclaimThreshold, "--reclaim-thresh"); | ||
| 1350 | - CheckAndPushError(reclaimErr, errors); | ||
| 1351 | - if (evictErr.empty() && reclaimErr.empty()) { | ||
| 1352 | - CheckAndPushError(ValidateEvictReclaimDelta(out.evictThreshold, out.reclaimThreshold), errors); | ||
| 1353 | - } | ||
| 1354 | - CheckAndPushError(ValidateSize(sizeIt->second, out.size), errors); | ||
| 1355 | - if (!errors.empty()) { | ||
| 1356 | - return "ERROR: " + JoinStrings(errors, "; "); | ||
| 1357 | } | 1313 | } |
| 1358 | return ""; | 1314 | return ""; |
| 1359 | } | 1315 | } |
| 1360 | 1316 | ||
| 1361 | -process_mem::def::ProcessMemPidInfo SetPidManagerInfo(const std::string& pidStr, const std::string& evictThresholdStr, | 1317 | +static std::string ValidateChangePidName(const std::map<std::string, std::string>& params, |
| 1362 | - const std::string& targetEvictThresholdStr, | 1318 | + process_mem::def::ProcessMemNewConfigInfo& newConfig) |
| 1363 | - const std::string& reclaimThresholdStr, uint64_t size) | ||
| 1364 | { | 1319 | { |
| 1365 | - process_mem::def::ProcessMemPidInfo tmpInfo{}; | 1320 | + auto pidIt = params.find(PID_OPTION); |
| 1366 | - tmpInfo.configInfo.expectedMemoryUsage = size; | 1321 | + auto nameIt = params.find(NAME_OPTION); |
| 1367 | - if (utils::ConvertStrToInt(pidStr, tmpInfo.configInfo.pid) != UBSE_OK || | 1322 | + if (pidIt == params.end() && nameIt == params.end()) { |
| 1368 | - utils::ConvertStrToInt(evictThresholdStr, tmpInfo.configInfo.evictThreshold) != UBSE_OK || | 1323 | + return "ERROR: -p/--pid or -n/--name is required."; |
| 1369 | - utils::ConvertStrToInt(targetEvictThresholdStr, tmpInfo.configInfo.targetEvictThreshold) != UBSE_OK || | ||
| 1370 | - utils::ConvertStrToInt(reclaimThresholdStr, tmpInfo.configInfo.reclaimThreshold) != UBSE_OK) { | ||
| 1371 | - tmpInfo.configInfo.pid = -1; | ||
| 1372 | } | 1324 | } |
| 1373 | - return tmpInfo; | 1325 | + if (pidIt != params.end() && nameIt != params.end()) { |
| 1326 | + return "ERROR: --pid and --name are mutually exclusive."; | ||
| 1327 | + } | ||
| 1328 | + if (pidIt != params.end()) { | ||
| 1329 | + auto errMsg = ValidatePid(pidIt->second); | ||
| 1330 | + if (!errMsg.empty()) { | ||
| 1331 | + return "ERROR: " + errMsg; | ||
| 1332 | + } | ||
| 1333 | + newConfig.isPid = true; | ||
| 1334 | + newConfig.identifier = pidIt->second; | ||
| 1335 | + } else { | ||
| 1336 | + if (!CheckProcName(nameIt->second)) { | ||
| 1337 | + return FormatNameParamError(MAX_PROC_NAME_LENGTH); | ||
| 1338 | + } | ||
| 1339 | + newConfig.isPid = false; | ||
| 1340 | + newConfig.identifier = nameIt->second; | ||
| 1341 | + } | ||
| 1342 | + return ""; | ||
| 1374 | } | 1343 | } |
| 1375 | 1344 | ||
| 1376 | -std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::PidSetThresholdFunc( | 1345 | +static std::string ValidateChangeSize(const std::map<std::string, std::string>& params, |
| 1346 | + process_mem::def::ProcessMemNewConfigInfo& newConfig) | ||
| 1347 | +{ | ||
| 1348 | + auto sizeIt = params.find(SIZE_OPTION); | ||
| 1349 | + if (sizeIt == params.end()) { | ||
| 1350 | + return "ERROR: -s/--size is required."; | ||
| 1351 | + } | ||
| 1352 | + uint64_t sizeBytes = 0; | ||
| 1353 | + auto sizeErr = ConvertSizeToBytes(sizeIt->second, sizeBytes); | ||
| 1354 | + if (!sizeErr.empty()) { | ||
| 1355 | + return sizeErr; | ||
| 1356 | + } | ||
| 1357 | + if (sizeBytes < PROCESS_MEM_SIZE_MIN || sizeBytes > PROCESS_MEM_SIZE_MAX) { | ||
| 1358 | + UBSE_LOG_ERROR << "size out of range: value=" << sizeBytes << " (valid: " << PROCESS_MEM_SIZE_MIN << "-" | ||
| 1359 | + << PROCESS_MEM_SIZE_MAX << ")"; | ||
| 1360 | + return "ERROR: -s/--size out of range, supported range is 128M~32G"; | ||
| 1361 | + } | ||
| 1362 | + newConfig.maxMemory = sizeBytes; | ||
| 1363 | + return ""; | ||
| 1364 | +} | ||
| 1365 | + | ||
| 1366 | +static std::string ValidateChangeRemoteRatio(const std::map<std::string, std::string>& params, | ||
| 1367 | + process_mem::def::ProcessMemNewConfigInfo& newConfig) | ||
| 1368 | +{ | ||
| 1369 | + auto remoteRatioIt = params.find(REMOTE_RATIO_OPTION); | ||
| 1370 | + if (remoteRatioIt == params.end()) { | ||
| 1371 | + return "ERROR: -r/--remote-ratio is required."; | ||
| 1372 | + } | ||
| 1373 | + auto ratioErr = ValidateRemoteRatio(remoteRatioIt->second); | ||
| 1374 | + if (!ratioErr.empty()) { | ||
| 1375 | + return ratioErr; | ||
| 1376 | + } | ||
| 1377 | + newConfig.remoteRatio = std::stod(remoteRatioIt->second); | ||
| 1378 | + return ""; | ||
| 1379 | +} | ||
| 1380 | + | ||
| 1381 | +std::shared_ptr<UbseCliResultEcho> UbseCliRegMemModule::ChangeProcessMemFunc( | ||
| 1377 | const std::map<std::string, std::string>& params) | 1382 | const std::map<std::string, std::string>& params) |
| 1378 | { | 1383 | { |
| 1379 | if (!UbseCliRegModule::DisableTimeoutTimer()) { | 1384 | if (!UbseCliRegModule::DisableTimeoutTimer()) { |
| 1380 | return UbseCliStringPromptReply(SET_TIMER_ERROR); | 1385 | return UbseCliStringPromptReply(SET_TIMER_ERROR); |
| 1381 | } | 1386 | } |
| 1382 | 1387 | ||
| 1383 | - ValidatedPidThresholdResult validated; | 1388 | + process_mem::def::ProcessMemNewConfigInfo newConfig{}; |
| 1384 | - auto errMsg = ValidatePidSetThresholdParams(params, validated); | 1389 | + auto errMsg = ValidateChangePidName(params, newConfig); |
| 1390 | + if (!errMsg.empty()) { | ||
| 1391 | + return UbseCliStringPromptReply(errMsg); | ||
| 1392 | + } | ||
| 1393 | + errMsg = ValidateChangeSize(params, newConfig); | ||
| 1394 | + if (!errMsg.empty()) { | ||
| 1395 | + return UbseCliStringPromptReply(errMsg); | ||
| 1396 | + } | ||
| 1397 | + errMsg = ValidateChangeRemoteRatio(params, newConfig); | ||
| 1385 | if (!errMsg.empty()) { | 1398 | if (!errMsg.empty()) { |
| 1386 | return UbseCliStringPromptReply(errMsg); | 1399 | return UbseCliStringPromptReply(errMsg); |
| 1387 | } | 1400 | } |
| 1388 | 1401 | ||
| 1389 | - auto pidInfo = SetPidManagerInfo(validated.pidVal, validated.evictThreshold, validated.targetEvictThreshold, | ||
| 1390 | - validated.reclaimThreshold, validated.size); | ||
| 1391 | - if (pidInfo.configInfo.pid == -1) { | ||
| 1392 | - return UbseCliStringPromptReply("Internal error"); | ||
| 1393 | - } | ||
| 1394 | - | ||
| 1395 | - auto srcNumaIdParam = params.find(SRC_NUMAID_OPTION); | ||
| 1396 | - if (srcNumaIdParam != params.end()) { | ||
| 1397 | - uint64_t srcNumaId; | ||
| 1398 | - if (utils::ConvertStrToUint64(srcNumaIdParam->second, srcNumaId) != UBSE_OK) { | ||
| 1399 | - return UbseCliStringPromptReply("Invalid --src-numa value"); | ||
| 1400 | - } | ||
| 1401 | - pidInfo.configInfo.srcNumaId = srcNumaId; | ||
| 1402 | - } | ||
| 1403 | UbseCliMemPid memPid{}; | 1402 | UbseCliMemPid memPid{}; |
| 1404 | - return memPid.UbseCliSetPidThreshold(pidInfo); | 1403 | + return memPid.UbseCliSetProcessMem(newConfig); |
| 1405 | } | 1404 | } |
| 1406 | 1405 | ||
| 1407 | } // namespace ubse::cli::reg | 1406 | } // namespace ubse::cli::reg |
| @@ -67,10 +67,12 @@ private: | |||
| 67 | 67 | ||
| 68 | static std::shared_ptr<UbseCliResultEcho> CreateMemoryFunc(const std::map<std::string, std::string>& params); | 68 | static std::shared_ptr<UbseCliResultEcho> CreateMemoryFunc(const std::map<std::string, std::string>& params); |
| 69 | 69 | ||
| 70 | - static std::shared_ptr<UbseCliResultEcho> PidSetThresholdFunc(const std::map<std::string, std::string>& params); | 70 | + static std::shared_ptr<UbseCliResultEcho> RemoveProcessMemFunc(const std::map<std::string, std::string>& params); |
| 71 | 71 | ||
| 72 | - static std::shared_ptr<UbseCliResultEcho> PidUnSetFunc(const std::map<std::string, std::string>& params); | 72 | + static std::shared_ptr<UbseCliResultEcho> ChangeProcessMemFunc(const std::map<std::string, std::string>& params); |
| 73 | }; | 73 | }; |
| 74 | bool CheckName(const std::string& name); | 74 | bool CheckName(const std::string& name); |
| 75 | +bool CheckName(const std::string& name, int maxLength); | ||
| 76 | +bool CheckProcName(const std::string& name); | ||
| 75 | } // namespace ubse::cli::reg | 77 | } // namespace ubse::cli::reg |
| 76 | -#endif | 78 | +#endif |
| @@ -86,9 +86,6 @@ typedef enum | |||
| 86 | UBSE_MEM_CLI_SHM_ATTACH = 0x0045, // SHM链接(CLI) | 86 | UBSE_MEM_CLI_SHM_ATTACH = 0x0045, // SHM链接(CLI) |
| 87 | UBSE_MEM_CLI_SHM_DETACH = 0x0046, // SHM释放(CLI) | 87 | UBSE_MEM_CLI_SHM_DETACH = 0x0046, // SHM释放(CLI) |
| 88 | UBSE_MEM_CLI_MEMORY_DELETE_BY_NAME_TYPE = 0x0047, // 内存删除(聚合接口)(CLI) | 88 | UBSE_MEM_CLI_MEMORY_DELETE_BY_NAME_TYPE = 0x0047, // 内存删除(聚合接口)(CLI) |
| 89 | - UBSE_MEM_CLI_PID_SET_THRESHOLD = 0x0048, // PID阈值设置(CLI) [DEPRECATED:旧模型,待CLI切换后移除] | ||
| 90 | - UBSE_MEM_CLI_PRINT_PID_INFO = 0x0049, // PID信息查询(CLI) [DEPRECATED:旧模型,待CLI切换后移除] | ||
| 91 | - UBSE_MEM_CLI_PID_UNSET = 0x0050, // PID配置删除(CLI) [DEPRECATED:旧模型,待CLI切换后移除] | ||
| 92 | // 0x0048~0x0050 已废弃(旧PID阈值模型), 0x0051~0x0052 为其他特性预留, 禁止复用 | 89 | // 0x0048~0x0050 已废弃(旧PID阈值模型), 0x0051~0x0052 为其他特性预留, 禁止复用 |
| 93 | UBSE_MEM_CLI_PROC_MEM_SET = 0x0053, // process-mem新模型设置(CLI) | 90 | UBSE_MEM_CLI_PROC_MEM_SET = 0x0053, // process-mem新模型设置(CLI) |
| 94 | UBSE_MEM_CLI_PROC_MEM_REMOVE = 0x0054, // process-mem新模型删除(CLI) | 91 | UBSE_MEM_CLI_PROC_MEM_REMOVE = 0x0054, // process-mem新模型删除(CLI) |
| @@ -210,7 +210,6 @@ enum class UbseMemFaultOpCode | |||
| 210 | UBSE_SHARE_MEM_FAULT_NOTIFY_REPLY = 0x0004, // 共享内存故障通知回复 | 210 | UBSE_SHARE_MEM_FAULT_NOTIFY_REPLY = 0x0004, // 共享内存故障通知回复 |
| 211 | UBSE_MEM_FAULT_BMC_AGENTS = 0x0005, // MemFault在master收到BMC故障通知,传递给agents. | 211 | UBSE_MEM_FAULT_BMC_AGENTS = 0x0005, // MemFault在master收到BMC故障通知,传递给agents. |
| 212 | UBSE_SINGLE_IMPORT_DEBT_NOTIFY = 0x0006, // Master通知Agent单导入债务信息 | 212 | UBSE_SINGLE_IMPORT_DEBT_NOTIFY = 0x0006, // Master通知Agent单导入债务信息 |
| 213 | - UBSE_PROCESS_MEM_NODE_FAULT_NOTIFY = 0x0007, // Master通知Agent处理process_mem节点故障 [DEPRECATED:旧模型,待bridge切换后移除] | ||
| 214 | UBSE_PROCESS_MEM_RETURN_REQUEST = 0x0008, // 借出方通知借入方归还 process_mem 远端内存 | 213 | UBSE_PROCESS_MEM_RETURN_REQUEST = 0x0008, // 借出方通知借入方归还 process_mem 远端内存 |
| 215 | }; | 214 | }; |
| 216 | 215 | ||
| @@ -694,117 +694,3 @@ uint32_t mock_urma_qos_invoke_call_not_applied(uint16_t module_code, uint16_t op | |||
| 694 | } | 694 | } |
| 695 | return UBSE_URMACONTRL_ERROR_ETS_TEMPLATE_NOT_APPLIED; | 695 | return UBSE_URMACONTRL_ERROR_ETS_TEMPLATE_NOT_APPLIED; |
| 696 | } | 696 | } |
| 697 | - | ||
| 698 | -uint32_t mock_pid_op_success(uint16_t module_code, uint16_t op_code, const ubse_api_buffer_t* request_data, | ||
| 699 | - ubse_api_buffer_t* response_data) | ||
| 700 | -{ | ||
| 701 | - UbseSerialization ser; | ||
| 702 | - int successCode = 1; | ||
| 703 | - std::string errMsg; | ||
| 704 | - ser << successCode << errMsg; | ||
| 705 | - response_data->buffer = static_cast<uint8_t*>(malloc(ser.GetLength())); | ||
| 706 | - response_data->length = 0; | ||
| 707 | - if (!response_data->buffer) { | ||
| 708 | - return UBSE_ERROR; | ||
| 709 | - } | ||
| 710 | - response_data->length = (uint32_t)ser.GetLength(); | ||
| 711 | - if (memcpy_s(response_data->buffer, ser.GetLength(), ser.GetBuffer(), ser.GetLength()) != EOK) { | ||
| 712 | - free(response_data->buffer); | ||
| 713 | - response_data->buffer = nullptr; | ||
| 714 | - response_data->length = 0; | ||
| 715 | - return UBSE_ERROR; | ||
| 716 | - } | ||
| 717 | - return UBSE_OK; | ||
| 718 | -} | ||
| 719 | - | ||
| 720 | -uint32_t mock_pid_op_failed(uint16_t module_code, uint16_t op_code, const ubse_api_buffer_t* request_data, | ||
| 721 | - ubse_api_buffer_t* response_data) | ||
| 722 | -{ | ||
| 723 | - UbseSerialization ser; | ||
| 724 | - int successCode = 0; | ||
| 725 | - std::string errMsg = "test error"; | ||
| 726 | - ser << successCode << errMsg; | ||
| 727 | - response_data->buffer = static_cast<uint8_t*>(malloc(ser.GetLength())); | ||
| 728 | - response_data->length = 0; | ||
| 729 | - if (!response_data->buffer) { | ||
| 730 | - return UBSE_ERROR; | ||
| 731 | - } | ||
| 732 | - response_data->length = (uint32_t)ser.GetLength(); | ||
| 733 | - if (memcpy_s(response_data->buffer, ser.GetLength(), ser.GetBuffer(), ser.GetLength()) != EOK) { | ||
| 734 | - free(response_data->buffer); | ||
| 735 | - response_data->buffer = nullptr; | ||
| 736 | - response_data->length = 0; | ||
| 737 | - return UBSE_ERROR; | ||
| 738 | - } | ||
| 739 | - return UBSE_OK; | ||
| 740 | -} | ||
| 741 | - | ||
| 742 | -uint32_t mock_pid_print_empty(uint16_t module_code, uint16_t op_code, const ubse_api_buffer_t* request_data, | ||
| 743 | - ubse_api_buffer_t* response_data) | ||
| 744 | -{ | ||
| 745 | - UbseSerialization ser; | ||
| 746 | - size_t count = 0; | ||
| 747 | - ser << count; | ||
| 748 | - response_data->buffer = static_cast<uint8_t*>(malloc(ser.GetLength())); | ||
| 749 | - response_data->length = 0; | ||
| 750 | - if (!response_data->buffer) { | ||
| 751 | - return UBSE_ERROR; | ||
| 752 | - } | ||
| 753 | - response_data->length = (uint32_t)ser.GetLength(); | ||
| 754 | - if (memcpy_s(response_data->buffer, ser.GetLength(), ser.GetBuffer(), ser.GetLength()) != EOK) { | ||
| 755 | - free(response_data->buffer); | ||
| 756 | - response_data->buffer = nullptr; | ||
| 757 | - response_data->length = 0; | ||
| 758 | - return UBSE_ERROR; | ||
| 759 | - } | ||
| 760 | - return UBSE_OK; | ||
| 761 | -} | ||
| 762 | - | ||
| 763 | -uint32_t mock_pid_print_success(uint16_t module_code, uint16_t op_code, const ubse_api_buffer_t* request_data, | ||
| 764 | - ubse_api_buffer_t* response_data) | ||
| 765 | -{ | ||
| 766 | - UbseSerialization ser; | ||
| 767 | - size_t count = 1; | ||
| 768 | - ser << count; | ||
| 769 | - int pidVal = 42; | ||
| 770 | - int evictThreshold = 80; | ||
| 771 | - int targetEvictThreshold = 70; | ||
| 772 | - int reclaimThreshold = 60; | ||
| 773 | - uint64_t expectedMemoryUsage = 1024; | ||
| 774 | - uint64_t hasSrcNuma = 0; | ||
| 775 | - ser << pidVal << evictThreshold << targetEvictThreshold << reclaimThreshold << expectedMemoryUsage << hasSrcNuma; | ||
| 776 | - response_data->buffer = static_cast<uint8_t*>(malloc(ser.GetLength())); | ||
| 777 | - response_data->length = 0; | ||
| 778 | - if (!response_data->buffer) { | ||
| 779 | - return UBSE_ERROR; | ||
| 780 | - } | ||
| 781 | - response_data->length = (uint32_t)ser.GetLength(); | ||
| 782 | - if (memcpy_s(response_data->buffer, ser.GetLength(), ser.GetBuffer(), ser.GetLength()) != EOK) { | ||
| 783 | - free(response_data->buffer); | ||
| 784 | - response_data->buffer = nullptr; | ||
| 785 | - response_data->length = 0; | ||
| 786 | - return UBSE_ERROR; | ||
| 787 | - } | ||
| 788 | - return UBSE_OK; | ||
| 789 | -} | ||
| 790 | - | ||
| 791 | -uint32_t mock_pid_print_deserialize_failed(uint16_t module_code, uint16_t op_code, | ||
| 792 | - const ubse_api_buffer_t* request_data, ubse_api_buffer_t* response_data) | ||
| 793 | -{ | ||
| 794 | - UbseSerialization ser; | ||
| 795 | - std::string garbage = "not a size_t value"; | ||
| 796 | - ser << garbage; | ||
| 797 | - response_data->buffer = static_cast<uint8_t*>(malloc(ser.GetLength())); | ||
| 798 | - response_data->length = 0; | ||
| 799 | - if (!response_data->buffer) { | ||
| 800 | - return UBSE_ERROR; | ||
| 801 | - } | ||
| 802 | - response_data->length = (uint32_t)ser.GetLength(); | ||
| 803 | - if (memcpy_s(response_data->buffer, ser.GetLength(), ser.GetBuffer(), ser.GetLength()) != EOK) { | ||
| 804 | - free(response_data->buffer); | ||
| 805 | - response_data->buffer = nullptr; | ||
| 806 | - response_data->length = 0; | ||
| 807 | - return UBSE_ERROR; | ||
| 808 | - } | ||
| 809 | - return UBSE_OK; | ||
| 810 | -} | ||
| @@ -127,16 +127,4 @@ uint32_t mock_node_info_ubse_invoke_call_success(uint16_t module_code, uint16_t | |||
| 127 | uint32_t mock_urma_qos_invoke_call_not_applied(uint16_t module_code, uint16_t op_code, | 127 | uint32_t mock_urma_qos_invoke_call_not_applied(uint16_t module_code, uint16_t op_code, |
| 128 | const ubse_api_buffer_t* request_data, ubse_api_buffer_t* response_data); | 128 | const ubse_api_buffer_t* request_data, ubse_api_buffer_t* response_data); |
| 129 | 129 | ||
| 130 | -// Process Mem PID mocks | ||
| 131 | -uint32_t mock_pid_op_success(uint16_t module_code, uint16_t op_code, const ubse_api_buffer_t* request_data, | ||
| 132 | - ubse_api_buffer_t* response_data); | ||
| 133 | -uint32_t mock_pid_op_failed(uint16_t module_code, uint16_t op_code, const ubse_api_buffer_t* request_data, | ||
| 134 | - ubse_api_buffer_t* response_data); | ||
| 135 | -uint32_t mock_pid_print_empty(uint16_t module_code, uint16_t op_code, const ubse_api_buffer_t* request_data, | ||
| 136 | - ubse_api_buffer_t* response_data); | ||
| 137 | -uint32_t mock_pid_print_success(uint16_t module_code, uint16_t op_code, const ubse_api_buffer_t* request_data, | ||
| 138 | - ubse_api_buffer_t* response_data); | ||
| 139 | -uint32_t mock_pid_print_deserialize_failed(uint16_t module_code, uint16_t op_code, | ||
| 140 | - const ubse_api_buffer_t* request_data, ubse_api_buffer_t* response_data); | ||
| 141 | - | ||
| 142 | 130 | ||
| @@ -16,106 +16,4 @@ void TestUbseCliMemPid::SetUp() {} | |||
| 16 | 16 | ||
| 17 | void TestUbseCliMemPid::TearDown() {} | 17 | void TestUbseCliMemPid::TearDown() {} |
| 18 | 18 | ||
| 19 | -TEST_F(TestUbseCliMemPid, SetPidThresholdInvokeFailed) | ||
| 20 | -{ | ||
| 21 | - UbseCliMemPid pidModule; | ||
| 22 | - MOCKER(&ubse_invoke_call).stubs().will(invoke(mock_ubse_invoke_call_failed)); | ||
| 23 | - ProcessMemPidInfo pidInfo; | ||
| 24 | - auto result = pidModule.UbseCliSetPidThreshold(pidInfo); | ||
| 25 | - ASSERT_NE(result, nullptr); | ||
| 26 | - EXPECT_NO_THROW(result->UbseCliDisplayResult()); | ||
| 27 | - MOCKER(&ubse_invoke_call).reset(); | ||
| 28 | -} | ||
| 29 | - | ||
| 30 | -TEST_F(TestUbseCliMemPid, SetPidThresholdSuccess) | ||
| 31 | -{ | ||
| 32 | - UbseCliMemPid pidModule; | ||
| 33 | - MOCKER(&ubse_invoke_call).stubs().will(invoke(mock_pid_op_success)); | ||
| 34 | - ProcessMemPidInfo pidInfo; | ||
| 35 | - auto result = pidModule.UbseCliSetPidThreshold(pidInfo); | ||
| 36 | - ASSERT_NE(result, nullptr); | ||
| 37 | - EXPECT_NO_THROW(result->UbseCliDisplayResult()); | ||
| 38 | - MOCKER(&ubse_invoke_call).reset(); | ||
| 39 | -} | ||
| 40 | - | ||
| 41 | -TEST_F(TestUbseCliMemPid, SetPidThresholdFailed) | ||
| 42 | -{ | ||
| 43 | - UbseCliMemPid pidModule; | ||
| 44 | - MOCKER(&ubse_invoke_call).stubs().will(invoke(mock_pid_op_failed)); | ||
| 45 | - ProcessMemPidInfo pidInfo; | ||
| 46 | - auto result = pidModule.UbseCliSetPidThreshold(pidInfo); | ||
| 47 | - ASSERT_NE(result, nullptr); | ||
| 48 | - EXPECT_NO_THROW(result->UbseCliDisplayResult()); | ||
| 49 | - MOCKER(&ubse_invoke_call).reset(); | ||
| 50 | -} | ||
| 51 | - | ||
| 52 | -TEST_F(TestUbseCliMemPid, PrintPidInfoInvokeFailed) | ||
| 53 | -{ | ||
| 54 | - UbseCliMemPid pidModule; | ||
| 55 | - MOCKER(&ubse_invoke_call).stubs().will(invoke(mock_ubse_invoke_call_failed)); | ||
| 56 | - auto result = pidModule.UbseCliPrintPidInfo(); | ||
| 57 | - ASSERT_NE(result, nullptr); | ||
| 58 | - EXPECT_NO_THROW(result->UbseCliDisplayResult()); | ||
| 59 | - MOCKER(&ubse_invoke_call).reset(); | ||
| 60 | -} | ||
| 61 | - | ||
| 62 | -TEST_F(TestUbseCliMemPid, PrintPidInfoEmpty) | ||
| 63 | -{ | ||
| 64 | - UbseCliMemPid pidModule; | ||
| 65 | - MOCKER(&ubse_invoke_call).stubs().will(invoke(mock_pid_print_empty)); | ||
| 66 | - auto result = pidModule.UbseCliPrintPidInfo(); | ||
| 67 | - ASSERT_NE(result, nullptr); | ||
| 68 | - EXPECT_NO_THROW(result->UbseCliDisplayResult()); | ||
| 69 | - MOCKER(&ubse_invoke_call).reset(); | ||
| 70 | -} | ||
| 71 | - | ||
| 72 | -TEST_F(TestUbseCliMemPid, PrintPidInfoDeserializeFailed) | ||
| 73 | -{ | ||
| 74 | - UbseCliMemPid pidModule; | ||
| 75 | - MOCKER(&ubse_invoke_call).stubs().will(invoke(mock_pid_print_deserialize_failed)); | ||
| 76 | - auto result = pidModule.UbseCliPrintPidInfo(); | ||
| 77 | - ASSERT_NE(result, nullptr); | ||
| 78 | - EXPECT_NO_THROW(result->UbseCliDisplayResult()); | ||
| 79 | - MOCKER(&ubse_invoke_call).reset(); | ||
| 80 | -} | ||
| 81 | - | ||
| 82 | -TEST_F(TestUbseCliMemPid, PrintPidInfoSuccess) | ||
| 83 | -{ | ||
| 84 | - UbseCliMemPid pidModule; | ||
| 85 | - MOCKER(&ubse_invoke_call).stubs().will(invoke(mock_pid_print_success)); | ||
| 86 | - auto result = pidModule.UbseCliPrintPidInfo(); | ||
| 87 | - ASSERT_NE(result, nullptr); | ||
| 88 | - EXPECT_NO_THROW(result->UbseCliDisplayResult()); | ||
| 89 | - MOCKER(&ubse_invoke_call).reset(); | ||
| 90 | -} | ||
| 91 | - | ||
| 92 | -TEST_F(TestUbseCliMemPid, UnsetPidInvokeFailed) | ||
| 93 | -{ | ||
| 94 | - UbseCliMemPid pidModule; | ||
| 95 | - MOCKER(&ubse_invoke_call).stubs().will(invoke(mock_ubse_invoke_call_failed)); | ||
| 96 | - auto result = pidModule.UbseCliUnsetPid(123); | ||
| 97 | - ASSERT_NE(result, nullptr); | ||
| 98 | - EXPECT_NO_THROW(result->UbseCliDisplayResult()); | ||
| 99 | - MOCKER(&ubse_invoke_call).reset(); | ||
| 100 | -} | ||
| 101 | - | ||
| 102 | -TEST_F(TestUbseCliMemPid, UnsetPidSuccess) | ||
| 103 | -{ | ||
| 104 | - UbseCliMemPid pidModule; | ||
| 105 | - MOCKER(&ubse_invoke_call).stubs().will(invoke(mock_pid_op_success)); | ||
| 106 | - auto result = pidModule.UbseCliUnsetPid(123); | ||
| 107 | - ASSERT_NE(result, nullptr); | ||
| 108 | - EXPECT_NO_THROW(result->UbseCliDisplayResult()); | ||
| 109 | - MOCKER(&ubse_invoke_call).reset(); | ||
| 110 | -} | ||
| 111 | - | ||
| 112 | -TEST_F(TestUbseCliMemPid, UnsetPidFailed) | ||
| 113 | -{ | ||
| 114 | - UbseCliMemPid pidModule; | ||
| 115 | - MOCKER(&ubse_invoke_call).stubs().will(invoke(mock_pid_op_failed)); | ||
| 116 | - auto result = pidModule.UbseCliUnsetPid(123); | ||
| 117 | - ASSERT_NE(result, nullptr); | ||
| 118 | - EXPECT_NO_THROW(result->UbseCliDisplayResult()); | ||
| 119 | - MOCKER(&ubse_invoke_call).reset(); | ||
| 120 | -} | ||
| 121 | } // namespace ubse::ut::cli | 19 | } // namespace ubse::ut::cli |
| @@ -6,7 +6,8 @@ add_executable(${CMAKE_PROJECT_NAME}_process_mem_ut EXCLUDE_FROM_ALL | |||
| 6 | ${PROCESS_MEM_TEST_SOURCES} ${MOCK_SOURCES} | 6 | ${PROCESS_MEM_TEST_SOURCES} ${MOCK_SOURCES} |
| 7 | ${SRC_DIR}/process_mem_pid_bridge.cpp ${SRC_DIR}/process_mem_pid_collect.cpp | 7 | ${SRC_DIR}/process_mem_pid_bridge.cpp ${SRC_DIR}/process_mem_pid_collect.cpp |
| 8 | ${SRC_DIR}/process_mem_pid_config_manager.cpp ${SRC_DIR}/process_mem_pid_info_manager.cpp | 8 | ${SRC_DIR}/process_mem_pid_config_manager.cpp ${SRC_DIR}/process_mem_pid_info_manager.cpp |
| 9 | - ${SRC_DIR}/process_mem_plugin.cpp ${PROJECT_SOURCE_DIR}/test/UT/main.cpp) | 9 | + ${SRC_DIR}/process_mem_pid_decision.cpp ${SRC_DIR}/process_mem_plugin.cpp |
| 10 | + ${PROJECT_SOURCE_DIR}/test/UT/main.cpp) | ||
| 10 | set_target_properties(${CMAKE_PROJECT_NAME}_process_mem_ut PROPERTIES LINK_FLAGS "-Wl,--as-needed") | 11 | set_target_properties(${CMAKE_PROJECT_NAME}_process_mem_ut PROPERTIES LINK_FLAGS "-Wl,--as-needed") |
| 11 | target_compile_options(${CMAKE_PROJECT_NAME}_process_mem_ut PRIVATE | 12 | target_compile_options(${CMAKE_PROJECT_NAME}_process_mem_ut PRIVATE |
| 12 | -fPIC -w -g -Wno-all -Wno-error -Wno-non-template-friend | 13 | -fPIC -w -g -Wno-all -Wno-error -Wno-non-template-friend |
| @@ -27,5 +28,5 @@ target_include_directories(${CMAKE_PROJECT_NAME}_process_mem_ut BEFORE PUBLIC | |||
| 27 | target_link_options(${CMAKE_PROJECT_NAME}_process_mem_ut PRIVATE -Wl,--relax --coverage -rdynamic -Wl,--as-needed) | 28 | target_link_options(${CMAKE_PROJECT_NAME}_process_mem_ut PRIVATE -Wl,--relax --coverage -rdynamic -Wl,--as-needed) |
| 28 | target_compile_definitions(${CMAKE_PROJECT_NAME}_process_mem_ut PRIVATE UB_ENVIRONMENT ENABLE_COV=OFF) | 29 | target_compile_definitions(${CMAKE_PROJECT_NAME}_process_mem_ut PRIVATE UB_ENVIRONMENT ENABLE_COV=OFF) |
| 29 | target_link_libraries(${CMAKE_PROJECT_NAME}_process_mem_ut PUBLIC | 30 | target_link_libraries(${CMAKE_PROJECT_NAME}_process_mem_ut PUBLIC |
| 30 | - test_exclusive_main ubse_serial mempooling_static rapidjson GTest::gmock_main mockcpp boundscheck) | 31 | + test_exclusive_main ubse_serial rapidjson GTest::gmock_main mockcpp boundscheck trace_context) |
| 31 | add_independent_exec_ut(${CMAKE_PROJECT_NAME}_process_mem_ut) | 32 | add_independent_exec_ut(${CMAKE_PROJECT_NAME}_process_mem_ut) |
| @@ -106,4 +106,4 @@ struct UbseLog { | |||
| 106 | bool operator==(UbseLoggerEntry& loggerEntry); | 106 | bool operator==(UbseLoggerEntry& loggerEntry); |
| 107 | }; | 107 | }; |
| 108 | } // namespace ubse::log | 108 | } // namespace ubse::log |
| 109 | -#endif // UBSE_LOGGER_H | 109 | +#endif |
| @@ -1,25 +1,63 @@ | |||
| 1 | 1 | ||
| 2 | 2 | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 3 | 6 | ||
| 7 | + | ||
| 4 | 8 | ||
| 5 | namespace ubse::mem::controller { | 9 | namespace ubse::mem::controller { |
| 6 | void MockSetDebtInfos(const std::vector<UbseNumaMemoryDebtInfo>& infos); | 10 | void MockSetDebtInfos(const std::vector<UbseNumaMemoryDebtInfo>& infos); |
| 7 | void MockSetImportDebtInfos(const std::vector<UbseNumaMemoryImportDebtInfo>& infos); | 11 | void MockSetImportDebtInfos(const std::vector<UbseNumaMemoryImportDebtInfo>& infos); |
| 8 | void MockClearDebtInfos(); | 12 | void MockClearDebtInfos(); |
| 9 | void MockSetNumaCreateError(uint32_t err); | 13 | void MockSetNumaCreateError(uint32_t err); |
| 14 | +void MockSetNumaCreateErrorOnce(uint32_t err); | ||
| 10 | void MockSetNumaDeleteError(uint32_t err); | 15 | void MockSetNumaDeleteError(uint32_t err); |
| 16 | +void MockSetNumaDeleteErrorOnce(uint32_t err); | ||
| 17 | +uint32_t MockGetNumaDeleteCallCount(); | ||
| 11 | void MockSetDebtInfoWithNodeError(uint32_t err); | 18 | void MockSetDebtInfoWithNodeError(uint32_t err); |
| 19 | +void MockSetNumaCreateDesc(int64_t numaId, uint32_t exportSlotId, uint64_t size); | ||
| 20 | +std::string MockGetLastNumaCreateName(); | ||
| 21 | +uint64_t MockGetLastNumaCreateSize(); | ||
| 22 | +void MockBlockNumaCreate(); | ||
| 23 | +void MockReleaseNumaCreate(); | ||
| 24 | +bool MockWaitNumaCreateEntered(int timeoutMs); | ||
| 25 | +void MockBlockNumaDelete(); | ||
| 26 | +void MockReleaseNumaDelete(); | ||
| 27 | +bool MockWaitNumaDeleteEntered(int timeoutMs); | ||
| 12 | void MockResetAllErrors(); | 28 | void MockResetAllErrors(); |
| 29 | + | ||
| 30 | +void MockSetNodeNumaInfos(const std::vector<UbseNodeNumaInfo>& infos); | ||
| 31 | +std::string MockGetLastNumaDeleteName(); | ||
| 32 | +void MockAdjustNodeFree(uint64_t deltaBytes); | ||
| 13 | } // namespace ubse::mem::controller | 33 | } // namespace ubse::mem::controller |
| 14 | 34 | ||
| 35 | +namespace ubse::com { | ||
| 36 | +struct MockRpcSendRecord { | ||
| 37 | + std::string address; | ||
| 38 | + std::string payload; | ||
| 39 | +}; | ||
| 40 | +void MockResetRpcState(); | ||
| 41 | +const std::vector<MockRpcSendRecord>& MockGetRpcSendRecords(); | ||
| 42 | +} // namespace ubse::com | ||
| 43 | + | ||
| 15 | namespace ubse::nodeController { | 44 | namespace ubse::nodeController { |
| 16 | void MockSetCurrentNodeId(const std::string& nodeId); | 45 | void MockSetCurrentNodeId(const std::string& nodeId); |
| 17 | void MockResetCurrentNodeId(); | 46 | void MockResetCurrentNodeId(); |
| 18 | } // namespace ubse::nodeController | 47 | } // namespace ubse::nodeController |
| 19 | 48 | ||
| 49 | +namespace ubse::task_executor { | ||
| 50 | +void MockSetExecutorAsync(bool async); | ||
| 51 | +void MockWaitExecutorIdle(); | ||
| 52 | +} // namespace ubse::task_executor | ||
| 53 | + | ||
| 20 | namespace ubse::storage { | 54 | namespace ubse::storage { |
| 21 | void MockSetStoragePutError(uint32_t err); | 55 | void MockSetStoragePutError(uint32_t err); |
| 22 | void MockSetStorageQueryError(uint32_t err); | 56 | void MockSetStorageQueryError(uint32_t err); |
| 23 | void MockSetStorageDeleteError(uint32_t err); | 57 | void MockSetStorageDeleteError(uint32_t err); |
| 58 | +void MockSetStorageListError(uint32_t err); | ||
| 59 | +void MockSetStorageKeys(const std::vector<std::string>& keys); | ||
| 60 | +void MockSetStorageQueryPayload(const std::string& keyPrefix, const std::string& key, | ||
| 61 | + const std::vector<uint8_t>& payload); | ||
| 24 | void MockResetStorageErrors(); | 62 | void MockResetStorageErrors(); |
| 25 | } // namespace ubse::storage | 63 | } // namespace ubse::storage |
| @@ -11,9 +11,22 @@ | |||
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | + | ||
| 14 | 15 | ||
| 15 | namespace ubse::com { | 16 | namespace ubse::com { |
| 16 | 17 | ||
| 18 | +static std::vector<MockRpcSendRecord> g_mockRpcSendRecords; | ||
| 19 | + | ||
| 20 | +void MockResetRpcState() | ||
| 21 | +{ | ||
| 22 | + g_mockRpcSendRecords.clear(); | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +const std::vector<MockRpcSendRecord>& MockGetRpcSendRecords() | ||
| 26 | +{ | ||
| 27 | + return g_mockRpcSendRecords; | ||
| 28 | +} | ||
| 29 | + | ||
| 17 | uint32_t UbseRegRpcService(const UbseComEndpoint& endpoint, const UbseComServiceHandler& handler) | 30 | uint32_t UbseRegRpcService(const UbseComEndpoint& endpoint, const UbseComServiceHandler& handler) |
| 18 | { | 31 | { |
| 19 | return 0; | 32 | return 0; |
| @@ -22,6 +35,12 @@ uint32_t UbseRegRpcService(const UbseComEndpoint& endpoint, const UbseComService | |||
| 22 | uint32_t UbseRpcSend(const UbseComEndpoint& endpoint, const UbseByteBuffer& reqData, void* ctx, | 35 | uint32_t UbseRpcSend(const UbseComEndpoint& endpoint, const UbseByteBuffer& reqData, void* ctx, |
| 23 | const UbseComRespHandler& handler) | 36 | const UbseComRespHandler& handler) |
| 24 | { | 37 | { |
| 38 | + MockRpcSendRecord record; | ||
| 39 | + record.address = endpoint.address; | ||
| 40 | + if (reqData.data != nullptr && reqData.len > 0) { | ||
| 41 | + record.payload.assign(reinterpret_cast<const char*>(reqData.data), reqData.len); | ||
| 42 | + } | ||
| 43 | + g_mockRpcSendRecords.push_back(std::move(record)); | ||
| 25 | return 0; | 44 | return 0; |
| 26 | } | 45 | } |
| 27 | 46 | ||
| @@ -10,17 +10,101 @@ | |||
| 10 | * See the Mulan PSL v2 for more details. | 10 | * See the Mulan PSL v2 for more details. |
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | -#include "ubse_mem_controller.h" | 13 | +#include <chrono> |
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 14 | 18 | ||
| 19 | + | ||
| 20 | + | ||
| 15 | 21 | ||
| 16 | namespace ubse::mem::controller { | 22 | namespace ubse::mem::controller { |
| 17 | 23 | ||
| 18 | -// Configurable mock data for tests | ||
| 19 | static std::vector<UbseNumaMemoryDebtInfo> g_mockDebtInfos; | 24 | static std::vector<UbseNumaMemoryDebtInfo> g_mockDebtInfos; |
| 20 | static std::vector<UbseNumaMemoryImportDebtInfo> g_mockImportDebtInfos; | 25 | static std::vector<UbseNumaMemoryImportDebtInfo> g_mockImportDebtInfos; |
| 21 | static uint32_t g_mockNumaCreateError = UBSE_OK; | 26 | static uint32_t g_mockNumaCreateError = UBSE_OK; |
| 27 | +static uint32_t g_mockNumaCreateErrorOnce = UBSE_OK; | ||
| 22 | static uint32_t g_mockNumaDeleteError = UBSE_OK; | 28 | static uint32_t g_mockNumaDeleteError = UBSE_OK; |
| 29 | +static uint32_t g_mockNumaDeleteErrorOnce = UBSE_OK; | ||
| 30 | +static uint32_t g_mockNumaDeleteCallCount = 0; | ||
| 23 | static uint32_t g_mockDebtInfoWithNodeError = UBSE_OK; | 31 | static uint32_t g_mockDebtInfoWithNodeError = UBSE_OK; |
| 32 | +static std::vector<UbseNodeNumaInfo> g_mockNodeNumaInfos; | ||
| 33 | +static int64_t g_mockNumaCreateNumaId = 3; | ||
| 34 | +static uint32_t g_mockNumaCreateExportSlot = 7; | ||
| 35 | +static uint64_t g_mockNumaCreateSize = 0; | ||
| 36 | +static uint64_t g_mockLastNumaCreateOptSize = 0; | ||
| 37 | +static std::string g_mockLastNumaCreateName; | ||
| 38 | +static std::string g_mockLastNumaDeleteName; | ||
| 39 | + | ||
| 40 | +static std::mutex g_createMutex; | ||
| 41 | +static std::condition_variable g_createCv; | ||
| 42 | +static bool g_createBlocked = false; | ||
| 43 | +static bool g_createEntered = false; | ||
| 44 | +static std::condition_variable g_createEnteredCv; | ||
| 45 | + | ||
| 46 | +static std::mutex g_deleteMutex; | ||
| 47 | +static std::condition_variable g_deleteCv; | ||
| 48 | +static bool g_deleteBlocked = false; | ||
| 49 | +static bool g_deleteEntered = false; | ||
| 50 | +static std::condition_variable g_deleteEnteredCv; | ||
| 51 | + | ||
| 52 | +void MockBlockNumaCreate() | ||
| 53 | +{ | ||
| 54 | + std::lock_guard<std::mutex> lock(g_createMutex); | ||
| 55 | + g_createBlocked = true; | ||
| 56 | + g_createEntered = false; | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +void MockReleaseNumaCreate() | ||
| 60 | +{ | ||
| 61 | + std::lock_guard<std::mutex> lock(g_createMutex); | ||
| 62 | + g_createBlocked = false; | ||
| 63 | + g_createCv.notify_all(); | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +bool MockWaitNumaCreateEntered(int timeoutMs) | ||
| 67 | +{ | ||
| 68 | + std::unique_lock<std::mutex> lock(g_createMutex); | ||
| 69 | + g_createEnteredCv.wait_for(lock, std::chrono::milliseconds(timeoutMs), [] { return g_createEntered; }); | ||
| 70 | + return g_createEntered; | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +void MockBlockNumaDelete() | ||
| 74 | +{ | ||
| 75 | + std::lock_guard<std::mutex> lock(g_deleteMutex); | ||
| 76 | + g_deleteBlocked = true; | ||
| 77 | + g_deleteEntered = false; | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +void MockReleaseNumaDelete() | ||
| 81 | +{ | ||
| 82 | + std::lock_guard<std::mutex> lock(g_deleteMutex); | ||
| 83 | + g_deleteBlocked = false; | ||
| 84 | + g_deleteCv.notify_all(); | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | +bool MockWaitNumaDeleteEntered(int timeoutMs) | ||
| 88 | +{ | ||
| 89 | + std::unique_lock<std::mutex> lock(g_deleteMutex); | ||
| 90 | + g_deleteEnteredCv.wait_for(lock, std::chrono::milliseconds(timeoutMs), [] { return g_deleteEntered; }); | ||
| 91 | + return g_deleteEntered; | ||
| 92 | +} | ||
| 93 | + | ||
| 94 | +void MockSetNumaCreateDesc(int64_t numaId, uint32_t exportSlotId, uint64_t size) | ||
| 95 | +{ | ||
| 96 | + g_mockNumaCreateNumaId = numaId; | ||
| 97 | + g_mockNumaCreateExportSlot = exportSlotId; | ||
| 98 | + g_mockNumaCreateSize = size; | ||
| 99 | +} | ||
| 100 | +std::string MockGetLastNumaCreateName() | ||
| 101 | +{ | ||
| 102 | + return g_mockLastNumaCreateName; | ||
| 103 | +} | ||
| 104 | +uint64_t MockGetLastNumaCreateSize() | ||
| 105 | +{ | ||
| 106 | + return g_mockLastNumaCreateOptSize; | ||
| 107 | +} | ||
| 24 | 108 | ||
| 25 | void MockSetDebtInfos(const std::vector<UbseNumaMemoryDebtInfo>& infos) | 109 | void MockSetDebtInfos(const std::vector<UbseNumaMemoryDebtInfo>& infos) |
| 26 | { | 110 | { |
| @@ -39,21 +123,63 @@ void MockSetNumaCreateError(uint32_t err) | |||
| 39 | { | 123 | { |
| 40 | g_mockNumaCreateError = err; | 124 | g_mockNumaCreateError = err; |
| 41 | } | 125 | } |
| 126 | +void MockSetNumaCreateErrorOnce(uint32_t err) | ||
| 127 | +{ | ||
| 128 | + g_mockNumaCreateErrorOnce = err; | ||
| 129 | +} | ||
| 42 | void MockSetNumaDeleteError(uint32_t err) | 130 | void MockSetNumaDeleteError(uint32_t err) |
| 43 | { | 131 | { |
| 44 | g_mockNumaDeleteError = err; | 132 | g_mockNumaDeleteError = err; |
| 45 | } | 133 | } |
| 134 | +void MockSetNumaDeleteErrorOnce(uint32_t err) | ||
| 135 | +{ | ||
| 136 | + g_mockNumaDeleteErrorOnce = err; | ||
| 137 | +} | ||
| 138 | +uint32_t MockGetNumaDeleteCallCount() | ||
| 139 | +{ | ||
| 140 | + return g_mockNumaDeleteCallCount; | ||
| 141 | +} | ||
| 46 | void MockSetDebtInfoWithNodeError(uint32_t err) | 142 | void MockSetDebtInfoWithNodeError(uint32_t err) |
| 47 | { | 143 | { |
| 48 | g_mockDebtInfoWithNodeError = err; | 144 | g_mockDebtInfoWithNodeError = err; |
| 49 | } | 145 | } |
| 146 | +void MockSetNodeNumaInfos(const std::vector<UbseNodeNumaInfo>& infos) | ||
| 147 | +{ | ||
| 148 | + g_mockNodeNumaInfos = infos; | ||
| 149 | +} | ||
| 150 | +std::string MockGetLastNumaDeleteName() | ||
| 151 | +{ | ||
| 152 | + return g_mockLastNumaDeleteName; | ||
| 153 | +} | ||
| 154 | + | ||
| 155 | +void MockAdjustNodeFree(uint64_t deltaBytes) | ||
| 156 | +{ | ||
| 157 | + if (g_mockNodeNumaInfos.empty()) { | ||
| 158 | + return; | ||
| 159 | + } | ||
| 160 | + for (auto& info : g_mockNodeNumaInfos) { | ||
| 161 | + info.memFree += deltaBytes; | ||
| 162 | + } | ||
| 163 | +} | ||
| 50 | void MockResetAllErrors() | 164 | void MockResetAllErrors() |
| 51 | { | 165 | { |
| 52 | g_mockDebtInfos.clear(); | 166 | g_mockDebtInfos.clear(); |
| 53 | g_mockImportDebtInfos.clear(); | 167 | g_mockImportDebtInfos.clear(); |
| 168 | + g_mockNodeNumaInfos.clear(); | ||
| 169 | + g_mockLastNumaCreateName.clear(); | ||
| 170 | + g_mockLastNumaDeleteName.clear(); | ||
| 54 | g_mockNumaCreateError = UBSE_OK; | 171 | g_mockNumaCreateError = UBSE_OK; |
| 172 | + g_mockNumaCreateErrorOnce = UBSE_OK; | ||
| 55 | g_mockNumaDeleteError = UBSE_OK; | 173 | g_mockNumaDeleteError = UBSE_OK; |
| 174 | + g_mockNumaDeleteErrorOnce = UBSE_OK; | ||
| 175 | + g_mockNumaDeleteCallCount = 0; | ||
| 56 | g_mockDebtInfoWithNodeError = UBSE_OK; | 176 | g_mockDebtInfoWithNodeError = UBSE_OK; |
| 177 | + g_mockNumaCreateNumaId = 3; | ||
| 178 | + g_mockNumaCreateExportSlot = 7; | ||
| 179 | + g_mockNumaCreateSize = 0; | ||
| 180 | + g_mockLastNumaCreateOptSize = 0; | ||
| 181 | + MockReleaseNumaCreate(); | ||
| 182 | + MockReleaseNumaDelete(); | ||
| 57 | } | 183 | } |
| 58 | 184 | ||
| 59 | UbseResult UbseGetNumaMemDebtInfoWithNode(const std::string& nodeId, std::vector<UbseNumaMemoryDebtInfo>& debtInfos) | 185 | UbseResult UbseGetNumaMemDebtInfoWithNode(const std::string& nodeId, std::vector<UbseNumaMemoryDebtInfo>& debtInfos) |
| @@ -69,13 +195,48 @@ UbseResult UbseGetNumaMemDebtInfoWithNode(const std::string& nodeId, std::vector | |||
| 69 | 195 | ||
| 70 | UbseResult UbseMemNumaDelete(const std::string& name, const UbseMemBorrower& borrower) | 196 | UbseResult UbseMemNumaDelete(const std::string& name, const UbseMemBorrower& borrower) |
| 71 | { | 197 | { |
| 198 | + ++g_mockNumaDeleteCallCount; | ||
| 199 | + g_mockLastNumaDeleteName = name; | ||
| 200 | + if (g_mockNumaDeleteErrorOnce != UBSE_OK) { | ||
| 201 | + UbseResult onceErr = g_mockNumaDeleteErrorOnce; | ||
| 202 | + g_mockNumaDeleteErrorOnce = UBSE_OK; | ||
| 203 | + return onceErr; | ||
| 204 | + } | ||
| 205 | + { | ||
| 206 | + std::unique_lock<std::mutex> lock(g_deleteMutex); | ||
| 207 | + g_deleteEntered = true; | ||
| 208 | + g_deleteEnteredCv.notify_all(); | ||
| 209 | + g_deleteCv.wait(lock, [] { return !g_deleteBlocked; }); | ||
| 210 | + } | ||
| 72 | return g_mockNumaDeleteError; | 211 | return g_mockNumaDeleteError; |
| 73 | } | 212 | } |
| 74 | 213 | ||
| 75 | UbseResult UbseMemNumaCreate(const std::string& name, const UbseMemBorrower& borrower, const UbseMemNumaCreateOpt& opt, | 214 | UbseResult UbseMemNumaCreate(const std::string& name, const UbseMemBorrower& borrower, const UbseMemNumaCreateOpt& opt, |
| 76 | UbseMemNumaDesc& desc) | 215 | UbseMemNumaDesc& desc) |
| 77 | { | 216 | { |
| 78 | - return g_mockNumaCreateError; | 217 | + if (g_mockNumaCreateErrorOnce != UBSE_OK) { |
| 218 | + UbseResult onceErr = g_mockNumaCreateErrorOnce; | ||
| 219 | + g_mockNumaCreateErrorOnce = UBSE_OK; | ||
| 220 | + return onceErr; | ||
| 221 | + } | ||
| 222 | + if (g_mockNumaCreateError != UBSE_OK) { | ||
| 223 | + return g_mockNumaCreateError; | ||
| 224 | + } | ||
| 225 | + g_mockLastNumaCreateName = name; | ||
| 226 | + g_mockLastNumaCreateOptSize = opt.size; | ||
| 227 | + { | ||
| 228 | + std::unique_lock<std::mutex> lock(g_createMutex); | ||
| 229 | + g_createEntered = true; | ||
| 230 | + g_createEnteredCv.notify_all(); | ||
| 231 | + g_createCv.wait(lock, [] { return !g_createBlocked; }); | ||
| 232 | + } | ||
| 233 | + desc.name = name; | ||
| 234 | + desc.numaId = g_mockNumaCreateNumaId; | ||
| 235 | + desc.exportNode.slotId = g_mockNumaCreateExportSlot; | ||
| 236 | + desc.size = (g_mockNumaCreateSize != 0) ? g_mockNumaCreateSize : opt.size; | ||
| 237 | + memcpy(desc.usrInfo, opt.usrInfo, UBSE_MAX_USR_INFO_LEN); | ||
| 238 | + MockAdjustNodeFree(opt.size); | ||
| 239 | + return UBSE_OK; | ||
| 79 | } | 240 | } |
| 80 | 241 | ||
| 81 | UbseResult UbseMemNumaCreateWithCandidate(const std::string& name, const UbseMemBorrower& borrower, | 242 | UbseResult UbseMemNumaCreateWithCandidate(const std::string& name, const UbseMemBorrower& borrower, |
| @@ -134,6 +295,9 @@ UbseResult UbseMemDebtCircleCheck(const std::string& srcNodeId, const std::strin | |||
| 134 | 295 | ||
| 135 | UbseResult UbseGetNodeNumaInfoByNodeId(const std::string& nodeId, std::vector<UbseNodeNumaInfo>& numaNodeInfoList) | 296 | UbseResult UbseGetNodeNumaInfoByNodeId(const std::string& nodeId, std::vector<UbseNodeNumaInfo>& numaNodeInfoList) |
| 136 | { | 297 | { |
| 298 | + if (!g_mockNodeNumaInfos.empty()) { | ||
| 299 | + numaNodeInfoList = g_mockNodeNumaInfos; | ||
| 300 | + } | ||
| 137 | return UBSE_OK; | 301 | return UBSE_OK; |
| 138 | } | 302 | } |
| 139 | 303 | ||
| @@ -0,0 +1,110 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved. | ||
| 3 | + * ubs-engine is licensed under Mulan PSL v2. | ||
| 4 | + * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 5 | + * You may obtain a copy of Mulan PSL v2 at: | ||
| 6 | + * http://license.coscl.org.cn/MulanPSL2 | ||
| 7 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 8 | + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 9 | + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 10 | + * See the Mulan PSL v2 for more details. | ||
| 11 | + */ | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +namespace ubse::smap { | ||
| 18 | + | ||
| 19 | +static std::mutex g_mtx; | ||
| 20 | +static int g_successCount = -1; | ||
| 21 | +static int g_failRet = 0; | ||
| 22 | +static uint32_t g_callCount = 0; | ||
| 23 | +static uint64_t g_callSeq = 0; | ||
| 24 | +static std::map<pid_t, std::map<int, uint64_t>> g_targets; | ||
| 25 | +static std::map<pid_t, std::map<int, uint64_t>> g_lastCallSeq; | ||
| 26 | +static std::vector<std::pair<int, uint64_t>> g_calls; | ||
| 27 | + | ||
| 28 | +void MockResetMigrateState() | ||
| 29 | +{ | ||
| 30 | + std::lock_guard<std::mutex> lock(g_mtx); | ||
| 31 | + g_successCount = -1; | ||
| 32 | + g_failRet = 0; | ||
| 33 | + g_callCount = 0; | ||
| 34 | + g_callSeq = 0; | ||
| 35 | + g_targets.clear(); | ||
| 36 | + g_lastCallSeq.clear(); | ||
| 37 | + g_calls.clear(); | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +void MockSetMigrateFail(int successCount, int failRet) | ||
| 41 | +{ | ||
| 42 | + std::lock_guard<std::mutex> lock(g_mtx); | ||
| 43 | + g_successCount = successCount; | ||
| 44 | + g_failRet = failRet; | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +int MockRmrsMigrateOut(const std::vector<mempooling::smap::MigrateOutPayload>& payloads, int) | ||
| 48 | +{ | ||
| 49 | + std::unique_lock<std::mutex> lock(g_mtx); | ||
| 50 | + const uint64_t callSeq = ++g_callSeq; | ||
| 51 | + ++g_callCount; | ||
| 52 | + for (const auto& p : payloads) { | ||
| 53 | + for (int i = 0; i < p.count; ++i) { | ||
| 54 | + g_calls.emplace_back(p.inner[i].destNid, p.inner[i].memSize); | ||
| 55 | + g_lastCallSeq[p.pid][p.inner[i].destNid] = callSeq; | ||
| 56 | + } | ||
| 57 | + } | ||
| 58 | + if (g_successCount >= 0 && g_callCount > static_cast<uint32_t>(g_successCount)) { | ||
| 59 | + return g_failRet; | ||
| 60 | + } | ||
| 61 | + for (const auto& p : payloads) { | ||
| 62 | + for (int i = 0; i < p.count; ++i) { | ||
| 63 | + if (g_lastCallSeq[p.pid][p.inner[i].destNid] == callSeq) { | ||
| 64 | + g_targets[p.pid][p.inner[i].destNid] = p.inner[i].memSize; | ||
| 65 | + } | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + for (auto& [pid, targets] : g_targets) { | ||
| 69 | + for (auto it = targets.begin(); it != targets.end();) { | ||
| 70 | + if (g_lastCallSeq[pid][it->first] < callSeq) { | ||
| 71 | + it = targets.erase(it); | ||
| 72 | + } else { | ||
| 73 | + ++it; | ||
| 74 | + } | ||
| 75 | + } | ||
| 76 | + } | ||
| 77 | + return 0; | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +uint64_t MockGetMigrateTargetKb(pid_t pid, int numaId) | ||
| 81 | +{ | ||
| 82 | + std::lock_guard<std::mutex> lock(g_mtx); | ||
| 83 | + auto it = g_targets.find(pid); | ||
| 84 | + if (it == g_targets.end()) { | ||
| 85 | + return 0; | ||
| 86 | + } | ||
| 87 | + auto itNuma = it->second.find(numaId); | ||
| 88 | + return (itNuma == it->second.end()) ? 0 : itNuma->second; | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +std::map<int, uint64_t> MockGetMigrateTargets(pid_t pid) | ||
| 92 | +{ | ||
| 93 | + std::lock_guard<std::mutex> lock(g_mtx); | ||
| 94 | + auto it = g_targets.find(pid); | ||
| 95 | + return (it == g_targets.end()) ? std::map<int, uint64_t>{} : it->second; | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +uint32_t MockGetMigrateCallCount() | ||
| 99 | +{ | ||
| 100 | + std::lock_guard<std::mutex> lock(g_mtx); | ||
| 101 | + return g_callCount; | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +std::vector<std::pair<int, uint64_t>> MockGetMigrateCalls() | ||
| 105 | +{ | ||
| 106 | + std::lock_guard<std::mutex> lock(g_mtx); | ||
| 107 | + return g_calls; | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +} // namespace ubse::smap | ||
| @@ -0,0 +1,18 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +namespace ubse::smap { | ||
| 11 | +void MockResetMigrateState(); | ||
| 12 | +void MockSetMigrateFail(int successCount, int failRet); | ||
| 13 | +int MockRmrsMigrateOut(const std::vector<mempooling::smap::MigrateOutPayload>& payloads, int); | ||
| 14 | +uint64_t MockGetMigrateTargetKb(pid_t pid, int numaId); | ||
| 15 | +std::map<int, uint64_t> MockGetMigrateTargets(pid_t pid); | ||
| 16 | +uint32_t MockGetMigrateCallCount(); | ||
| 17 | +std::vector<std::pair<int, uint64_t>> MockGetMigrateCalls(); | ||
| 18 | +} // namespace ubse::smap | ||
| @@ -10,6 +10,9 @@ | |||
| 10 | * See the Mulan PSL v2 for more details. | 10 | * See the Mulan PSL v2 for more details. |
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 13 | 16 | ||
| 14 | 17 | ||
| 15 | namespace ubse::storage { | 18 | namespace ubse::storage { |
| @@ -17,6 +20,15 @@ namespace ubse::storage { | |||
| 17 | static uint32_t g_mockStoragePutError = 0; | 20 | static uint32_t g_mockStoragePutError = 0; |
| 18 | static uint32_t g_mockStorageQueryError = 0; | 21 | static uint32_t g_mockStorageQueryError = 0; |
| 19 | static uint32_t g_mockStorageDeleteError = 0; | 22 | static uint32_t g_mockStorageDeleteError = 0; |
| 23 | +static uint32_t g_mockStorageListError = 0; | ||
| 24 | +static std::vector<std::string> g_mockStorageKeys; | ||
| 25 | +static std::map<std::string, std::vector<uint8_t>> g_mockStorageQueryPayloads; | ||
| 26 | + | ||
| 27 | +void MockSetStorageQueryPayload(const std::string& keyPrefix, const std::string& key, | ||
| 28 | + const std::vector<uint8_t>& payload) | ||
| 29 | +{ | ||
| 30 | + g_mockStorageQueryPayloads[keyPrefix + ":" + key] = payload; | ||
| 31 | +} | ||
| 20 | 32 | ||
| 21 | void MockSetStoragePutError(uint32_t err) | 33 | void MockSetStoragePutError(uint32_t err) |
| 22 | { | 34 | { |
| @@ -30,11 +42,22 @@ void MockSetStorageDeleteError(uint32_t err) | |||
| 30 | { | 42 | { |
| 31 | g_mockStorageDeleteError = err; | 43 | g_mockStorageDeleteError = err; |
| 32 | } | 44 | } |
| 45 | +void MockSetStorageListError(uint32_t err) | ||
| 46 | +{ | ||
| 47 | + g_mockStorageListError = err; | ||
| 48 | +} | ||
| 49 | +void MockSetStorageKeys(const std::vector<std::string>& keys) | ||
| 50 | +{ | ||
| 51 | + g_mockStorageKeys = keys; | ||
| 52 | +} | ||
| 33 | void MockResetStorageErrors() | 53 | void MockResetStorageErrors() |
| 34 | { | 54 | { |
| 35 | g_mockStoragePutError = 0; | 55 | g_mockStoragePutError = 0; |
| 36 | g_mockStorageQueryError = 0; | 56 | g_mockStorageQueryError = 0; |
| 37 | g_mockStorageDeleteError = 0; | 57 | g_mockStorageDeleteError = 0; |
| 58 | + g_mockStorageListError = 0; | ||
| 59 | + g_mockStorageKeys.clear(); | ||
| 60 | + g_mockStorageQueryPayloads.clear(); | ||
| 38 | } | 61 | } |
| 39 | 62 | ||
| 40 | uint32_t UbseStoragePutData(const std::string& keyPrefix, const std::string& key, UbseByteBuffer* data) | 63 | uint32_t UbseStoragePutData(const std::string& keyPrefix, const std::string& key, UbseByteBuffer* data) |
| @@ -45,6 +68,13 @@ uint32_t UbseStoragePutData(const std::string& keyPrefix, const std::string& key | |||
| 45 | uint32_t UbseStorageQueryData(const std::string& keyPrefix, const std::string& key, void* ctx, | 68 | uint32_t UbseStorageQueryData(const std::string& keyPrefix, const std::string& key, void* ctx, |
| 46 | UbseStorageDealDataFunc func) | 69 | UbseStorageDealDataFunc func) |
| 47 | { | 70 | { |
| 71 | + auto it = g_mockStorageQueryPayloads.find(keyPrefix + ":" + key); | ||
| 72 | + if (it != g_mockStorageQueryPayloads.end()) { | ||
| 73 | + UbseByteBuffer buff{}; | ||
| 74 | + buff.data = const_cast<uint8_t*>(it->second.data()); | ||
| 75 | + buff.len = it->second.size(); | ||
| 76 | + func(keyPrefix, key, buff, ctx); | ||
| 77 | + } | ||
| 48 | return g_mockStorageQueryError; | 78 | return g_mockStorageQueryError; |
| 49 | } | 79 | } |
| 50 | 80 | ||
| @@ -52,4 +82,13 @@ uint32_t UbseStorageDeleteData(const std::string& keyPrefix, const std::string& | |||
| 52 | { | 82 | { |
| 53 | return g_mockStorageDeleteError; | 83 | return g_mockStorageDeleteError; |
| 54 | } | 84 | } |
| 85 | + | ||
| 86 | +uint32_t UbseStorageListKeys(const std::string& keyPrefix, std::vector<std::string>& keys) | ||
| 87 | +{ | ||
| 88 | + if (g_mockStorageListError != 0) { | ||
| 89 | + return g_mockStorageListError; | ||
| 90 | + } | ||
| 91 | + keys = g_mockStorageKeys; | ||
| 92 | + return 0; | ||
| 93 | +} | ||
| 55 | } // namespace ubse::storage | 94 | } // namespace ubse::storage |
| @@ -12,8 +12,93 @@ | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 15 | namespace ubse::task_executor { | 24 | namespace ubse::task_executor { |
| 16 | 25 | ||
| 26 | +namespace { | ||
| 27 | +std::atomic<bool> g_asyncMode{false}; | ||
| 28 | +std::atomic<bool> g_workerStop{false}; | ||
| 29 | +std::atomic<uint64_t> g_inflight{0}; | ||
| 30 | +std::mutex g_qMutex; | ||
| 31 | +std::condition_variable g_qCv; | ||
| 32 | +std::condition_variable g_idleCv; | ||
| 33 | +std::queue<std::function<void()>> g_tasks; | ||
| 34 | +std::vector<std::thread> g_workers; | ||
| 35 | + | ||
| 36 | +void MockWorkerLoop() | ||
| 37 | +{ | ||
| 38 | + for (;;) { | ||
| 39 | + std::function<void()> task; | ||
| 40 | + { | ||
| 41 | + std::unique_lock<std::mutex> lock(g_qMutex); | ||
| 42 | + g_qCv.wait(lock, [] { return g_workerStop.load() || !g_tasks.empty(); }); | ||
| 43 | + if (g_workerStop.load()) { | ||
| 44 | + return; | ||
| 45 | + } | ||
| 46 | + task = std::move(g_tasks.front()); | ||
| 47 | + g_tasks.pop(); | ||
| 48 | + } | ||
| 49 | + task(); | ||
| 50 | + if (g_inflight.fetch_sub(1) == 1) { | ||
| 51 | + g_idleCv.notify_all(); | ||
| 52 | + } | ||
| 53 | + } | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +bool TryEnqueue(const std::function<void()>& task) | ||
| 57 | +{ | ||
| 58 | + if (!g_asyncMode.load()) { | ||
| 59 | + return false; | ||
| 60 | + } | ||
| 61 | + g_inflight.fetch_add(1); | ||
| 62 | + { | ||
| 63 | + std::lock_guard<std::mutex> lock(g_qMutex); | ||
| 64 | + g_tasks.push(task); | ||
| 65 | + } | ||
| 66 | + g_qCv.notify_one(); | ||
| 67 | + return true; | ||
| 68 | +} | ||
| 69 | +} // namespace | ||
| 70 | + | ||
| 71 | +void MockSetExecutorAsync(bool async) | ||
| 72 | +{ | ||
| 73 | + if (async == g_asyncMode.load()) { | ||
| 74 | + return; | ||
| 75 | + } | ||
| 76 | + if (async) { | ||
| 77 | + g_workerStop.store(false); | ||
| 78 | + for (int i = 0; i < 2; ++i) { | ||
| 79 | + g_workers.emplace_back(MockWorkerLoop); | ||
| 80 | + } | ||
| 81 | + g_asyncMode.store(true); | ||
| 82 | + } else { | ||
| 83 | + MockWaitExecutorIdle(); | ||
| 84 | + g_workerStop.store(true); | ||
| 85 | + g_qCv.notify_all(); | ||
| 86 | + for (auto& worker : g_workers) { | ||
| 87 | + if (worker.joinable()) { | ||
| 88 | + worker.join(); | ||
| 89 | + } | ||
| 90 | + } | ||
| 91 | + g_workers.clear(); | ||
| 92 | + g_asyncMode.store(false); | ||
| 93 | + } | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +void MockWaitExecutorIdle() | ||
| 97 | +{ | ||
| 98 | + std::unique_lock<std::mutex> lock(g_qMutex); | ||
| 99 | + g_idleCv.wait(lock, [] { return g_inflight.load() == 0 && g_tasks.empty(); }); | ||
| 100 | +} | ||
| 101 | + | ||
| 17 | void UbseRunnable::Run() | 102 | void UbseRunnable::Run() |
| 18 | { | 103 | { |
| 19 | if (mTask) { | 104 | if (mTask) { |
| @@ -69,7 +154,9 @@ bool UbseTaskExecutor::Execute(const UbseRunnablePtr& runnable) | |||
| 69 | return false; | 154 | return false; |
| 70 | } | 155 | } |
| 71 | if (runnable.Get() != nullptr) { | 156 | if (runnable.Get() != nullptr) { |
| 72 | - runnable->Run(); | 157 | + if (!TryEnqueue([runnable]() { runnable->Run(); })) { |
| 158 | + runnable->Run(); | ||
| 159 | + } | ||
| 73 | } | 160 | } |
| 74 | return true; | 161 | return true; |
| 75 | } | 162 | } |
| @@ -80,7 +167,9 @@ bool UbseTaskExecutor::Execute(const std::function<void()>& task) | |||
| 80 | return false; | 167 | return false; |
| 81 | } | 168 | } |
| 82 | if (task) { | 169 | if (task) { |
| 83 | - task(); | 170 | + if (!TryEnqueue(task)) { |
| 171 | + task(); | ||
| 172 | + } | ||
| 84 | } | 173 | } |
| 85 | return true; | 174 | return true; |
| 86 | } | 175 | } |
| @@ -0,0 +1,135 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved. | ||
| 3 | + * ubs-engine is licensed under Mulan PSL v2. | ||
| 4 | + * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 5 | + * You may obtain a copy of Mulan PSL v2 at: | ||
| 6 | + * http://license.coscl.org.cn/MulanPSL2 | ||
| 7 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 8 | + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 9 | + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 10 | + * See the Mulan PSL v2 for more details. | ||
| 11 | + */ | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +namespace ubse::ut::process_mem { | ||
| 16 | +using namespace ::process_mem::decision; | ||
| 17 | +using namespace ::process_mem::manager; | ||
| 18 | +using namespace ::process_mem::def; | ||
| 19 | +using namespace ::process_mem::pid::bridge; | ||
| 20 | + | ||
| 21 | +namespace { | ||
| 22 | +constexpr uint64_t BYTES_PER_GB = 1073741824; | ||
| 23 | +constexpr uint64_t GB3_5 = 3758096384; | ||
| 24 | +} // namespace | ||
| 25 | + | ||
| 26 | +void TestProcessMemPidBorrowReuse::SetUp() | ||
| 27 | +{ | ||
| 28 | + ubse::mem::controller::MockResetAllErrors(); | ||
| 29 | + ubse::nodeController::MockSetCurrentNodeId("NODE0"); | ||
| 30 | + | ||
| 31 | + ubse::smap::MockResetMigrateState(); | ||
| 32 | + ProcessMemPidBridge::rmrsMigrateOut = ubse::smap::MockRmrsMigrateOut; | ||
| 33 | + | ||
| 34 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 35 | + if (decision.returnExecutor_ == nullptr || !decision.returnExecutor_->IsStart()) { | ||
| 36 | + decision.returnExecutor_ = ubse::task_executor::UbseTaskExecutor::Create("ProcMemReuseReturn", 2, 64); | ||
| 37 | + decision.returnExecutor_->Start(); | ||
| 38 | + } | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +void TestProcessMemPidBorrowReuse::TearDown() | ||
| 42 | +{ | ||
| 43 | + ProcessMemPidBridge::rmrsMigrateOut = {}; | ||
| 44 | + ubse::smap::MockResetMigrateState(); | ||
| 45 | + | ||
| 46 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 47 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 48 | + for (const auto& [pid, entry] : snapshot) { | ||
| 49 | + infoMgr.RemoveManagedPidEntry(pid); | ||
| 50 | + } | ||
| 51 | + ubse::nodeController::MockResetCurrentNodeId(); | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +void TestProcessMemPidBorrowReuse::AddPidWithBorrow(pid_t pid, const def::BorrowState& borrow, | ||
| 55 | + def::ProcessStatus status) | ||
| 56 | +{ | ||
| 57 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 58 | + infoMgr.AddNameSourceToManagedPid(pid, "reuse_proc", 32 * BYTES_PER_GB, 0.5); | ||
| 59 | + infoMgr.UpdateManagedPidBorrowState(pid, borrow, status); | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +TEST_F(TestProcessMemPidBorrowReuse, NewBorrowAllTargetsOneCallMigrateFailWholeAbort) | ||
| 63 | +{ | ||
| 64 | + const pid_t pid = 15964; | ||
| 65 | + constexpr uint64_t need = 2 * BYTES_PER_GB; | ||
| 66 | + | ||
| 67 | + def::BorrowSlot slot; | ||
| 68 | + slot.capacity = 4 * BYTES_PER_GB; | ||
| 69 | + slot.migratedBytes = GB3_5; | ||
| 70 | + slot.debtId = "d1"; | ||
| 71 | + slot.remoteNumaId = 3; | ||
| 72 | + slot.status = def::BorrowSlotStatus::COMPLETED; | ||
| 73 | + def::BorrowState borrow; | ||
| 74 | + borrow.currentRemote = GB3_5; | ||
| 75 | + borrow.slots.push_back(slot); | ||
| 76 | + AddPidWithBorrow(pid, borrow, def::ProcessStatus::BORROWED); | ||
| 77 | + | ||
| 78 | + ubse::mem::controller::MockSetNumaCreateDesc(5, 9, 0); | ||
| 79 | + ubse::smap::MockSetMigrateFail(0, 3); | ||
| 80 | + | ||
| 81 | + auto debtId = ProcessMemPidDecision::GetInstance().RecordPendingBorrow(pid, need, 0, 1); | ||
| 82 | + ASSERT_FALSE(debtId.empty()); | ||
| 83 | + ProcessMemPidDecision::GetInstance().AsyncBorrowAndMigrate(debtId, pid, need, 0, 1); | ||
| 84 | + | ||
| 85 | + ASSERT_EQ(ubse::smap::MockGetMigrateCallCount(), 1u); | ||
| 86 | + auto calls = ubse::smap::MockGetMigrateCalls(); | ||
| 87 | + ASSERT_EQ(calls.size(), 2u); | ||
| 88 | + EXPECT_EQ(calls[0].first, 3); | ||
| 89 | + EXPECT_EQ(calls[0].second, GB3_5 / 1024); | ||
| 90 | + EXPECT_EQ(calls[1].first, 5); | ||
| 91 | + EXPECT_EQ(calls[1].second, need / 1024); | ||
| 92 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 3), 0u); | ||
| 93 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 5), 0u); | ||
| 94 | + | ||
| 95 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 1u); | ||
| 96 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), debtId); | ||
| 97 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 98 | + auto it = snapshot.find(pid); | ||
| 99 | + ASSERT_NE(it, snapshot.end()); | ||
| 100 | + ASSERT_EQ(it->second.borrow.slots.size(), 1u); | ||
| 101 | + EXPECT_EQ(it->second.borrow.slots[0].migratedBytes, GB3_5); | ||
| 102 | + EXPECT_EQ(it->second.borrow.currentRemote, GB3_5); | ||
| 103 | + EXPECT_EQ(it->second.processStatus, def::ProcessStatus::BORROWED); | ||
| 104 | +} | ||
| 105 | + | ||
| 106 | +TEST_F(TestProcessMemPidBorrowReuse, VanishedSlotAfterCreateReturnsUbseDebt) | ||
| 107 | +{ | ||
| 108 | + const pid_t pid = 15964; | ||
| 109 | + constexpr uint64_t need = 1 * BYTES_PER_GB; | ||
| 110 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 111 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 112 | + decision.timeoutPer128MbMs_ = 1000; | ||
| 113 | + | ||
| 114 | + infoMgr.AddNameSourceToManagedPid(pid, "reuse_proc", 32 * BYTES_PER_GB, 0.5); | ||
| 115 | + | ||
| 116 | + auto debtId = decision.RecordPendingBorrow(pid, need, 0, 1); | ||
| 117 | + ASSERT_FALSE(debtId.empty()); | ||
| 118 | + | ||
| 119 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 120 | + auto borrow = snapshot.at(pid).borrow; | ||
| 121 | + ASSERT_EQ(borrow.slots.size(), 1u); | ||
| 122 | + borrow.slots[0].borrowTime = std::chrono::steady_clock::now() - std::chrono::seconds(60); | ||
| 123 | + infoMgr.UpdateManagedPidBorrowState(pid, borrow, def::ProcessStatus::BORROWING); | ||
| 124 | + decision.CheckTimeouts(2); | ||
| 125 | + ASSERT_EQ(infoMgr.GetManagedPidCacheSnapshot().at(pid).borrow.slots.size(), 0u); | ||
| 126 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 1u); | ||
| 127 | + | ||
| 128 | + ubse::mem::controller::MockSetNumaCreateDesc(5, 9, need); | ||
| 129 | + decision.AsyncBorrowAndMigrate(debtId, pid, need, 0, 1); | ||
| 130 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 2u); | ||
| 131 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), debtId); | ||
| 132 | + snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 133 | + EXPECT_EQ(snapshot.at(pid).borrow.slots.size(), 0u); | ||
| 134 | +} | ||
| 135 | +} // namespace ubse::ut::process_mem | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved. | ||
| 3 | + * ubs-engine is licensed under Mulan PSL v2. | ||
| 4 | + * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 5 | + * You may obtain a copy of Mulan PSL v2 at: | ||
| 6 | + * http://license.coscl.org.cn/MulanPSL2 | ||
| 7 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 8 | + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 9 | + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 10 | + * See the Mulan PSL v2 for more details. | ||
| 11 | + */ | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +namespace ubse::ut::process_mem { | ||
| 35 | +namespace def = ::process_mem::def; | ||
| 36 | + | ||
| 37 | +class TestProcessMemPidBorrowReuse : public testing::Test { | ||
| 38 | +public: | ||
| 39 | + TestProcessMemPidBorrowReuse() = default; | ||
| 40 | + | ||
| 41 | + void SetUp() override; | ||
| 42 | + void TearDown() override; | ||
| 43 | + | ||
| 44 | + void AddPidWithBorrow(pid_t pid, const def::BorrowState& borrow, def::ProcessStatus status); | ||
| 45 | +}; | ||
| 46 | + | ||
| 47 | +} // namespace ubse::ut::process_mem | ||
| 48 | + | ||
| @@ -12,159 +12,31 @@ | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | + | ||
| 16 | + | ||
| 15 | 17 | ||
| 16 | 18 | ||
| 17 | 19 | ||
| 18 | 20 | ||
| 19 | 21 | ||
| 20 | 22 | ||
| 21 | -// Forward declare functions from process_mem_pid_bridge.cpp for testing | ||
| 22 | namespace process_mem::pid::bridge { | 23 | namespace process_mem::pid::bridge { |
| 23 | -void GetLentInfo(const ubse::mem::controller::UbseNumaMemoryDebtInfo& info, | ||
| 24 | - const ubse::mem::controller::UbseMemNumaDesc& desc, uint32_t& socketId, uint64_t& numaId, bool& flag); | ||
| 25 | -uint32_t ValidateSrcNumaIdOnCurrentNode(const process_mem::def::ProcessMemPidConfigInfo& configInfo); | ||
| 26 | -uint32_t SendSetPidMapErrorResponse(uint32_t ret, uint64_t requestId); | ||
| 27 | uint32_t SendPidSetResponse(int successCode, const std::string& errorMsg, uint64_t requestId); | 24 | uint32_t SendPidSetResponse(int successCode, const std::string& errorMsg, uint64_t requestId); |
| 28 | -uint32_t SetPidInfo(const api::server::UbseIpcMessage& request, const api::server::UbseRequestContext& context); | 25 | +void BuildConfigEntries(std::vector<::process_mem::def::ProcessMemDisplayEntry>& entries, |
| 29 | -uint32_t PidInfoPrint(const api::server::UbseIpcMessage& request, const api::server::UbseRequestContext& context); | 26 | + const std::vector<::process_mem::def::ProcessMemNewConfigInfo>& newConfigs); |
| 30 | -uint32_t UnSetPidInfoPrint(const api::server::UbseIpcMessage& request, const api::server::UbseRequestContext& context); | 27 | +void BuildProcDetailEntries(std::vector<::process_mem::def::ProcessMemDisplayEntry>& entries, |
| 28 | + const std::map<pid_t, ::process_mem::def::ManagedPidEntry>& managedSnapshot); | ||
| 31 | } // namespace process_mem::pid::bridge | 29 | } // namespace process_mem::pid::bridge |
| 32 | 30 | ||
| 33 | namespace ubse::ut::process_mem { | 31 | namespace ubse::ut::process_mem { |
| 34 | using namespace ::process_mem::pid::bridge; | 32 | using namespace ::process_mem::pid::bridge; |
| 35 | -using namespace ::process_mem::def; | ||
| 36 | using namespace ::process_mem::manager; | 33 | using namespace ::process_mem::manager; |
| 34 | +namespace def = ::process_mem::def; | ||
| 37 | 35 | ||
| 38 | void TestProcessMemPidBridge::SetUp() {} | 36 | void TestProcessMemPidBridge::SetUp() {} |
| 39 | 37 | ||
| 40 | void TestProcessMemPidBridge::TearDown() {} | 38 | void TestProcessMemPidBridge::TearDown() {} |
| 41 | 39 | ||
| 42 | -// ==================== GetLentInfo tests ==================== | ||
| 43 | - | ||
| 44 | -TEST_F(TestProcessMemPidBridge, GetLentInfoMatchingNameAndBorrowNode) | ||
| 45 | -{ | ||
| 46 | - ubse::mem::controller::UbseNumaMemoryDebtInfo info{}; | ||
| 47 | - info.name = "test_debt"; | ||
| 48 | - info.borrowNodeId = "1"; | ||
| 49 | - info.lentSocketIdList = {0}; | ||
| 50 | - info.lentNumaIdList = {2}; | ||
| 51 | - | ||
| 52 | - ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 53 | - desc.name = "test_debt"; | ||
| 54 | - desc.importNode.slotId = 1; | ||
| 55 | - | ||
| 56 | - uint32_t socketId = 99; | ||
| 57 | - uint64_t numaId = 99; | ||
| 58 | - bool flag = false; | ||
| 59 | - GetLentInfo(info, desc, socketId, numaId, flag); | ||
| 60 | - EXPECT_TRUE(flag); | ||
| 61 | - EXPECT_EQ(socketId, 0u); | ||
| 62 | - EXPECT_EQ(numaId, 2u); | ||
| 63 | -} | ||
| 64 | - | ||
| 65 | -TEST_F(TestProcessMemPidBridge, GetLentInfoMismatchingName) | ||
| 66 | -{ | ||
| 67 | - ubse::mem::controller::UbseNumaMemoryDebtInfo info{}; | ||
| 68 | - info.name = "other_debt"; | ||
| 69 | - info.borrowNodeId = "1"; | ||
| 70 | - info.lentSocketIdList = {0}; | ||
| 71 | - info.lentNumaIdList = {2}; | ||
| 72 | - | ||
| 73 | - ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 74 | - desc.name = "test_debt"; | ||
| 75 | - desc.importNode.slotId = 1; | ||
| 76 | - | ||
| 77 | - uint32_t socketId = 99; | ||
| 78 | - uint64_t numaId = 99; | ||
| 79 | - bool flag = false; | ||
| 80 | - GetLentInfo(info, desc, socketId, numaId, flag); | ||
| 81 | - EXPECT_FALSE(flag); | ||
| 82 | -} | ||
| 83 | - | ||
| 84 | -TEST_F(TestProcessMemPidBridge, GetLentInfoMismatchingBorrowNode) | ||
| 85 | -{ | ||
| 86 | - ubse::mem::controller::UbseNumaMemoryDebtInfo info{}; | ||
| 87 | - info.name = "test_debt"; | ||
| 88 | - info.borrowNodeId = "2"; | ||
| 89 | - info.lentSocketIdList = {0}; | ||
| 90 | - info.lentNumaIdList = {2}; | ||
| 91 | - | ||
| 92 | - ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 93 | - desc.name = "test_debt"; | ||
| 94 | - desc.importNode.slotId = 1; | ||
| 95 | - | ||
| 96 | - uint32_t socketId = 99; | ||
| 97 | - uint64_t numaId = 99; | ||
| 98 | - bool flag = false; | ||
| 99 | - GetLentInfo(info, desc, socketId, numaId, flag); | ||
| 100 | - EXPECT_FALSE(flag); | ||
| 101 | -} | ||
| 102 | - | ||
| 103 | -TEST_F(TestProcessMemPidBridge, GetLentInfoEmptySocketAndNumaList) | ||
| 104 | -{ | ||
| 105 | - ubse::mem::controller::UbseNumaMemoryDebtInfo info{}; | ||
| 106 | - info.name = "test_debt"; | ||
| 107 | - info.borrowNodeId = "1"; | ||
| 108 | - // Both empty: flag stays false, no crash | ||
| 109 | - | ||
| 110 | - ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 111 | - desc.name = "test_debt"; | ||
| 112 | - desc.importNode.slotId = 1; | ||
| 113 | - | ||
| 114 | - uint32_t socketId = 99; | ||
| 115 | - uint64_t numaId = 99; | ||
| 116 | - bool flag = false; | ||
| 117 | - GetLentInfo(info, desc, socketId, numaId, flag); | ||
| 118 | - EXPECT_FALSE(flag); | ||
| 119 | -} | ||
| 120 | - | ||
| 121 | -// ==================== ValidateSrcNumaIdOnCurrentNode tests ==================== | ||
| 122 | - | ||
| 123 | -TEST_F(TestProcessMemPidBridge, ValidateSrcNumaIdOnCurrentNodeNoSrcNuma) | ||
| 124 | -{ | ||
| 125 | - ProcessMemPidConfigInfo configInfo{}; | ||
| 126 | - configInfo.srcNumaId = std::nullopt; | ||
| 127 | - auto ret = ValidateSrcNumaIdOnCurrentNode(configInfo); | ||
| 128 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 129 | -} | ||
| 130 | - | ||
| 131 | -TEST_F(TestProcessMemPidBridge, ValidateSrcNumaIdOnCurrentNodeWithSrcNuma) | ||
| 132 | -{ | ||
| 133 | - ProcessMemPidConfigInfo configInfo{}; | ||
| 134 | - configInfo.srcNumaId = 2; | ||
| 135 | - auto ret = ValidateSrcNumaIdOnCurrentNode(configInfo); | ||
| 136 | - // mock GetCurNode returns empty numaInfos, so srcNumaId won't match | ||
| 137 | - EXPECT_NE(ret, UBSE_OK); | ||
| 138 | -} | ||
| 139 | - | ||
| 140 | -// ==================== SendSetPidMapErrorResponse tests ==================== | ||
| 141 | - | ||
| 142 | -TEST_F(TestProcessMemPidBridge, SendSetPidMapErrorResponseNotExist) | ||
| 143 | -{ | ||
| 144 | - auto ret = SendSetPidMapErrorResponse(UBSE_ERR_NOT_EXIST, 12345); | ||
| 145 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 146 | -} | ||
| 147 | - | ||
| 148 | -TEST_F(TestProcessMemPidBridge, SendSetPidMapErrorResponseInvalidArg) | ||
| 149 | -{ | ||
| 150 | - auto ret = SendSetPidMapErrorResponse(UBSE_ERR_INVALID_ARG, 12345); | ||
| 151 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 152 | -} | ||
| 153 | - | ||
| 154 | -TEST_F(TestProcessMemPidBridge, SendSetPidMapErrorResponseResourceBusy) | ||
| 155 | -{ | ||
| 156 | - auto ret = SendSetPidMapErrorResponse(UBSE_ERR_RESOURCE_BUSY, 12345); | ||
| 157 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 158 | -} | ||
| 159 | - | ||
| 160 | -TEST_F(TestProcessMemPidBridge, SendSetPidMapErrorResponseGeneric) | ||
| 161 | -{ | ||
| 162 | - auto ret = SendSetPidMapErrorResponse(UBSE_ERROR, 12345); | ||
| 163 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 164 | -} | ||
| 165 | - | ||
| 166 | -// ==================== SendPidSetResponse tests ==================== | ||
| 167 | - | ||
| 168 | TEST_F(TestProcessMemPidBridge, SendPidSetResponseSuccess) | 40 | TEST_F(TestProcessMemPidBridge, SendPidSetResponseSuccess) |
| 169 | { | 41 | { |
| 170 | auto ret = SendPidSetResponse(1, "", 12345); | 42 | auto ret = SendPidSetResponse(1, "", 12345); |
| @@ -177,180 +49,18 @@ TEST_F(TestProcessMemPidBridge, SendPidSetResponseFailure) | |||
| 177 | EXPECT_EQ(ret, UBSE_OK); | 49 | EXPECT_EQ(ret, UBSE_OK); |
| 178 | } | 50 | } |
| 179 | 51 | ||
| 180 | -// ==================== SetPidInfo tests ==================== | 52 | +TEST_F(TestProcessMemPidBridge, SendPidSetResponseWithLongMessage) |
| 181 | - | ||
| 182 | -TEST_F(TestProcessMemPidBridge, SetPidInfoWithInvalidDeserialize) | ||
| 183 | { | 53 | { |
| 184 | - api::server::UbseIpcMessage request{}; | 54 | + auto ret = SendPidSetResponse(1, "A very long error message that exceeds normal length", 99999); |
| 185 | - request.buffer = nullptr; | ||
| 186 | - request.length = 0; | ||
| 187 | - api::server::UbseRequestContext context{}; | ||
| 188 | - context.requestId = 1; | ||
| 189 | - auto ret = SetPidInfo(request, context); | ||
| 190 | - EXPECT_NE(ret, UBSE_OK); | ||
| 191 | -} | ||
| 192 | - | ||
| 193 | -TEST_F(TestProcessMemPidBridge, SetPidInfoWithValidData) | ||
| 194 | -{ | ||
| 195 | - ProcessMemPidInfo pidInfo{}; | ||
| 196 | - pidInfo.configInfo.pid = 77777; | ||
| 197 | - pidInfo.configInfo.evictThreshold = 80; | ||
| 198 | - pidInfo.startTime = 1000; | ||
| 199 | - | ||
| 200 | - ubse::serial::UbseSerialization serializer; | ||
| 201 | - pidInfo.configInfo.SerializeConfigInfo(serializer); | ||
| 202 | - | ||
| 203 | - api::server::UbseIpcMessage request{}; | ||
| 204 | - request.buffer = serializer.GetBuffer(); | ||
| 205 | - request.length = static_cast<uint32_t>(serializer.GetLength()); | ||
| 206 | - api::server::UbseRequestContext context{}; | ||
| 207 | - context.requestId = 1; | ||
| 208 | - | ||
| 209 | - auto ret = SetPidInfo(request, context); | ||
| 210 | - // mock curNode has no numa matching srcNumaId -> will reject with message about srcNumaId | ||
| 211 | EXPECT_EQ(ret, UBSE_OK); | 55 | EXPECT_EQ(ret, UBSE_OK); |
| 212 | } | 56 | } |
| 213 | 57 | ||
| 214 | -// ==================== PidInfoPrint tests ==================== | 58 | +TEST_F(TestProcessMemPidBridge, InitToleratesMissingLibrary) |
| 215 | - | ||
| 216 | -TEST_F(TestProcessMemPidBridge, PidInfoPrintSuccess) | ||
| 217 | -{ | ||
| 218 | - api::server::UbseIpcMessage request{}; | ||
| 219 | - request.buffer = nullptr; | ||
| 220 | - request.length = 0; | ||
| 221 | - api::server::UbseRequestContext context{}; | ||
| 222 | - context.requestId = 1; | ||
| 223 | - auto ret = PidInfoPrint(request, context); | ||
| 224 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 225 | -} | ||
| 226 | - | ||
| 227 | -// ==================== UnSetPidInfoPrint tests ==================== | ||
| 228 | - | ||
| 229 | -TEST_F(TestProcessMemPidBridge, UnSetPidInfoPrintNotConfigured) | ||
| 230 | -{ | ||
| 231 | - ubse::serial::UbseSerialization serializer; | ||
| 232 | - pid_t pid = 99999999; | ||
| 233 | - serializer << pid; | ||
| 234 | - | ||
| 235 | - api::server::UbseIpcMessage request{}; | ||
| 236 | - request.buffer = serializer.GetBuffer(); | ||
| 237 | - request.length = static_cast<uint32_t>(serializer.GetLength()); | ||
| 238 | - api::server::UbseRequestContext context{}; | ||
| 239 | - context.requestId = 1; | ||
| 240 | - | ||
| 241 | - auto ret = UnSetPidInfoPrint(request, context); | ||
| 242 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 243 | -} | ||
| 244 | - | ||
| 245 | -TEST_F(TestProcessMemPidBridge, UnSetPidInfoPrintSuccess) | ||
| 246 | -{ | ||
| 247 | - auto& mgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 248 | - ProcessMemPidInfo pidInfo{}; | ||
| 249 | - pidInfo.configInfo.pid = 50001; | ||
| 250 | - pidInfo.startTime = 1000; | ||
| 251 | - mgr.SetPidInfoMap(pidInfo); | ||
| 252 | - | ||
| 253 | - ubse::serial::UbseSerialization serializer; | ||
| 254 | - pid_t unsetPid = 50001; | ||
| 255 | - serializer << unsetPid; | ||
| 256 | - | ||
| 257 | - api::server::UbseIpcMessage request{}; | ||
| 258 | - request.buffer = serializer.GetBuffer(); | ||
| 259 | - request.length = static_cast<uint32_t>(serializer.GetLength()); | ||
| 260 | - api::server::UbseRequestContext context{}; | ||
| 261 | - context.requestId = 1; | ||
| 262 | - | ||
| 263 | - auto ret = UnSetPidInfoPrint(request, context); | ||
| 264 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 265 | -} | ||
| 266 | - | ||
| 267 | -TEST_F(TestProcessMemPidBridge, MemoryBorrowWithExportSlotIdNegative) | ||
| 268 | -{ | ||
| 269 | - ProcessMemPidInfo pidInfo{}; | ||
| 270 | - pidInfo.configInfo.pid = 1234; | ||
| 271 | - pidInfo.memBorrowInfo.exportSlotId = -1; | ||
| 272 | - | ||
| 273 | - ubse::mem::controller::UbseMemBorrower borrower{}; | ||
| 274 | - borrower.nodeId = "NODE0"; | ||
| 275 | - | ||
| 276 | - MemoryBorrowRequest request{}; | ||
| 277 | - request.name = "test_borrow"; | ||
| 278 | - request.size = 1024 * 1024; | ||
| 279 | - | ||
| 280 | - ubse::mem::controller::UbseMemNumaDesc borrowInfo{}; | ||
| 281 | - | ||
| 282 | - auto ret = ProcessMemPidBridge::MemoryBorrow(pidInfo, borrower, request, borrowInfo); | ||
| 283 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 284 | -} | ||
| 285 | - | ||
| 286 | -TEST_F(TestProcessMemPidBridge, MemoryBorrowWithExportSlotIdSet) | ||
| 287 | -{ | ||
| 288 | - ProcessMemPidInfo pidInfo{}; | ||
| 289 | - pidInfo.configInfo.pid = 2345; | ||
| 290 | - pidInfo.memBorrowInfo.exportSlotId = 1; | ||
| 291 | - | ||
| 292 | - ubse::mem::controller::UbseMemBorrower borrower{}; | ||
| 293 | - borrower.nodeId = "NODE0"; | ||
| 294 | - | ||
| 295 | - MemoryBorrowRequest request{}; | ||
| 296 | - request.name = "test_borrow_candidate"; | ||
| 297 | - request.size = 2048 * 1024; | ||
| 298 | - | ||
| 299 | - ubse::mem::controller::UbseMemNumaDesc borrowInfo{}; | ||
| 300 | - | ||
| 301 | - auto ret = ProcessMemPidBridge::MemoryBorrow(pidInfo, borrower, request, borrowInfo); | ||
| 302 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 303 | -} | ||
| 304 | - | ||
| 305 | -TEST_F(TestProcessMemPidBridge, MemoryReturnSuccess) | ||
| 306 | -{ | ||
| 307 | - auto ret = ProcessMemPidBridge::MemoryReturn("test_return_name"); | ||
| 308 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 309 | -} | ||
| 310 | - | ||
| 311 | -TEST_F(TestProcessMemPidBridge, GetRemoteNumaSocketInfo) | ||
| 312 | -{ | ||
| 313 | - ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 314 | - desc.name = "test_numa"; | ||
| 315 | - desc.exportNode.slotId = 1; | ||
| 316 | - desc.importNode.slotId = 2; | ||
| 317 | - | ||
| 318 | - uint32_t socketId = 0; | ||
| 319 | - uint64_t numaId = 0; | ||
| 320 | - | ||
| 321 | - auto ret = ProcessMemPidBridge::GetRemoteNumaSocketInfo(desc, socketId, numaId); | ||
| 322 | - EXPECT_NE(ret, UBSE_OK); | ||
| 323 | -} | ||
| 324 | - | ||
| 325 | -TEST_F(TestProcessMemPidBridge, FaultHandlerSuccess) | ||
| 326 | -{ | ||
| 327 | - auto ret = ProcessMemPidBridge::FaultHandler(ubse::ras::ALARM_PANIC_EVENT, "NODE_FAULT"); | ||
| 328 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 329 | -} | ||
| 330 | - | ||
| 331 | -TEST_F(TestProcessMemPidBridge, ProcessMemNodeFaultNotifyHandler) | ||
| 332 | -{ | ||
| 333 | - ubse::serial::UbseSerialization serializer; | ||
| 334 | - std::string faultNodeId = "NODE_FAULT_1"; | ||
| 335 | - serializer << faultNodeId; | ||
| 336 | - | ||
| 337 | - UbseByteBuffer req{}; | ||
| 338 | - req.data = serializer.GetBuffer(); | ||
| 339 | - req.len = serializer.GetLength(); | ||
| 340 | - | ||
| 341 | - UbseByteBuffer resp{}; | ||
| 342 | - EXPECT_NO_THROW(ProcessMemPidBridge::ProcessMemNodeFaultNotifyHandler(req, resp)); | ||
| 343 | -} | ||
| 344 | - | ||
| 345 | -TEST_F(TestProcessMemPidBridge, NotifyBorrowNodesOnFault) | ||
| 346 | -{ | ||
| 347 | - EXPECT_NO_THROW(ProcessMemPidBridge::NotifyBorrowNodesOnFault("NODE_FAULT_2")); | ||
| 348 | -} | ||
| 349 | - | ||
| 350 | -TEST_F(TestProcessMemPidBridge, InitFailNoLibrary) | ||
| 351 | { | 59 | { |
| 60 | + ProcessMemPidBridge::memPoolingHandle = nullptr; | ||
| 352 | auto ret = ProcessMemPidBridge::Init(); | 61 | auto ret = ProcessMemPidBridge::Init(); |
| 353 | - EXPECT_NE(ret, UBSE_OK); | 62 | + EXPECT_EQ(ret, UBSE_OK); |
| 63 | + EXPECT_EQ(ProcessMemPidBridge::memPoolingHandle, nullptr); | ||
| 354 | } | 64 | } |
| 355 | 65 | ||
| 356 | TEST_F(TestProcessMemPidBridge, UnInitWithNullHandle) | 66 | TEST_F(TestProcessMemPidBridge, UnInitWithNullHandle) |
| @@ -360,110 +70,9 @@ TEST_F(TestProcessMemPidBridge, UnInitWithNullHandle) | |||
| 360 | EXPECT_EQ(ret, UBSE_OK); | 70 | EXPECT_EQ(ret, UBSE_OK); |
| 361 | } | 71 | } |
| 362 | 72 | ||
| 363 | -TEST_F(TestProcessMemPidBridge, MemoryBorrowWithZeroSize) | 73 | +TEST_F(TestProcessMemPidBridge, MemoryReturnSuccess) |
| 364 | { | 74 | { |
| 365 | - ProcessMemPidInfo pidInfo{}; | 75 | + auto ret = ProcessMemPidBridge::MemoryReturn("test_return_name"); |
| 366 | - pidInfo.configInfo.pid = 3001; | ||
| 367 | - pidInfo.memBorrowInfo.exportSlotId = -1; | ||
| 368 | - | ||
| 369 | - ubse::mem::controller::UbseMemBorrower borrower{}; | ||
| 370 | - borrower.nodeId = "NODE0"; | ||
| 371 | - | ||
| 372 | - MemoryBorrowRequest request{}; | ||
| 373 | - request.name = "test_borrow_zero_size"; | ||
| 374 | - request.size = 0; | ||
| 375 | - | ||
| 376 | - ubse::mem::controller::UbseMemNumaDesc borrowInfo{}; | ||
| 377 | - | ||
| 378 | - auto ret = ProcessMemPidBridge::MemoryBorrow(pidInfo, borrower, request, borrowInfo); | ||
| 379 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 380 | -} | ||
| 381 | - | ||
| 382 | -TEST_F(TestProcessMemPidBridge, MemoryBorrowWithEmptyName) | ||
| 383 | -{ | ||
| 384 | - ProcessMemPidInfo pidInfo{}; | ||
| 385 | - pidInfo.configInfo.pid = 3002; | ||
| 386 | - pidInfo.memBorrowInfo.exportSlotId = -1; | ||
| 387 | - | ||
| 388 | - ubse::mem::controller::UbseMemBorrower borrower{}; | ||
| 389 | - borrower.nodeId = "NODE0"; | ||
| 390 | - | ||
| 391 | - MemoryBorrowRequest request{}; | ||
| 392 | - request.name = ""; | ||
| 393 | - request.size = 1024 * 1024; | ||
| 394 | - | ||
| 395 | - ubse::mem::controller::UbseMemNumaDesc borrowInfo{}; | ||
| 396 | - | ||
| 397 | - auto ret = ProcessMemPidBridge::MemoryBorrow(pidInfo, borrower, request, borrowInfo); | ||
| 398 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 399 | -} | ||
| 400 | - | ||
| 401 | -TEST_F(TestProcessMemPidBridge, MemoryBorrowWithUsrInfo) | ||
| 402 | -{ | ||
| 403 | - ProcessMemPidInfo pidInfo{}; | ||
| 404 | - pidInfo.configInfo.pid = 3003; | ||
| 405 | - pidInfo.memBorrowInfo.exportSlotId = -1; | ||
| 406 | - | ||
| 407 | - ubse::mem::controller::UbseMemBorrower borrower{}; | ||
| 408 | - borrower.nodeId = "NODE0"; | ||
| 409 | - | ||
| 410 | - MemoryBorrowRequest request{}; | ||
| 411 | - request.name = "test_borrow_usr_info"; | ||
| 412 | - request.size = 4096 * 1024; | ||
| 413 | - ProcessMemUsrInfo usrInfo{}; | ||
| 414 | - usrInfo.pluginId = UsrInfoPluginType::PROCESS_MEM; | ||
| 415 | - usrInfo.pid = 3003; | ||
| 416 | - usrInfo.startTime = 12345; | ||
| 417 | - usrInfo.srcNuma = 0; | ||
| 418 | - memcpy(request.usrInfo, &usrInfo, sizeof(usrInfo)); | ||
| 419 | - | ||
| 420 | - ubse::mem::controller::UbseMemNumaDesc borrowInfo{}; | ||
| 421 | - | ||
| 422 | - auto ret = ProcessMemPidBridge::MemoryBorrow(pidInfo, borrower, request, borrowInfo); | ||
| 423 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 424 | -} | ||
| 425 | - | ||
| 426 | -TEST_F(TestProcessMemPidBridge, MemoryBorrowWithCandidateAndUsrInfo) | ||
| 427 | -{ | ||
| 428 | - ProcessMemPidInfo pidInfo{}; | ||
| 429 | - pidInfo.configInfo.pid = 3004; | ||
| 430 | - pidInfo.memBorrowInfo.exportSlotId = 5; | ||
| 431 | - | ||
| 432 | - ubse::mem::controller::UbseMemBorrower borrower{}; | ||
| 433 | - borrower.nodeId = "NODE0"; | ||
| 434 | - | ||
| 435 | - MemoryBorrowRequest request{}; | ||
| 436 | - request.name = "test_borrow_candidate_usr"; | ||
| 437 | - request.size = 8192 * 1024; | ||
| 438 | - ProcessMemUsrInfo usrInfo{}; | ||
| 439 | - usrInfo.pluginId = UsrInfoPluginType::PROCESS_MEM; | ||
| 440 | - usrInfo.pid = 3004; | ||
| 441 | - usrInfo.startTime = 54321; | ||
| 442 | - usrInfo.srcNuma = 1; | ||
| 443 | - memcpy(request.usrInfo, &usrInfo, sizeof(usrInfo)); | ||
| 444 | - | ||
| 445 | - ubse::mem::controller::UbseMemNumaDesc borrowInfo{}; | ||
| 446 | - | ||
| 447 | - auto ret = ProcessMemPidBridge::MemoryBorrow(pidInfo, borrower, request, borrowInfo); | ||
| 448 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 449 | -} | ||
| 450 | - | ||
| 451 | -TEST_F(TestProcessMemPidBridge, MemoryBorrowWithLargeExportSlotId) | ||
| 452 | -{ | ||
| 453 | - ProcessMemPidInfo pidInfo{}; | ||
| 454 | - pidInfo.configInfo.pid = 3005; | ||
| 455 | - pidInfo.memBorrowInfo.exportSlotId = 255; | ||
| 456 | - | ||
| 457 | - ubse::mem::controller::UbseMemBorrower borrower{}; | ||
| 458 | - borrower.nodeId = "NODE1"; | ||
| 459 | - | ||
| 460 | - MemoryBorrowRequest request{}; | ||
| 461 | - request.name = "test_borrow_large_slot"; | ||
| 462 | - request.size = 16 * 1024 * 1024; | ||
| 463 | - | ||
| 464 | - ubse::mem::controller::UbseMemNumaDesc borrowInfo{}; | ||
| 465 | - | ||
| 466 | - auto ret = ProcessMemPidBridge::MemoryBorrow(pidInfo, borrower, request, borrowInfo); | ||
| 467 | EXPECT_EQ(ret, UBSE_OK); | 76 | EXPECT_EQ(ret, UBSE_OK); |
| 468 | } | 77 | } |
| 469 | 78 | ||
| @@ -473,327 +82,6 @@ TEST_F(TestProcessMemPidBridge, MemoryReturnWithEmptyName) | |||
| 473 | EXPECT_EQ(ret, UBSE_OK); | 82 | EXPECT_EQ(ret, UBSE_OK); |
| 474 | } | 83 | } |
| 475 | 84 | ||
| 476 | -TEST_F(TestProcessMemPidBridge, GetRemoteNumaSocketInfoWithDefaultDesc) | ||
| 477 | -{ | ||
| 478 | - ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 479 | - | ||
| 480 | - uint32_t socketId = 0; | ||
| 481 | - uint64_t numaId = 0; | ||
| 482 | - | ||
| 483 | - auto ret = ProcessMemPidBridge::GetRemoteNumaSocketInfo(desc, socketId, numaId); | ||
| 484 | - EXPECT_NE(ret, UBSE_OK); | ||
| 485 | -} | ||
| 486 | - | ||
| 487 | -TEST_F(TestProcessMemPidBridge, GetRemoteNumaSocketInfoWithSameSlotIds) | ||
| 488 | -{ | ||
| 489 | - ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 490 | - desc.name = "test_same_slots"; | ||
| 491 | - desc.exportNode.slotId = 1; | ||
| 492 | - desc.importNode.slotId = 1; | ||
| 493 | - | ||
| 494 | - uint32_t socketId = 0; | ||
| 495 | - uint64_t numaId = 0; | ||
| 496 | - | ||
| 497 | - auto ret = ProcessMemPidBridge::GetRemoteNumaSocketInfo(desc, socketId, numaId); | ||
| 498 | - EXPECT_NE(ret, UBSE_OK); | ||
| 499 | -} | ||
| 500 | - | ||
| 501 | -TEST_F(TestProcessMemPidBridge, GetRemoteNumaSocketInfoWithLargeSlotIds) | ||
| 502 | -{ | ||
| 503 | - ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 504 | - desc.name = "test_large_slots"; | ||
| 505 | - desc.exportNode.slotId = 100; | ||
| 506 | - desc.importNode.slotId = 200; | ||
| 507 | - | ||
| 508 | - uint32_t socketId = 0; | ||
| 509 | - uint64_t numaId = 0; | ||
| 510 | - | ||
| 511 | - auto ret = ProcessMemPidBridge::GetRemoteNumaSocketInfo(desc, socketId, numaId); | ||
| 512 | - EXPECT_NE(ret, UBSE_OK); | ||
| 513 | -} | ||
| 514 | - | ||
| 515 | -TEST_F(TestProcessMemPidBridge, FaultHandlerWithKernelRebootEvent) | ||
| 516 | -{ | ||
| 517 | - auto ret = ProcessMemPidBridge::FaultHandler(ubse::ras::ALARM_KERNEL_REBOOT_EVENT, "NODE_REBOOT"); | ||
| 518 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 519 | -} | ||
| 520 | - | ||
| 521 | -TEST_F(TestProcessMemPidBridge, FaultHandlerWithEmptyFaultInfo) | ||
| 522 | -{ | ||
| 523 | - auto ret = ProcessMemPidBridge::FaultHandler(ubse::ras::ALARM_PANIC_EVENT, ""); | ||
| 524 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 525 | -} | ||
| 526 | - | ||
| 527 | -TEST_F(TestProcessMemPidBridge, ProcessMemNodeFaultNotifyHandlerWithNullData) | ||
| 528 | -{ | ||
| 529 | - UbseByteBuffer req{}; | ||
| 530 | - req.data = nullptr; | ||
| 531 | - req.len = 0; | ||
| 532 | - | ||
| 533 | - UbseByteBuffer resp{}; | ||
| 534 | - EXPECT_NO_THROW(ProcessMemPidBridge::ProcessMemNodeFaultNotifyHandler(req, resp)); | ||
| 535 | -} | ||
| 536 | - | ||
| 537 | -TEST_F(TestProcessMemPidBridge, ProcessMemNodeFaultNotifyHandlerWithEmptyData) | ||
| 538 | -{ | ||
| 539 | - uint8_t dummy = 0; | ||
| 540 | - UbseByteBuffer req{}; | ||
| 541 | - req.data = &dummy; | ||
| 542 | - req.len = 0; | ||
| 543 | - | ||
| 544 | - UbseByteBuffer resp{}; | ||
| 545 | - EXPECT_NO_THROW(ProcessMemPidBridge::ProcessMemNodeFaultNotifyHandler(req, resp)); | ||
| 546 | -} | ||
| 547 | - | ||
| 548 | -TEST_F(TestProcessMemPidBridge, ProcessMemNodeFaultNotifyHandlerWithValidData) | ||
| 549 | -{ | ||
| 550 | - ubse::serial::UbseSerialization serializer; | ||
| 551 | - std::string faultNodeId = "NODE_FAULT_VALID"; | ||
| 552 | - serializer << faultNodeId; | ||
| 553 | - | ||
| 554 | - UbseByteBuffer req{}; | ||
| 555 | - req.data = serializer.GetBuffer(); | ||
| 556 | - req.len = serializer.GetLength(); | ||
| 557 | - | ||
| 558 | - UbseByteBuffer resp{}; | ||
| 559 | - EXPECT_NO_THROW(ProcessMemPidBridge::ProcessMemNodeFaultNotifyHandler(req, resp)); | ||
| 560 | -} | ||
| 561 | - | ||
| 562 | -TEST_F(TestProcessMemPidBridge, NotifyBorrowNodesOnFaultWithEmptyNodeId) | ||
| 563 | -{ | ||
| 564 | - EXPECT_NO_THROW(ProcessMemPidBridge::NotifyBorrowNodesOnFault("")); | ||
| 565 | -} | ||
| 566 | - | ||
| 567 | -TEST_F(TestProcessMemPidBridge, NotifyBorrowNodesOnFaultWithNonExistentNode) | ||
| 568 | -{ | ||
| 569 | - EXPECT_NO_THROW(ProcessMemPidBridge::NotifyBorrowNodesOnFault("NON_EXISTENT_NODE")); | ||
| 570 | -} | ||
| 571 | - | ||
| 572 | -TEST_F(TestProcessMemPidBridge, InitFailNoLibraryResetsHandle) | ||
| 573 | -{ | ||
| 574 | - ProcessMemPidBridge::memPoolingHandle = nullptr; | ||
| 575 | - auto ret = ProcessMemPidBridge::Init(); | ||
| 576 | - EXPECT_NE(ret, UBSE_OK); | ||
| 577 | - EXPECT_EQ(ProcessMemPidBridge::memPoolingHandle, nullptr); | ||
| 578 | -} | ||
| 579 | - | ||
| 580 | -// ==================== Extended tests ==================== | ||
| 581 | - | ||
| 582 | -TEST_F(TestProcessMemPidBridge, FaultHandlerWithUnknownFaultEvent) | ||
| 583 | -{ | ||
| 584 | - auto ret = ProcessMemPidBridge::FaultHandler(static_cast<ubse::ras::ALARM_FAULT_TYPE>(999), "NODE_UNKNOWN"); | ||
| 585 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 586 | -} | ||
| 587 | - | ||
| 588 | -// ==================== Additional edge case tests ==================== | ||
| 589 | - | ||
| 590 | -TEST_F(TestProcessMemPidBridge, MemoryBorrowWithDifferentUsrInfoSizes) | ||
| 591 | -{ | ||
| 592 | - ProcessMemPidInfo pidInfo{}; | ||
| 593 | - pidInfo.configInfo.pid = 4001; | ||
| 594 | - pidInfo.memBorrowInfo.exportSlotId = -1; | ||
| 595 | - | ||
| 596 | - ubse::mem::controller::UbseMemBorrower borrower{}; | ||
| 597 | - borrower.nodeId = "NODE2"; | ||
| 598 | - | ||
| 599 | - MemoryBorrowRequest request{}; | ||
| 600 | - request.name = "test_diff_usr_sizes"; | ||
| 601 | - request.size = 1024; | ||
| 602 | - ProcessMemUsrInfo usrInfo{}; | ||
| 603 | - usrInfo.pluginId = UsrInfoPluginType::PROCESS_MEM; | ||
| 604 | - usrInfo.pid = 4001; | ||
| 605 | - memcpy(request.usrInfo, &usrInfo, sizeof(usrInfo)); | ||
| 606 | - | ||
| 607 | - ubse::mem::controller::UbseMemNumaDesc borrowInfo{}; | ||
| 608 | - auto ret = ProcessMemPidBridge::MemoryBorrow(pidInfo, borrower, request, borrowInfo); | ||
| 609 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 610 | -} | ||
| 611 | - | ||
| 612 | -TEST_F(TestProcessMemPidBridge, MemoryBorrowWithNodeIdVariations) | ||
| 613 | -{ | ||
| 614 | - ProcessMemPidInfo pidInfo{}; | ||
| 615 | - pidInfo.configInfo.pid = 4002; | ||
| 616 | - pidInfo.memBorrowInfo.exportSlotId = -1; | ||
| 617 | - | ||
| 618 | - ubse::mem::controller::UbseMemBorrower borrower{}; | ||
| 619 | - borrower.nodeId = "NODE_ZERO"; | ||
| 620 | - | ||
| 621 | - MemoryBorrowRequest request{}; | ||
| 622 | - request.name = "test_node_variation"; | ||
| 623 | - request.size = 4096; | ||
| 624 | - | ||
| 625 | - ubse::mem::controller::UbseMemNumaDesc borrowInfo{}; | ||
| 626 | - auto ret = ProcessMemPidBridge::MemoryBorrow(pidInfo, borrower, request, borrowInfo); | ||
| 627 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 628 | -} | ||
| 629 | - | ||
| 630 | -TEST_F(TestProcessMemPidBridge, MemoryBorrowWithLargeSize) | ||
| 631 | -{ | ||
| 632 | - ProcessMemPidInfo pidInfo{}; | ||
| 633 | - pidInfo.configInfo.pid = 4003; | ||
| 634 | - pidInfo.memBorrowInfo.exportSlotId = -1; | ||
| 635 | - | ||
| 636 | - ubse::mem::controller::UbseMemBorrower borrower{}; | ||
| 637 | - borrower.nodeId = "NODE0"; | ||
| 638 | - | ||
| 639 | - MemoryBorrowRequest request{}; | ||
| 640 | - request.name = "test_large_size"; | ||
| 641 | - request.size = UINT64_MAX; | ||
| 642 | - | ||
| 643 | - ubse::mem::controller::UbseMemNumaDesc borrowInfo{}; | ||
| 644 | - auto ret = ProcessMemPidBridge::MemoryBorrow(pidInfo, borrower, request, borrowInfo); | ||
| 645 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 646 | -} | ||
| 647 | - | ||
| 648 | -TEST_F(TestProcessMemPidBridge, MemoryReturnWithTrailingSpaces) | ||
| 649 | -{ | ||
| 650 | - auto ret = ProcessMemPidBridge::MemoryReturn("name_with_trailing "); | ||
| 651 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 652 | -} | ||
| 653 | - | ||
| 654 | -TEST_F(TestProcessMemPidBridge, SetPidInfoWithZeroThresholds) | ||
| 655 | -{ | ||
| 656 | - ProcessMemPidInfo pidInfo{}; | ||
| 657 | - pidInfo.configInfo.pid = 5001; | ||
| 658 | - pidInfo.configInfo.evictThreshold = 0; | ||
| 659 | - pidInfo.configInfo.targetEvictThreshold = 0; | ||
| 660 | - pidInfo.configInfo.reclaimThreshold = 0; | ||
| 661 | - pidInfo.configInfo.expectedMemoryUsage = 0; | ||
| 662 | - pidInfo.startTime = 1000; | ||
| 663 | - | ||
| 664 | - ubse::serial::UbseSerialization serializer; | ||
| 665 | - pidInfo.configInfo.SerializeConfigInfo(serializer); | ||
| 666 | - | ||
| 667 | - api::server::UbseIpcMessage request{}; | ||
| 668 | - request.buffer = serializer.GetBuffer(); | ||
| 669 | - request.length = static_cast<uint32_t>(serializer.GetLength()); | ||
| 670 | - api::server::UbseRequestContext context{}; | ||
| 671 | - context.requestId = 2; | ||
| 672 | - | ||
| 673 | - auto ret = SetPidInfo(request, context); | ||
| 674 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 675 | -} | ||
| 676 | - | ||
| 677 | -TEST_F(TestProcessMemPidBridge, PidInfoPrintWithConfiguredPids) | ||
| 678 | -{ | ||
| 679 | - ProcessMemPidInfo pidInfo{}; | ||
| 680 | - pidInfo.configInfo.pid = 5002; | ||
| 681 | - pidInfo.startTime = 1000; | ||
| 682 | - auto& mgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 683 | - mgr.SetPidInfoMap(pidInfo); | ||
| 684 | - | ||
| 685 | - api::server::UbseIpcMessage request{}; | ||
| 686 | - request.buffer = nullptr; | ||
| 687 | - request.length = 0; | ||
| 688 | - api::server::UbseRequestContext context{}; | ||
| 689 | - context.requestId = 2; | ||
| 690 | - auto ret = PidInfoPrint(request, context); | ||
| 691 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 692 | - | ||
| 693 | - mgr.UnsetPidInfo(5002); | ||
| 694 | -} | ||
| 695 | - | ||
| 696 | -TEST_F(TestProcessMemPidBridge, GetRemoteNumaSocketInfoWithValidDesc) | ||
| 697 | -{ | ||
| 698 | - ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 699 | - desc.name = "remote_numa"; | ||
| 700 | - desc.exportNode.slotId = 5; | ||
| 701 | - desc.importNode.slotId = 3; | ||
| 702 | - | ||
| 703 | - uint32_t socketId = 99; | ||
| 704 | - uint64_t numaId = 99; | ||
| 705 | - auto ret = ProcessMemPidBridge::GetRemoteNumaSocketInfo(desc, socketId, numaId); | ||
| 706 | - EXPECT_NE(ret, UBSE_OK); | ||
| 707 | -} | ||
| 708 | - | ||
| 709 | -TEST_F(TestProcessMemPidBridge, NotifyBorrowNodesOnFaultWithValidNodeId) | ||
| 710 | -{ | ||
| 711 | - auto ret = ProcessMemPidBridge::NotifyBorrowNodesOnFault("VALID_NODE_1"); | ||
| 712 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 713 | -} | ||
| 714 | - | ||
| 715 | -// ==================== PidInfoPrint with null buffer (empty request) ==================== | ||
| 716 | - | ||
| 717 | -TEST_F(TestProcessMemPidBridge, PidInfoPrintWithNonnullEmptyBuffer) | ||
| 718 | -{ | ||
| 719 | - api::server::UbseIpcMessage request{}; | ||
| 720 | - uint8_t dummy = 0; | ||
| 721 | - request.buffer = &dummy; | ||
| 722 | - request.length = 0; | ||
| 723 | - api::server::UbseRequestContext context{}; | ||
| 724 | - context.requestId = 3; | ||
| 725 | - auto ret = PidInfoPrint(request, context); | ||
| 726 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 727 | -} | ||
| 728 | - | ||
| 729 | -// ==================== UnSetPidInfoPrint edge cases ==================== | ||
| 730 | - | ||
| 731 | -TEST_F(TestProcessMemPidBridge, UnSetPidInfoPrintWithNullBuffer) | ||
| 732 | -{ | ||
| 733 | - api::server::UbseIpcMessage request{}; | ||
| 734 | - request.buffer = nullptr; | ||
| 735 | - request.length = 0; | ||
| 736 | - api::server::UbseRequestContext context{}; | ||
| 737 | - context.requestId = 4; | ||
| 738 | - auto ret = UnSetPidInfoPrint(request, context); | ||
| 739 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 740 | -} | ||
| 741 | - | ||
| 742 | -// ==================== ValidateSrcNumaIdOnCurrentNode with nullopt ==================== | ||
| 743 | - | ||
| 744 | -TEST_F(TestProcessMemPidBridge, ValidateSrcNumaIdOnCurrentNodeNullOpt) | ||
| 745 | -{ | ||
| 746 | - ProcessMemPidConfigInfo configInfo{}; | ||
| 747 | - configInfo.srcNumaId = std::nullopt; | ||
| 748 | - auto ret = ValidateSrcNumaIdOnCurrentNode(configInfo); | ||
| 749 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 750 | -} | ||
| 751 | - | ||
| 752 | -// ==================== FaultHandler with all known event types ==================== | ||
| 753 | - | ||
| 754 | -TEST_F(TestProcessMemPidBridge, FaultHandlerPanicEvent) | ||
| 755 | -{ | ||
| 756 | - auto ret = ProcessMemPidBridge::FaultHandler(ubse::ras::ALARM_PANIC_EVENT, "NODE_PANIC"); | ||
| 757 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 758 | -} | ||
| 759 | - | ||
| 760 | -TEST_F(TestProcessMemPidBridge, FaultHandlerKernelRebootEvent) | ||
| 761 | -{ | ||
| 762 | - auto ret = ProcessMemPidBridge::FaultHandler(ubse::ras::ALARM_KERNEL_REBOOT_EVENT, "NODE_REBOOT_1"); | ||
| 763 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 764 | -} | ||
| 765 | - | ||
| 766 | -// ==================== SendSetPidMapErrorResponse all branches ==================== | ||
| 767 | - | ||
| 768 | -TEST_F(TestProcessMemPidBridge, SendSetPidMapErrorResponseAllBranches) | ||
| 769 | -{ | ||
| 770 | - // UBSE_ERR_NOT_EXIST -> "PID does not exist on current node" | ||
| 771 | - auto ret1 = SendSetPidMapErrorResponse(UBSE_ERR_NOT_EXIST, 99999); | ||
| 772 | - EXPECT_EQ(ret1, UBSE_OK); | ||
| 773 | - | ||
| 774 | - // UBSE_ERR_INVALID_ARG -> "PID start time mismatch..." | ||
| 775 | - auto ret2 = SendSetPidMapErrorResponse(UBSE_ERR_INVALID_ARG, 99999); | ||
| 776 | - EXPECT_EQ(ret2, UBSE_OK); | ||
| 777 | - | ||
| 778 | - // UBSE_ERR_RESOURCE_BUSY -> "Cannot modify srcNumaId..." | ||
| 779 | - auto ret3 = SendSetPidMapErrorResponse(UBSE_ERR_RESOURCE_BUSY, 99999); | ||
| 780 | - EXPECT_EQ(ret3, UBSE_OK); | ||
| 781 | - | ||
| 782 | - // Default -> "Set PID info failed" | ||
| 783 | - auto ret4 = SendSetPidMapErrorResponse(UBSE_ERROR, 99999); | ||
| 784 | - EXPECT_EQ(ret4, UBSE_OK); | ||
| 785 | -} | ||
| 786 | - | ||
| 787 | -// ==================== SendPidSetResponse edge cases ==================== | ||
| 788 | - | ||
| 789 | -TEST_F(TestProcessMemPidBridge, SendPidSetResponseWithLongMessage) | ||
| 790 | -{ | ||
| 791 | - auto ret = SendPidSetResponse(1, "A very long error message that exceeds normal length", 99999); | ||
| 792 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 793 | -} | ||
| 794 | - | ||
| 795 | -// ==================== MemoryReturn with empty nodeId ==================== | ||
| 796 | - | ||
| 797 | TEST_F(TestProcessMemPidBridge, MemoryReturnEmptyNodeId) | 85 | TEST_F(TestProcessMemPidBridge, MemoryReturnEmptyNodeId) |
| 798 | { | 86 | { |
| 799 | ubse::nodeController::MockSetCurrentNodeId(""); | 87 | ubse::nodeController::MockSetCurrentNodeId(""); |
| @@ -802,62 +90,6 @@ TEST_F(TestProcessMemPidBridge, MemoryReturnEmptyNodeId) | |||
| 802 | ubse::nodeController::MockResetCurrentNodeId(); | 90 | ubse::nodeController::MockResetCurrentNodeId(); |
| 803 | } | 91 | } |
| 804 | 92 | ||
| 805 | -// ==================== GetRemoteNumaSocketInfo retry tests ==================== | ||
| 806 | - | ||
| 807 | -TEST_F(TestProcessMemPidBridge, GetRemoteNumaSocketInfoRetryAndError) | ||
| 808 | -{ | ||
| 809 | - ubse::mem::controller::MockSetDebtInfoWithNodeError(UBSE_ERROR); | ||
| 810 | - | ||
| 811 | - ubse::mem::controller::UbseMemNumaDesc desc{}; | ||
| 812 | - desc.name = "retry_test"; | ||
| 813 | - desc.exportNode.slotId = 5; | ||
| 814 | - | ||
| 815 | - uint32_t socketId = 0; | ||
| 816 | - uint64_t numaId = 0; | ||
| 817 | - auto ret = ProcessMemPidBridge::GetRemoteNumaSocketInfo(desc, socketId, numaId); | ||
| 818 | - EXPECT_NE(ret, UBSE_OK); | ||
| 819 | - | ||
| 820 | - ubse::mem::controller::MockSetDebtInfoWithNodeError(UBSE_OK); | ||
| 821 | -} | ||
| 822 | - | ||
| 823 | -// ==================== NotifyBorrowNodesOnFault retry+error tests ==================== | ||
| 824 | - | ||
| 825 | -TEST_F(TestProcessMemPidBridge, NotifyBorrowNodesOnFaultRetryExhausted) | ||
| 826 | -{ | ||
| 827 | - ubse::mem::controller::MockSetDebtInfoWithNodeError(UBSE_ERROR); | ||
| 828 | - | ||
| 829 | - auto ret = ProcessMemPidBridge::NotifyBorrowNodesOnFault("FAULT_RETRY_NODE"); | ||
| 830 | - EXPECT_NE(ret, UBSE_OK); | ||
| 831 | - | ||
| 832 | - ubse::mem::controller::MockSetDebtInfoWithNodeError(UBSE_OK); | ||
| 833 | -} | ||
| 834 | - | ||
| 835 | -// ==================== SetPidInfo success path test ==================== | ||
| 836 | - | ||
| 837 | -TEST_F(TestProcessMemPidBridge, SetPidInfoSuccessPath) | ||
| 838 | -{ | ||
| 839 | - ProcessMemPidInfo pidInfo{}; | ||
| 840 | - pidInfo.configInfo.pid = getpid(); | ||
| 841 | - pidInfo.configInfo.evictThreshold = 80; | ||
| 842 | - | ||
| 843 | - ubse::serial::UbseSerialization serializer; | ||
| 844 | - pidInfo.configInfo.SerializeConfigInfo(serializer); | ||
| 845 | - | ||
| 846 | - api::server::UbseIpcMessage request{}; | ||
| 847 | - request.buffer = serializer.GetBuffer(); | ||
| 848 | - request.length = static_cast<uint32_t>(serializer.GetLength()); | ||
| 849 | - api::server::UbseRequestContext context{}; | ||
| 850 | - context.requestId = 100; | ||
| 851 | - | ||
| 852 | - auto ret = SetPidInfo(request, context); | ||
| 853 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 854 | - | ||
| 855 | - // Cleanup | ||
| 856 | - ProcessMemPidInfoManager::GetInstance().UnsetPidInfo(getpid()); | ||
| 857 | -} | ||
| 858 | - | ||
| 859 | -// ==================== MemoryReturn with delete error ==================== | ||
| 860 | - | ||
| 861 | TEST_F(TestProcessMemPidBridge, MemoryReturnDeleteError) | 93 | TEST_F(TestProcessMemPidBridge, MemoryReturnDeleteError) |
| 862 | { | 94 | { |
| 863 | ubse::mem::controller::MockSetNumaDeleteError(UBSE_ERROR); | 95 | ubse::mem::controller::MockSetNumaDeleteError(UBSE_ERROR); |
| @@ -868,110 +100,165 @@ TEST_F(TestProcessMemPidBridge, MemoryReturnDeleteError) | |||
| 868 | ubse::mem::controller::MockSetNumaDeleteError(UBSE_OK); | 100 | ubse::mem::controller::MockSetNumaDeleteError(UBSE_OK); |
| 869 | } | 101 | } |
| 870 | 102 | ||
| 871 | -// ==================== MemoryBorrow create error tests ==================== | 103 | +namespace { |
| 872 | 104 | ||
| 873 | -TEST_F(TestProcessMemPidBridge, MemoryBorrowCreateError) | 105 | +def::ProcessMemNewConfigInfo MakePidCfg(const std::string& pid, uint64_t maxMemory, double ratio) |
| 874 | { | 106 | { |
| 875 | - ProcessMemPidInfo pidInfo{}; | 107 | + def::ProcessMemNewConfigInfo cfg{}; |
| 876 | - pidInfo.configInfo.pid = 3001; | 108 | + cfg.isPid = true; |
| 877 | - pidInfo.memBorrowInfo.exportSlotId = -1; | 109 | + cfg.identifier = pid; |
| 878 | - | 110 | + cfg.maxMemory = maxMemory; |
| 879 | - ubse::mem::controller::UbseMemBorrower borrower{}; | 111 | + cfg.remoteRatio = ratio; |
| 880 | - borrower.nodeId = "NODE0"; | 112 | + return cfg; |
| 881 | - | ||
| 882 | - MemoryBorrowRequest request{}; | ||
| 883 | - request.name = "borrow_create_err"; | ||
| 884 | - request.size = 1024 * 1024; | ||
| 885 | - | ||
| 886 | - ubse::mem::controller::MockSetNumaCreateError(UBSE_ERROR); | ||
| 887 | - | ||
| 888 | - ubse::mem::controller::UbseMemNumaDesc borrowInfo{}; | ||
| 889 | - auto ret = ProcessMemPidBridge::MemoryBorrow(pidInfo, borrower, request, borrowInfo); | ||
| 890 | - EXPECT_NE(ret, UBSE_OK); | ||
| 891 | - | ||
| 892 | - ubse::mem::controller::MockSetNumaCreateError(UBSE_OK); | ||
| 893 | } | 113 | } |
| 894 | 114 | ||
| 895 | -TEST_F(TestProcessMemPidBridge, MemoryBorrowWithCandidateCreateError) | 115 | +def::ProcessMemNewConfigInfo MakeNameCfg(const std::string& name, uint64_t maxMemory, double ratio) |
| 896 | { | 116 | { |
| 897 | - ProcessMemPidInfo pidInfo{}; | 117 | + def::ProcessMemNewConfigInfo cfg{}; |
| 898 | - pidInfo.configInfo.pid = 3002; | 118 | + cfg.isPid = false; |
| 899 | - pidInfo.memBorrowInfo.exportSlotId = 5; | 119 | + cfg.identifier = name; |
| 900 | - | 120 | + cfg.maxMemory = maxMemory; |
| 901 | - ubse::mem::controller::UbseMemBorrower borrower{}; | 121 | + cfg.remoteRatio = ratio; |
| 902 | - borrower.nodeId = "NODE0"; | 122 | + return cfg; |
| 903 | - | ||
| 904 | - MemoryBorrowRequest request{}; | ||
| 905 | - request.name = "borrow_candidate_err"; | ||
| 906 | - request.size = 2048 * 1024; | ||
| 907 | - | ||
| 908 | - ubse::mem::controller::MockSetNumaCreateError(UBSE_ERROR); | ||
| 909 | - | ||
| 910 | - ubse::mem::controller::UbseMemNumaDesc borrowInfo{}; | ||
| 911 | - auto ret = ProcessMemPidBridge::MemoryBorrow(pidInfo, borrower, request, borrowInfo); | ||
| 912 | - EXPECT_NE(ret, UBSE_OK); | ||
| 913 | - | ||
| 914 | - ubse::mem::controller::MockSetNumaCreateError(UBSE_OK); | ||
| 915 | } | 123 | } |
| 916 | 124 | ||
| 917 | -// ==================== FaultHandler error paths ==================== | 125 | +} // namespace |
| 918 | 126 | ||
| 919 | -TEST_F(TestProcessMemPidBridge, FaultHandlerWithHandleNodeFaultError) | 127 | +TEST_F(TestProcessMemPidBridge, DisplayConfigPidAlone) |
| 920 | { | 128 | { |
| 921 | - // HandleNodeFaultEvent may return an error for unexpected input | 129 | + std::vector<def::ProcessMemNewConfigInfo> configs = {MakePidCfg("1001", 10ull * 1024 * 1024 * 1024, 0.5)}; |
| 922 | - auto ret = ProcessMemPidBridge::FaultHandler(ubse::ras::ALARM_PANIC_EVENT, ""); | 130 | + |
| 923 | - // The fault handler should still work, HandleNodeFaultEvent may succeed | 131 | + std::vector<def::ProcessMemDisplayEntry> entries; |
| 924 | - (void)ret; | 132 | + BuildConfigEntries(entries, configs); |
| 133 | + | ||
| 134 | + ASSERT_EQ(entries.size(), 1u); | ||
| 135 | + EXPECT_EQ(entries[0].pid, 1001); | ||
| 136 | + EXPECT_EQ(entries[0].name, "N/A"); | ||
| 137 | + EXPECT_EQ(entries[0].maxMemory, 10ull * 1024 * 1024 * 1024); | ||
| 138 | + EXPECT_DOUBLE_EQ(entries[0].remoteRatio, 0.5); | ||
| 925 | } | 139 | } |
| 926 | 140 | ||
| 927 | -// ==================== NotifyBorrowNodesOnFault with remote targets (coverage for SendProcessMemNodeFaultToNode) ==================== | 141 | +TEST_F(TestProcessMemPidBridge, DisplayConfigNameAlone) |
| 928 | - | ||
| 929 | -TEST_F(TestProcessMemPidBridge, NotifyBorrowNodesOnFaultSendsToRemote) | ||
| 930 | { | 142 | { |
| 931 | - ubse::mem::controller::UbseNumaMemoryDebtInfo debt{}; | 143 | + std::vector<def::ProcessMemNewConfigInfo> configs = {MakeNameCfg("testproc", 5ull * 1024 * 1024 * 1024, 0.3)}; |
| 932 | - debt.name = "send_to_remote"; | ||
| 933 | - debt.lentNodeId = "FAULT_SEND_NODE"; | ||
| 934 | - debt.borrowNodeId = "REMOTE_TARGET"; // != "NODE0" | ||
| 935 | 144 | ||
| 936 | - ubse::mem::controller::MockSetDebtInfos({debt}); | 145 | + std::vector<def::ProcessMemDisplayEntry> entries; |
| 146 | + BuildConfigEntries(entries, configs); | ||
| 937 | 147 | ||
| 938 | - auto ret = ProcessMemPidBridge::NotifyBorrowNodesOnFault("FAULT_SEND_NODE"); | 148 | + ASSERT_EQ(entries.size(), 1u); |
| 939 | - EXPECT_EQ(ret, UBSE_OK); | 149 | + EXPECT_EQ(entries[0].pid, 0); |
| 940 | - | 150 | + EXPECT_EQ(entries[0].name, "testproc"); |
| 941 | - ubse::mem::controller::MockClearDebtInfos(); | 151 | + EXPECT_EQ(entries[0].maxMemory, 5ull * 1024 * 1024 * 1024); |
| 152 | + EXPECT_DOUBLE_EQ(entries[0].remoteRatio, 0.3); | ||
| 942 | } | 153 | } |
| 943 | 154 | ||
| 944 | -// ==================== FaultHandler with NotifyBorrowNodesOnFault error ==================== | 155 | +TEST_F(TestProcessMemPidBridge, DisplayConfigPidAndNameSorted) |
| 945 | - | ||
| 946 | -TEST_F(TestProcessMemPidBridge, FaultHandlerNotifyBorrowNodesFailed) | ||
| 947 | { | 156 | { |
| 948 | - ubse::mem::controller::MockSetDebtInfoWithNodeError(UBSE_ERROR); | 157 | + std::vector<def::ProcessMemNewConfigInfo> configs = {MakePidCfg("2002", 8ull * 1024 * 1024 * 1024, 0.4), |
| 158 | + MakeNameCfg("testproc", 5ull * 1024 * 1024 * 1024, 0.3), | ||
| 159 | + MakePidCfg("1001", 10ull * 1024 * 1024 * 1024, 0.5)}; | ||
| 949 | 160 | ||
| 950 | - auto ret = ProcessMemPidBridge::FaultHandler(ubse::ras::ALARM_PANIC_EVENT, "FAULT_NOTIFY_FAIL"); | 161 | + std::vector<def::ProcessMemDisplayEntry> entries; |
| 951 | - EXPECT_NE(ret, UBSE_OK); | 162 | + BuildConfigEntries(entries, configs); |
| 952 | 163 | ||
| 953 | - ubse::mem::controller::MockSetDebtInfoWithNodeError(UBSE_OK); | 164 | + ASSERT_EQ(entries.size(), 3u); |
| 165 | + EXPECT_EQ(entries[0].pid, 1001); | ||
| 166 | + EXPECT_EQ(entries[0].name, "N/A"); | ||
| 167 | + EXPECT_EQ(entries[1].pid, 2002); | ||
| 168 | + EXPECT_EQ(entries[1].name, "N/A"); | ||
| 169 | + EXPECT_EQ(entries[2].pid, 0); | ||
| 170 | + EXPECT_EQ(entries[2].name, "testproc"); | ||
| 954 | } | 171 | } |
| 955 | 172 | ||
| 956 | -// ==================== SetPidInfo with srcNumaId not found ==================== | 173 | +TEST_F(TestProcessMemPidBridge, DisplayDetailNameManagedEntries) |
| 957 | - | ||
| 958 | -TEST_F(TestProcessMemPidBridge, SetPidInfoSrcNumaIdNotFound) | ||
| 959 | { | 174 | { |
| 960 | - ProcessMemPidConfigInfo configInfo{}; | 175 | + std::map<pid_t, def::ManagedPidEntry> snapshot; |
| 961 | - configInfo.pid = getpid(); | 176 | + def::ManagedPidEntry e1{}; |
| 962 | - configInfo.evictThreshold = 80; | 177 | + e1.pid = 1001; |
| 963 | - configInfo.srcNumaId = 42; // Non-matching numaId | 178 | + e1.nameConfigName = "testproc"; |
| 179 | + e1.maxMemory = 5ull * 1024 * 1024 * 1024; | ||
| 180 | + e1.remoteRatio = 0.3; | ||
| 181 | + def::ManagedPidEntry e2{}; | ||
| 182 | + e2.pid = 2002; | ||
| 183 | + e2.nameConfigName = "testproc"; | ||
| 184 | + e2.maxMemory = 5ull * 1024 * 1024 * 1024; | ||
| 185 | + e2.remoteRatio = 0.3; | ||
| 186 | + snapshot[1001] = e1; | ||
| 187 | + snapshot[2002] = e2; | ||
| 964 | 188 | ||
| 965 | - ubse::serial::UbseSerialization serializer; | 189 | + std::vector<def::ProcessMemDisplayEntry> entries; |
| 966 | - configInfo.SerializeConfigInfo(serializer); | 190 | + BuildProcDetailEntries(entries, snapshot); |
| 967 | 191 | ||
| 968 | - api::server::UbseIpcMessage request{}; | 192 | + ASSERT_EQ(entries.size(), 2u); |
| 969 | - request.buffer = serializer.GetBuffer(); | 193 | + EXPECT_EQ(entries[0].pid, 1001); |
| 970 | - request.length = static_cast<uint32_t>(serializer.GetLength()); | 194 | + EXPECT_EQ(entries[0].name, "testproc"); |
| 971 | - api::server::UbseRequestContext context{}; | 195 | + EXPECT_EQ(entries[0].maxMemory, 5ull * 1024 * 1024 * 1024); |
| 972 | - context.requestId = 101; | 196 | + EXPECT_DOUBLE_EQ(entries[0].remoteRatio, 0.3); |
| 197 | + EXPECT_EQ(entries[1].pid, 2002); | ||
| 198 | + EXPECT_EQ(entries[1].name, "testproc"); | ||
| 199 | + EXPECT_EQ(entries[1].maxMemory, 5ull * 1024 * 1024 * 1024); | ||
| 200 | +} | ||
| 973 | 201 | ||
| 974 | - auto ret = SetPidInfo(request, context); | 202 | +TEST_F(TestProcessMemPidBridge, DisplayDetailNoManagedEntries) |
| 975 | - EXPECT_EQ(ret, UBSE_OK); // SendPidSetResponse returns UBSE_OK even for error responses | 203 | +{ |
| 204 | + std::map<pid_t, def::ManagedPidEntry> snapshot; | ||
| 205 | + | ||
| 206 | + std::vector<def::ProcessMemDisplayEntry> entries; | ||
| 207 | + BuildProcDetailEntries(entries, snapshot); | ||
| 208 | + | ||
| 209 | + EXPECT_TRUE(entries.empty()); | ||
| 210 | +} | ||
| 211 | + | ||
| 212 | +TEST_F(TestProcessMemPidBridge, DisplayDetailPidEntryShowsNA) | ||
| 213 | +{ | ||
| 214 | + std::map<pid_t, def::ManagedPidEntry> snapshot; | ||
| 215 | + def::ManagedPidEntry e{}; | ||
| 216 | + e.pid = 2002; | ||
| 217 | + e.maxMemory = 10ull * 1024 * 1024 * 1024; | ||
| 218 | + e.remoteRatio = 0.5; | ||
| 219 | + snapshot[2002] = e; | ||
| 220 | + | ||
| 221 | + std::vector<def::ProcessMemDisplayEntry> entries; | ||
| 222 | + BuildProcDetailEntries(entries, snapshot); | ||
| 223 | + | ||
| 224 | + ASSERT_EQ(entries.size(), 1u); | ||
| 225 | + EXPECT_EQ(entries[0].pid, 2002); | ||
| 226 | + EXPECT_EQ(entries[0].name, "N/A"); | ||
| 227 | + EXPECT_EQ(entries[0].maxMemory, 10ull * 1024 * 1024 * 1024); | ||
| 228 | + EXPECT_DOUBLE_EQ(entries[0].remoteRatio, 0.5); | ||
| 229 | +} | ||
| 230 | + | ||
| 231 | +TEST_F(TestProcessMemPidBridge, DisplayDetailMixedNameAndPidEntries) | ||
| 232 | +{ | ||
| 233 | + std::map<pid_t, def::ManagedPidEntry> snapshot; | ||
| 234 | + def::ManagedPidEntry e1{}; | ||
| 235 | + e1.pid = 1001; | ||
| 236 | + e1.sources = static_cast<uint8_t>(def::ConfigSource::NAME_CONFIG); | ||
| 237 | + e1.nameConfigName = "testproc"; | ||
| 238 | + e1.maxMemory = 5ull * 1024 * 1024 * 1024; | ||
| 239 | + e1.remoteRatio = 0.3; | ||
| 240 | + def::ManagedPidEntry e2{}; | ||
| 241 | + e2.pid = 2002; | ||
| 242 | + // name+pid 同时命中: pid 配置优先, name 强制 N/A, 值取 pid 配置 | ||
| 243 | + e2.sources = static_cast<uint8_t>(def::ConfigSource::PID_CONFIG) | | ||
| 244 | + static_cast<uint8_t>(def::ConfigSource::NAME_CONFIG); | ||
| 245 | + e2.nameConfigName = "testproc"; | ||
| 246 | + e2.maxMemory = 10ull * 1024 * 1024 * 1024; | ||
| 247 | + e2.remoteRatio = 0.5; | ||
| 248 | + snapshot[1001] = e1; | ||
| 249 | + snapshot[2002] = e2; | ||
| 250 | + | ||
| 251 | + std::vector<def::ProcessMemDisplayEntry> entries; | ||
| 252 | + BuildProcDetailEntries(entries, snapshot); | ||
| 253 | + | ||
| 254 | + ASSERT_EQ(entries.size(), 2u); | ||
| 255 | + EXPECT_EQ(entries[0].pid, 1001); | ||
| 256 | + EXPECT_EQ(entries[0].name, "testproc"); | ||
| 257 | + EXPECT_EQ(entries[0].maxMemory, 5ull * 1024 * 1024 * 1024); | ||
| 258 | + EXPECT_DOUBLE_EQ(entries[0].remoteRatio, 0.3); | ||
| 259 | + EXPECT_EQ(entries[1].pid, 2002); | ||
| 260 | + EXPECT_EQ(entries[1].name, "N/A"); | ||
| 261 | + EXPECT_EQ(entries[1].maxMemory, 10ull * 1024 * 1024 * 1024); | ||
| 262 | + EXPECT_DOUBLE_EQ(entries[1].remoteRatio, 0.5); | ||
| 976 | } | 263 | } |
| 977 | } // namespace ubse::ut::process_mem | 264 | } // namespace ubse::ut::process_mem |
| @@ -12,9 +12,11 @@ | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 15 | 18 | ||
| 16 | 19 | ||
| 17 | -// Forward declare functions from process_mem_pid_collect.cpp for testing | ||
| 18 | namespace process_mem::collect { | 20 | namespace process_mem::collect { |
| 19 | size_t GetPageSize(); | 21 | size_t GetPageSize(); |
| 20 | bool GetNumaInfoFromToken(const std::string& token, const size_t pageSize, | 22 | bool GetNumaInfoFromToken(const std::string& token, const size_t pageSize, |
| @@ -22,138 +24,94 @@ bool GetNumaInfoFromToken(const std::string& token, const size_t pageSize, | |||
| 22 | void ParseLine(const std::string& line, std::unordered_map<uint32_t, size_t>& numaMemDistribution); | 24 | void ParseLine(const std::string& line, std::unordered_map<uint32_t, size_t>& numaMemDistribution); |
| 23 | std::vector<pid_t> GetChildrenPidsFallback(pid_t parentPid); | 25 | std::vector<pid_t> GetChildrenPidsFallback(pid_t parentPid); |
| 24 | std::vector<pid_t> GetChildrenPids(pid_t parentPid); | 26 | std::vector<pid_t> GetChildrenPids(pid_t parentPid); |
| 25 | -uint32_t GetPidInfoByCollect(process_mem::def::ProcessMemPidInfo& pidInfo, CollectInfoMap& pidCollectInfo); | ||
| 26 | } // namespace process_mem::collect | 27 | } // namespace process_mem::collect |
| 27 | 28 | ||
| 28 | namespace ubse::ut::process_mem { | 29 | namespace ubse::ut::process_mem { |
| 29 | using namespace ::process_mem::collect; | 30 | using namespace ::process_mem::collect; |
| 30 | -using namespace ::process_mem::def; | ||
| 31 | using namespace ::process_mem::manager; | 31 | using namespace ::process_mem::manager; |
| 32 | +namespace def = ::process_mem::def; | ||
| 32 | 33 | ||
| 33 | void TestProcessMemPidCollect::SetUp() {} | 34 | void TestProcessMemPidCollect::SetUp() {} |
| 34 | 35 | ||
| 35 | void TestProcessMemPidCollect::TearDown() {} | 36 | void TestProcessMemPidCollect::TearDown() {} |
| 36 | 37 | ||
| 37 | -// ==================== GetPageSize tests ==================== | ||
| 38 | - | ||
| 39 | TEST_F(TestProcessMemPidCollect, GetPageSizeReturnsPositive) | 38 | TEST_F(TestProcessMemPidCollect, GetPageSizeReturnsPositive) |
| 40 | { | 39 | { |
| 41 | size_t pageSize = GetPageSize(); | 40 | size_t pageSize = GetPageSize(); |
| 42 | EXPECT_GT(pageSize, 0u); | 41 | EXPECT_GT(pageSize, 0u); |
| 43 | } | 42 | } |
| 44 | 43 | ||
| 45 | -// ==================== GetNumaInfoFromToken tests ==================== | 44 | +struct NumaTokenCase { |
| 45 | + std::string token; | ||
| 46 | + size_t pageSize; | ||
| 47 | + bool expectEmpty; | ||
| 48 | + uint32_t expectNuma{0}; | ||
| 49 | + uint64_t expectBytes{0}; | ||
| 50 | + std::string secondToken; | ||
| 51 | +}; | ||
| 46 | 52 | ||
| 47 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenValid) | 53 | +struct NumaLineCase { |
| 54 | + std::string line; | ||
| 55 | + std::vector<std::pair<uint32_t, uint64_t>> expect; | ||
| 56 | +}; | ||
| 57 | + | ||
| 58 | +class TestProcessMemPidCollectNumaToken | ||
| 59 | + : public TestProcessMemPidCollect | ||
| 60 | + , public ::testing::WithParamInterface<NumaTokenCase> { | ||
| 61 | +}; | ||
| 62 | +class TestProcessMemPidCollectParseLine | ||
| 63 | + : public TestProcessMemPidCollect | ||
| 64 | + , public ::testing::WithParamInterface<NumaLineCase> { | ||
| 65 | +}; | ||
| 66 | + | ||
| 67 | +TEST_P(TestProcessMemPidCollectNumaToken, GetNumaInfoFromToken) | ||
| 48 | { | 68 | { |
| 69 | + const auto& tc = GetParam(); | ||
| 49 | std::unordered_map<uint32_t, size_t> numaMemDistribution; | 70 | std::unordered_map<uint32_t, size_t> numaMemDistribution; |
| 50 | - bool result = GetNumaInfoFromToken("N0=100", 4096, numaMemDistribution); | 71 | + bool result = GetNumaInfoFromToken(tc.token, tc.pageSize, numaMemDistribution); |
| 51 | EXPECT_TRUE(result); | 72 | EXPECT_TRUE(result); |
| 52 | - EXPECT_EQ(numaMemDistribution[0], 100u * 4096); | 73 | + if (!tc.secondToken.empty()) { |
| 74 | + EXPECT_TRUE(GetNumaInfoFromToken(tc.secondToken, tc.pageSize, numaMemDistribution)); | ||
| 75 | + } | ||
| 76 | + if (tc.expectEmpty) { | ||
| 77 | + EXPECT_TRUE(numaMemDistribution.empty()); | ||
| 78 | + } else { | ||
| 79 | + EXPECT_EQ(numaMemDistribution[tc.expectNuma], tc.expectBytes); | ||
| 80 | + } | ||
| 53 | } | 81 | } |
| 54 | 82 | ||
| 55 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenValidMultiplePages) | 83 | +INSTANTIATE_TEST_SUITE_P( |
| 56 | -{ | 84 | + NumaTokenCases, TestProcessMemPidCollectNumaToken, |
| 57 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | 85 | + testing::Values(NumaTokenCase{"N0=100", 4096, false, 0, 100u * 4096, ""}, |
| 58 | - bool result = GetNumaInfoFromToken("N3=500", 1024, numaMemDistribution); | 86 | + NumaTokenCase{"N3=500", 1024, false, 3, 500u * 1024, ""}, |
| 59 | - EXPECT_TRUE(result); | 87 | + NumaTokenCase{"N12=256", 4096, false, 12, 256u * 4096, ""}, |
| 60 | - EXPECT_EQ(numaMemDistribution[3], 500u * 1024); | 88 | + NumaTokenCase{"kernel=100", 4096, true, 0, 0, ""}, NumaTokenCase{"N", 4096, true, 0, 0, ""}, |
| 61 | -} | 89 | + NumaTokenCase{"NX=100", 4096, true, 0, 0, ""}, NumaTokenCase{"", 4096, true, 0, 0, ""}, |
| 90 | + NumaTokenCase{"N0=100", 4096, false, 0, 150u * 4096, "N0=50"}, | ||
| 91 | + NumaTokenCase{"N0x100", 4096, true, 0, 0, ""}, NumaTokenCase{"N0=0", 4096, false, 0, 0u, ""}, | ||
| 92 | + NumaTokenCase{"N-1=100", 4096, true, 0, 0, ""}, NumaTokenCase{" N0=100", 4096, true, 0, 0, ""}, | ||
| 93 | + NumaTokenCase{"N999=123", 1024, false, 999, 123u * 1024, ""}, | ||
| 94 | + NumaTokenCase{"N0=100=200", 4096, false, 0, 100u * 4096, ""})); | ||
| 62 | 95 | ||
| 63 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenMultiDigitNuma) | 96 | +TEST_P(TestProcessMemPidCollectParseLine, ParseLine) |
| 64 | { | 97 | { |
| 98 | + const auto& tc = GetParam(); | ||
| 65 | std::unordered_map<uint32_t, size_t> numaMemDistribution; | 99 | std::unordered_map<uint32_t, size_t> numaMemDistribution; |
| 66 | - bool result = GetNumaInfoFromToken("N12=256", 4096, numaMemDistribution); | 100 | + EXPECT_NO_THROW(ParseLine(tc.line, numaMemDistribution)); |
| 67 | - EXPECT_TRUE(result); | ||
| 68 | - EXPECT_EQ(numaMemDistribution[12], 256u * 4096); | ||
| 69 | -} | ||
| 70 | - | ||
| 71 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenNotNumaToken) | ||
| 72 | -{ | ||
| 73 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 74 | - bool result = GetNumaInfoFromToken("kernel=100", 4096, numaMemDistribution); | ||
| 75 | - EXPECT_TRUE(result); | ||
| 76 | - EXPECT_TRUE(numaMemDistribution.empty()); | ||
| 77 | -} | ||
| 78 | - | ||
| 79 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenTooShort) | ||
| 80 | -{ | ||
| 81 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 82 | - bool result = GetNumaInfoFromToken("N", 4096, numaMemDistribution); | ||
| 83 | - EXPECT_TRUE(result); | ||
| 84 | - EXPECT_TRUE(numaMemDistribution.empty()); | ||
| 85 | -} | ||
| 86 | - | ||
| 87 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenNoNumaAfterPrefix) | ||
| 88 | -{ | ||
| 89 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 90 | - bool result = GetNumaInfoFromToken("NX=100", 4096, numaMemDistribution); | ||
| 91 | - EXPECT_TRUE(result); | ||
| 92 | - EXPECT_TRUE(numaMemDistribution.empty()); | ||
| 93 | -} | ||
| 94 | - | ||
| 95 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenEmpty) | ||
| 96 | -{ | ||
| 97 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 98 | - bool result = GetNumaInfoFromToken("", 4096, numaMemDistribution); | ||
| 99 | - EXPECT_TRUE(result); | ||
| 100 | - EXPECT_TRUE(numaMemDistribution.empty()); | ||
| 101 | -} | ||
| 102 | - | ||
| 103 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenAccumulates) | ||
| 104 | -{ | ||
| 105 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 106 | - // First token | ||
| 107 | - GetNumaInfoFromToken("N0=100", 4096, numaMemDistribution); | ||
| 108 | - // Second token with same numa adds to existing | ||
| 109 | - GetNumaInfoFromToken("N0=50", 4096, numaMemDistribution); | ||
| 110 | - EXPECT_EQ(numaMemDistribution[0], 150u * 4096); | ||
| 111 | -} | ||
| 112 | - | ||
| 113 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenNoEquals) | ||
| 114 | -{ | ||
| 115 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 116 | - bool result = GetNumaInfoFromToken("N0x100", 4096, numaMemDistribution); | ||
| 117 | - EXPECT_TRUE(result); | ||
| 118 | - EXPECT_TRUE(numaMemDistribution.empty()); | ||
| 119 | -} | ||
| 120 | - | ||
| 121 | -// ==================== ParseLine tests ==================== | ||
| 122 | - | ||
| 123 | -TEST_F(TestProcessMemPidCollect, ParseLineSingleNumaToken) | ||
| 124 | -{ | ||
| 125 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 126 | - ParseLine("N0=100", numaMemDistribution); | ||
| 127 | size_t pageSize = GetPageSize(); | 101 | size_t pageSize = GetPageSize(); |
| 128 | - EXPECT_EQ(numaMemDistribution[0], 100u * pageSize); | 102 | + ASSERT_EQ(numaMemDistribution.size(), tc.expect.size()); |
| 103 | + for (const auto& [numa, pages] : tc.expect) { | ||
| 104 | + EXPECT_EQ(numaMemDistribution[numa], pages * pageSize); | ||
| 105 | + } | ||
| 129 | } | 106 | } |
| 130 | 107 | ||
| 131 | -TEST_F(TestProcessMemPidCollect, ParseLineMultipleNumaTokens) | 108 | +INSTANTIATE_TEST_SUITE_P(ParseLineCases, TestProcessMemPidCollectParseLine, |
| 132 | -{ | 109 | + testing::Values(NumaLineCase{"N0=100", {{0, 100}}}, |
| 133 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | 110 | + NumaLineCase{"N0=100 N1=200 N2=50", {{0, 100}, {1, 200}, {2, 50}}}, |
| 134 | - ParseLine("N0=100 N1=200 N2=50", numaMemDistribution); | 111 | + NumaLineCase{"N0=100 kernel=50 N1=200", {{0, 100}, {1, 200}}}, |
| 135 | - size_t pageSize = GetPageSize(); | 112 | + NumaLineCase{"", {}}, |
| 136 | - EXPECT_EQ(numaMemDistribution[0], 100u * pageSize); | 113 | + NumaLineCase{" N0=100 N1=200 ", {{0, 100}, {1, 200}}}, |
| 137 | - EXPECT_EQ(numaMemDistribution[1], 200u * pageSize); | 114 | + NumaLineCase{"N0=100\nN1=200", {{0, 100}, {1, 200}}})); |
| 138 | - EXPECT_EQ(numaMemDistribution[2], 50u * pageSize); | ||
| 139 | -} | ||
| 140 | - | ||
| 141 | -TEST_F(TestProcessMemPidCollect, ParseLineMixedTokens) | ||
| 142 | -{ | ||
| 143 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 144 | - ParseLine("N0=100 kernel=50 N1=200", numaMemDistribution); | ||
| 145 | - EXPECT_GT(numaMemDistribution[0], 0u); | ||
| 146 | - EXPECT_GT(numaMemDistribution[1], 0u); | ||
| 147 | -} | ||
| 148 | - | ||
| 149 | -TEST_F(TestProcessMemPidCollect, ParseLineEmpty) | ||
| 150 | -{ | ||
| 151 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 152 | - EXPECT_NO_THROW(ParseLine("", numaMemDistribution)); | ||
| 153 | - EXPECT_TRUE(numaMemDistribution.empty()); | ||
| 154 | -} | ||
| 155 | - | ||
| 156 | -// ==================== GetChildrenPids tests ==================== | ||
| 157 | 115 | ||
| 158 | TEST_F(TestProcessMemPidCollect, GetChildrenPidsFallbackReturnsVector) | 116 | TEST_F(TestProcessMemPidCollect, GetChildrenPidsFallbackReturnsVector) |
| 159 | { | 117 | { |
| @@ -167,59 +125,6 @@ TEST_F(TestProcessMemPidCollect, GetChildrenPidsReturnsVector) | |||
| 167 | EXPECT_TRUE(children.empty() || !children.empty()); | 125 | EXPECT_TRUE(children.empty() || !children.empty()); |
| 168 | } | 126 | } |
| 169 | 127 | ||
| 170 | -// ==================== GetPidInfoByCollect tests ==================== | ||
| 171 | - | ||
| 172 | -TEST_F(TestProcessMemPidCollect, GetPidInfoByCollectNonExistentPid) | ||
| 173 | -{ | ||
| 174 | - ProcessMemPidInfo pidInfo{}; | ||
| 175 | - pidInfo.configInfo.pid = 99999999; | ||
| 176 | - CollectInfoMap pidCollectInfo; | ||
| 177 | - auto ret = GetPidInfoByCollect(pidInfo, pidCollectInfo); | ||
| 178 | - EXPECT_NE(ret, UBSE_OK); | ||
| 179 | - EXPECT_EQ(pidInfo.processStatus, ProcessStatus::INACTIVE); | ||
| 180 | -} | ||
| 181 | - | ||
| 182 | -TEST_F(TestProcessMemPidCollect, RegisterCollectHandlerSuccess) | ||
| 183 | -{ | ||
| 184 | - auto& collector = ProcessMemPidCollect::GetInstance(); | ||
| 185 | - NumaMemDistributionCollectHandler handler = [](CollectInfoMap) { | ||
| 186 | - }; | ||
| 187 | - auto ret = collector.RegisterCollectHandler("test_handler", handler); | ||
| 188 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 189 | - collector.UnRegisterCollectHandler("test_handler"); | ||
| 190 | -} | ||
| 191 | - | ||
| 192 | -TEST_F(TestProcessMemPidCollect, RegisterCollectHandlerNullHandler) | ||
| 193 | -{ | ||
| 194 | - auto& collector = ProcessMemPidCollect::GetInstance(); | ||
| 195 | - NumaMemDistributionCollectHandler nullHandler; | ||
| 196 | - auto ret = collector.RegisterCollectHandler("null_handler", nullHandler); | ||
| 197 | - EXPECT_EQ(ret, UBSE_ERROR); | ||
| 198 | -} | ||
| 199 | - | ||
| 200 | -TEST_F(TestProcessMemPidCollect, RegisterCollectHandlerOverwrite) | ||
| 201 | -{ | ||
| 202 | - auto& collector = ProcessMemPidCollect::GetInstance(); | ||
| 203 | - NumaMemDistributionCollectHandler handler1 = [](CollectInfoMap) { | ||
| 204 | - }; | ||
| 205 | - NumaMemDistributionCollectHandler handler2 = [](CollectInfoMap) { | ||
| 206 | - }; | ||
| 207 | - | ||
| 208 | - auto ret = collector.RegisterCollectHandler("overwrite_handler", handler1); | ||
| 209 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 210 | - | ||
| 211 | - ret = collector.RegisterCollectHandler("overwrite_handler", handler2); | ||
| 212 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 213 | - | ||
| 214 | - collector.UnRegisterCollectHandler("overwrite_handler"); | ||
| 215 | -} | ||
| 216 | - | ||
| 217 | -TEST_F(TestProcessMemPidCollect, UnRegisterCollectHandlerNotExist) | ||
| 218 | -{ | ||
| 219 | - auto& collector = ProcessMemPidCollect::GetInstance(); | ||
| 220 | - EXPECT_NO_THROW(collector.UnRegisterCollectHandler("non_existent_handler")); | ||
| 221 | -} | ||
| 222 | - | ||
| 223 | TEST_F(TestProcessMemPidCollect, CollectProcessNumaMemDistributionInvalidPid) | 128 | TEST_F(TestProcessMemPidCollect, CollectProcessNumaMemDistributionInvalidPid) |
| 224 | { | 129 | { |
| 225 | auto& collector = ProcessMemPidCollect::GetInstance(); | 130 | auto& collector = ProcessMemPidCollect::GetInstance(); |
| @@ -236,114 +141,11 @@ TEST_F(TestProcessMemPidCollect, CollectProcessNumaMemDistributionNonExistentPid | |||
| 236 | EXPECT_NE(ret, UBSE_OK); | 141 | EXPECT_NE(ret, UBSE_OK); |
| 237 | } | 142 | } |
| 238 | 143 | ||
| 239 | -TEST_F(TestProcessMemPidCollect, RegisterMultipleHandlers) | ||
| 240 | -{ | ||
| 241 | - auto& collector = ProcessMemPidCollect::GetInstance(); | ||
| 242 | - NumaMemDistributionCollectHandler handler1 = [](CollectInfoMap) { | ||
| 243 | - }; | ||
| 244 | - NumaMemDistributionCollectHandler handler2 = [](CollectInfoMap) { | ||
| 245 | - }; | ||
| 246 | - | ||
| 247 | - auto ret1 = collector.RegisterCollectHandler("multi_handler_1", handler1); | ||
| 248 | - auto ret2 = collector.RegisterCollectHandler("multi_handler_2", handler2); | ||
| 249 | - EXPECT_EQ(ret1, UBSE_OK); | ||
| 250 | - EXPECT_EQ(ret2, UBSE_OK); | ||
| 251 | - | ||
| 252 | - collector.UnRegisterCollectHandler("multi_handler_1"); | ||
| 253 | - collector.UnRegisterCollectHandler("multi_handler_2"); | ||
| 254 | -} | ||
| 255 | - | ||
| 256 | -TEST_F(TestProcessMemPidCollect, UnRegisterAndReRegister) | ||
| 257 | -{ | ||
| 258 | - auto& collector = ProcessMemPidCollect::GetInstance(); | ||
| 259 | - NumaMemDistributionCollectHandler handler = [](CollectInfoMap) { | ||
| 260 | - }; | ||
| 261 | - | ||
| 262 | - auto ret = collector.RegisterCollectHandler("reregister_handler", handler); | ||
| 263 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 264 | - | ||
| 265 | - collector.UnRegisterCollectHandler("reregister_handler"); | ||
| 266 | - | ||
| 267 | - ret = collector.RegisterCollectHandler("reregister_handler", handler); | ||
| 268 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 269 | - | ||
| 270 | - collector.UnRegisterCollectHandler("reregister_handler"); | ||
| 271 | -} | ||
| 272 | - | ||
| 273 | -// ==================== Additional edge case tests ==================== | ||
| 274 | - | ||
| 275 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenZeroPages) | ||
| 276 | -{ | ||
| 277 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 278 | - bool result = GetNumaInfoFromToken("N0=0", 4096, numaMemDistribution); | ||
| 279 | - EXPECT_TRUE(result); | ||
| 280 | - EXPECT_EQ(numaMemDistribution[0], 0u); | ||
| 281 | -} | ||
| 282 | - | ||
| 283 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenNegativeNuma) | ||
| 284 | -{ | ||
| 285 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 286 | - bool result = GetNumaInfoFromToken("N-1=100", 4096, numaMemDistribution); | ||
| 287 | - EXPECT_TRUE(result); | ||
| 288 | - EXPECT_TRUE(numaMemDistribution.empty()); | ||
| 289 | -} | ||
| 290 | - | ||
| 291 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenLeadingSpaces) | ||
| 292 | -{ | ||
| 293 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 294 | - bool result = GetNumaInfoFromToken(" N0=100", 4096, numaMemDistribution); | ||
| 295 | - EXPECT_TRUE(result); | ||
| 296 | - EXPECT_TRUE(numaMemDistribution.empty()); | ||
| 297 | -} | ||
| 298 | - | ||
| 299 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenWithMaxNuma) | ||
| 300 | -{ | ||
| 301 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 302 | - bool result = GetNumaInfoFromToken("N999=123", 1024, numaMemDistribution); | ||
| 303 | - EXPECT_TRUE(result); | ||
| 304 | - EXPECT_EQ(numaMemDistribution[999], 123u * 1024); | ||
| 305 | -} | ||
| 306 | - | ||
| 307 | -TEST_F(TestProcessMemPidCollect, GetNumaInfoFromTokenMultipleEquals) | ||
| 308 | -{ | ||
| 309 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 310 | - bool result = GetNumaInfoFromToken("N0=100=200", 4096, numaMemDistribution); | ||
| 311 | - EXPECT_TRUE(result); | ||
| 312 | - // strtoul stops at non-digit, so after = we get "100=200" -> strtoul gets 100 | ||
| 313 | -} | ||
| 314 | - | ||
| 315 | -TEST_F(TestProcessMemPidCollect, ParseLineWithEmptyTokens) | ||
| 316 | -{ | ||
| 317 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 318 | - ParseLine(" N0=100 N1=200 ", numaMemDistribution); | ||
| 319 | - size_t pageSize = GetPageSize(); | ||
| 320 | - EXPECT_EQ(numaMemDistribution[0], 100u * pageSize); | ||
| 321 | - EXPECT_EQ(numaMemDistribution[1], 200u * pageSize); | ||
| 322 | -} | ||
| 323 | - | ||
| 324 | -TEST_F(TestProcessMemPidCollect, ParseLineNewlineHandling) | ||
| 325 | -{ | ||
| 326 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | ||
| 327 | - ParseLine("N0=100\nN1=200", numaMemDistribution); | ||
| 328 | - size_t pageSize = GetPageSize(); | ||
| 329 | - EXPECT_EQ(numaMemDistribution[0], 100u * pageSize); | ||
| 330 | -} | ||
| 331 | - | ||
| 332 | TEST_F(TestProcessMemPidCollect, CollectProcessNumaMemDistributionInitPid) | 144 | TEST_F(TestProcessMemPidCollect, CollectProcessNumaMemDistributionInitPid) |
| 333 | { | 145 | { |
| 334 | auto& collector = ProcessMemPidCollect::GetInstance(); | 146 | auto& collector = ProcessMemPidCollect::GetInstance(); |
| 335 | std::unordered_map<uint32_t, size_t> numaMemDistribution; | 147 | std::unordered_map<uint32_t, size_t> numaMemDistribution; |
| 336 | auto ret = collector.CollectProcessNumaMemDistribution(1, numaMemDistribution); | 148 | auto ret = collector.CollectProcessNumaMemDistribution(1, numaMemDistribution); |
| 337 | - // init process (pid 1) should exist | ||
| 338 | - (void)ret; | ||
| 339 | -} | ||
| 340 | - | ||
| 341 | -TEST_F(TestProcessMemPidCollect, GetPidInfoByCollectInitPid) | ||
| 342 | -{ | ||
| 343 | - ProcessMemPidInfo pidInfo{}; | ||
| 344 | - pidInfo.configInfo.pid = 1; | ||
| 345 | - CollectInfoMap pidCollectInfo; | ||
| 346 | - auto ret = GetPidInfoByCollect(pidInfo, pidCollectInfo); | ||
| 347 | (void)ret; | 149 | (void)ret; |
| 348 | } | 150 | } |
| 349 | 151 | ||
| @@ -359,24 +161,63 @@ TEST_F(TestProcessMemPidCollect, GetChildrenPidsFallbackForKnownPid) | |||
| 359 | EXPECT_TRUE(children.empty() || !children.empty()); | 161 | EXPECT_TRUE(children.empty() || !children.empty()); |
| 360 | } | 162 | } |
| 361 | 163 | ||
| 362 | -TEST_F(TestProcessMemPidCollect, RegisterNullHandlerByName) | 164 | +std::string ReadComm(pid_t pid) |
| 363 | { | 165 | { |
| 364 | - auto& collector = ProcessMemPidCollect::GetInstance(); | 166 | + std::ifstream commFile("/proc/" + std::to_string(pid) + "/comm"); |
| 365 | - NumaMemDistributionCollectHandler nullHandler; | 167 | + std::string comm; |
| 366 | - auto ret = collector.RegisterCollectHandler("test_null_handler", nullHandler); | 168 | + std::getline(commFile, comm); |
| 367 | - EXPECT_EQ(ret, UBSE_ERROR); | 169 | + return comm; |
| 368 | } | 170 | } |
| 369 | 171 | ||
| 370 | -TEST_F(TestProcessMemPidCollect, UnRegisterThenCallCollect) | 172 | +TEST_F(TestProcessMemPidCollect, HandleNewPidsByNameSkipsRootProcess) |
| 371 | { | 173 | { |
| 372 | - auto& collector = ProcessMemPidCollect::GetInstance(); | 174 | + ASSERT_EQ(GetProcUid(1), 0); |
| 373 | - NumaMemDistributionCollectHandler handler = [](CollectInfoMap) { | ||
| 374 | - }; | ||
| 375 | - collector.RegisterCollectHandler("tmp_handler", handler); | ||
| 376 | - collector.UnRegisterCollectHandler("tmp_handler"); | ||
| 377 | 175 | ||
| 378 | - std::unordered_map<uint32_t, size_t> numaMemDistribution; | 176 | + auto& mgr = ProcessMemPidInfoManager::GetInstance(); |
| 379 | - auto ret = collector.CollectProcessNumaMemDistribution(1, numaMemDistribution); | 177 | + std::string comm = ReadComm(1); |
| 380 | - (void)ret; | 178 | + ASSERT_FALSE(comm.empty()); |
| 179 | + | ||
| 180 | + def::ProcessMemNewConfigInfo config{}; | ||
| 181 | + config.isPid = false; | ||
| 182 | + config.identifier = comm; | ||
| 183 | + config.maxMemory = 1073741824; | ||
| 184 | + config.remoteRatio = 0.5; | ||
| 185 | + ASSERT_EQ(mgr.SetProcMemConfig(config), UBSE_OK); | ||
| 186 | + | ||
| 187 | + ProcessMemPidCollect::GetInstance().HandleNewPidsByName({1}, 1); | ||
| 188 | + | ||
| 189 | + auto snapshot = mgr.GetManagedPidCacheSnapshot(); | ||
| 190 | + EXPECT_EQ(snapshot.find(1), snapshot.end()); | ||
| 191 | + EXPECT_EQ(mgr.GetProcMemConfig(true, "1").maxMemory, 0u); | ||
| 192 | + | ||
| 193 | + EXPECT_EQ(mgr.RemoveProcMemConfig(false, comm), UBSE_OK); | ||
| 194 | +} | ||
| 195 | + | ||
| 196 | +TEST_F(TestProcessMemPidCollect, HandleNewPidsByNameManagesNonRootProcess) | ||
| 197 | +{ | ||
| 198 | + if (getuid() == 0) { | ||
| 199 | + GTEST_SKIP() << "本进程为 root,无法作为非 root 正例"; | ||
| 200 | + } | ||
| 201 | + auto& mgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 202 | + std::string comm = ReadComm(getpid()); | ||
| 203 | + ASSERT_FALSE(comm.empty()); | ||
| 204 | + | ||
| 205 | + def::ProcessMemNewConfigInfo config{}; | ||
| 206 | + config.isPid = false; | ||
| 207 | + config.identifier = comm; | ||
| 208 | + config.maxMemory = 1073741824; | ||
| 209 | + config.remoteRatio = 0.5; | ||
| 210 | + ASSERT_EQ(mgr.SetProcMemConfig(config), UBSE_OK); | ||
| 211 | + | ||
| 212 | + ProcessMemPidCollect::GetInstance().HandleNewPidsByName({getpid()}, 1); | ||
| 213 | + | ||
| 214 | + auto snapshot = mgr.GetManagedPidCacheSnapshot(); | ||
| 215 | + auto it = snapshot.find(getpid()); | ||
| 216 | + ASSERT_NE(it, snapshot.end()); | ||
| 217 | + EXPECT_EQ(it->second.sources & static_cast<uint8_t>(def::ConfigSource::NAME_CONFIG), | ||
| 218 | + static_cast<uint8_t>(def::ConfigSource::NAME_CONFIG)); | ||
| 219 | + | ||
| 220 | + mgr.RemoveManagedPidEntry(getpid()); | ||
| 221 | + EXPECT_EQ(mgr.RemoveProcMemConfig(false, comm), UBSE_OK); | ||
| 381 | } | 222 | } |
| 382 | } // namespace ubse::ut::process_mem | 223 | } // namespace ubse::ut::process_mem |
| @@ -14,7 +14,10 @@ | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | + | ||
| 18 | + | ||
| 17 | 19 | ||
| 20 | + | ||
| 18 | 21 | ||
| 19 | namespace ubse::ut::process_mem { | 22 | namespace ubse::ut::process_mem { |
| 20 | 23 | ||
| @@ -18,9 +18,15 @@ namespace ubse::ut::process_mem { | |||
| 18 | using namespace ::process_mem::manager; | 18 | using namespace ::process_mem::manager; |
| 19 | using namespace ::process_mem::def; | 19 | using namespace ::process_mem::def; |
| 20 | 20 | ||
| 21 | -void TestProcessMemPidConfigManager::SetUp() {} | 21 | +void TestProcessMemPidConfigManager::SetUp() |
| 22 | +{ | ||
| 23 | + ubse::storage::MockResetStorageErrors(); | ||
| 24 | +} | ||
| 22 | 25 | ||
| 23 | -void TestProcessMemPidConfigManager::TearDown() {} | 26 | +void TestProcessMemPidConfigManager::TearDown() |
| 27 | +{ | ||
| 28 | + ubse::storage::MockResetStorageErrors(); | ||
| 29 | +} | ||
| 24 | 30 | ||
| 25 | TEST_F(TestProcessMemPidConfigManager, GetExactStartTimeInvalidPid) | 31 | TEST_F(TestProcessMemPidConfigManager, GetExactStartTimeInvalidPid) |
| 26 | { | 32 | { |
| @@ -34,198 +40,121 @@ TEST_F(TestProcessMemPidConfigManager, GetExactStartTimeNonExistentPid) | |||
| 34 | EXPECT_EQ(startTime, 0u); | 40 | EXPECT_EQ(startTime, 0u); |
| 35 | } | 41 | } |
| 36 | 42 | ||
| 37 | -TEST_F(TestProcessMemPidConfigManager, IsPidInfoExistInvalidPid) | 43 | +TEST_F(TestProcessMemPidConfigManager, PersistProcMemConfigPidMode) |
| 38 | { | 44 | { |
| 39 | - bool result = ProcessMemPidConfigManager::IsPidInfoExist(-1, 12345); | 45 | + ProcessMemNewConfigInfo config{}; |
| 40 | - EXPECT_FALSE(result); | 46 | + config.isPid = true; |
| 47 | + config.identifier = "1234"; | ||
| 48 | + config.maxMemory = 1024000; | ||
| 49 | + config.remoteRatio = 0.5; | ||
| 50 | + config.startTime = 9999; | ||
| 51 | + | ||
| 52 | + auto ret = ProcessMemPidConfigManager::PersistProcMemConfig(config); | ||
| 53 | + EXPECT_EQ(ret, UBSE_OK); | ||
| 54 | + | ||
| 55 | + ProcessMemPidConfigManager::DeleteProcMemConfig(true, "1234"); | ||
| 41 | } | 56 | } |
| 42 | 57 | ||
| 43 | -TEST_F(TestProcessMemPidConfigManager, IsPidInfoExistNonExistentPid) | 58 | +TEST_F(TestProcessMemPidConfigManager, PersistProcMemConfigNameMode) |
| 44 | { | 59 | { |
| 45 | - bool result = ProcessMemPidConfigManager::IsPidInfoExist(99999999, 12345); | 60 | + ProcessMemNewConfigInfo config{}; |
| 46 | - EXPECT_FALSE(result); | 61 | + config.isPid = false; |
| 62 | + config.identifier = "myproc"; | ||
| 63 | + config.maxMemory = 2048000; | ||
| 64 | + config.remoteRatio = 0.3; | ||
| 65 | + | ||
| 66 | + auto ret = ProcessMemPidConfigManager::PersistProcMemConfig(config); | ||
| 67 | + EXPECT_EQ(ret, UBSE_OK); | ||
| 68 | + | ||
| 69 | + ProcessMemPidConfigManager::DeleteProcMemConfig(false, "myproc"); | ||
| 47 | } | 70 | } |
| 48 | 71 | ||
| 49 | -TEST_F(TestProcessMemPidConfigManager, IsPidInfoExistValidPidWrongStartTime) | 72 | +TEST_F(TestProcessMemPidConfigManager, PersistProcMemConfigStoragePutError) |
| 50 | { | 73 | { |
| 51 | - bool result = ProcessMemPidConfigManager::IsPidInfoExist(1, 0); | 74 | + ubse::storage::MockSetStoragePutError(UBSE_ERROR); |
| 52 | - EXPECT_FALSE(result); | 75 | + |
| 76 | + ProcessMemNewConfigInfo config{}; | ||
| 77 | + config.isPid = true; | ||
| 78 | + config.identifier = "4001"; | ||
| 79 | + config.maxMemory = 1024000; | ||
| 80 | + | ||
| 81 | + auto ret = ProcessMemPidConfigManager::PersistProcMemConfig(config); | ||
| 82 | + EXPECT_EQ(ret, UBSE_ERROR); | ||
| 53 | } | 83 | } |
| 54 | 84 | ||
| 55 | -TEST_F(TestProcessMemPidConfigManager, IsPidInfoExistValidPid) | 85 | +TEST_F(TestProcessMemPidConfigManager, DeleteProcMemConfigNonExistent) |
| 56 | { | 86 | { |
| 57 | - auto exactStartTime = ProcessMemPidConfigManager::GetExactStartTime(1); | 87 | + EXPECT_NO_THROW(ProcessMemPidConfigManager::DeleteProcMemConfig(true, "99999999")); |
| 58 | - bool result = ProcessMemPidConfigManager::IsPidInfoExist(1, exactStartTime); | ||
| 59 | - EXPECT_TRUE(result); | ||
| 60 | } | 88 | } |
| 61 | 89 | ||
| 62 | -TEST_F(TestProcessMemPidConfigManager, CheckPidConfigInfoInvalidPid) | 90 | +TEST_F(TestProcessMemPidConfigManager, DeleteProcMemConfigStorageDeleteError) |
| 63 | { | 91 | { |
| 64 | - ProcessMemPidInfo pidInfo{}; | 92 | + ubse::storage::MockSetStorageDeleteError(UBSE_ERROR); |
| 65 | - pidInfo.configInfo.pid = -1; | 93 | + EXPECT_NO_THROW(ProcessMemPidConfigManager::DeleteProcMemConfig(true, "4002")); |
| 66 | - pidInfo.startTime = 12345; | ||
| 67 | - bool result = ProcessMemPidConfigManager::CheckPidConfigInfo(pidInfo); | ||
| 68 | - EXPECT_FALSE(result); | ||
| 69 | } | 94 | } |
| 70 | 95 | ||
| 71 | -TEST_F(TestProcessMemPidConfigManager, CheckPidConfigInfoNonExistentPid) | 96 | +TEST_F(TestProcessMemPidConfigManager, GetAllPersistedProcMemConfigsEmpty) |
| 72 | { | 97 | { |
| 73 | - ProcessMemPidInfo pidInfo{}; | 98 | + std::vector<ProcessMemNewConfigInfo> configs; |
| 74 | - pidInfo.configInfo.pid = 99999999; | 99 | + EXPECT_NO_THROW(ProcessMemPidConfigManager::GetAllPersistedProcMemConfigs(configs)); |
| 75 | - pidInfo.startTime = 12345; | 100 | + EXPECT_TRUE(configs.empty()); |
| 76 | - bool result = ProcessMemPidConfigManager::CheckPidConfigInfo(pidInfo); | ||
| 77 | - EXPECT_FALSE(result); | ||
| 78 | } | 101 | } |
| 79 | 102 | ||
| 80 | -TEST_F(TestProcessMemPidConfigManager, CheckPidConfigInfoValidPidWrongStartTime) | 103 | +TEST_F(TestProcessMemPidConfigManager, GetAllPersistedProcMemConfigsStorageListError) |
| 81 | { | 104 | { |
| 82 | - ProcessMemPidInfo pidInfo{}; | 105 | + ubse::storage::MockSetStorageListError(UBSE_ERROR); |
| 83 | - pidInfo.configInfo.pid = 1; | 106 | + |
| 84 | - pidInfo.startTime = 0; | 107 | + std::vector<ProcessMemNewConfigInfo> configs; |
| 85 | - bool result = ProcessMemPidConfigManager::CheckPidConfigInfo(pidInfo); | 108 | + EXPECT_NO_THROW(ProcessMemPidConfigManager::GetAllPersistedProcMemConfigs(configs)); |
| 86 | - EXPECT_FALSE(result); | 109 | + EXPECT_TRUE(configs.empty()); |
| 87 | } | 110 | } |
| 88 | 111 | ||
| 89 | -TEST_F(TestProcessMemPidConfigManager, PersistPidConfigInfo) | 112 | +TEST_F(TestProcessMemPidConfigManager, QueryProcMemConfigCallbackNullBuffer) |
| 90 | -{ | ||
| 91 | - ProcessMemPidInfo pidInfo{}; | ||
| 92 | - pidInfo.configInfo.pid = 1234; | ||
| 93 | - pidInfo.configInfo.evictThreshold = 80; | ||
| 94 | - pidInfo.configInfo.targetEvictThreshold = 70; | ||
| 95 | - pidInfo.configInfo.reclaimThreshold = 50; | ||
| 96 | - pidInfo.configInfo.expectedMemoryUsage = 1024000; | ||
| 97 | - pidInfo.startTime = 9999; | ||
| 98 | - | ||
| 99 | - EXPECT_NO_THROW(ProcessMemPidConfigManager::PersistPidConfigInfo(pidInfo)); | ||
| 100 | - | ||
| 101 | - ProcessMemPidConfigManager::DeletePidConfigInfo(1234); | ||
| 102 | -} | ||
| 103 | - | ||
| 104 | -TEST_F(TestProcessMemPidConfigManager, DeletePidConfigInfo) | ||
| 105 | -{ | ||
| 106 | - ProcessMemPidInfo pidInfo{}; | ||
| 107 | - pidInfo.configInfo.pid = 5678; | ||
| 108 | - pidInfo.configInfo.evictThreshold = 85; | ||
| 109 | - pidInfo.startTime = 8888; | ||
| 110 | - | ||
| 111 | - ProcessMemPidConfigManager::PersistPidConfigInfo(pidInfo); | ||
| 112 | - EXPECT_NO_THROW(ProcessMemPidConfigManager::DeletePidConfigInfo(5678)); | ||
| 113 | -} | ||
| 114 | - | ||
| 115 | -TEST_F(TestProcessMemPidConfigManager, DeletePidConfigInfoNonExistent) | ||
| 116 | -{ | ||
| 117 | - EXPECT_NO_THROW(ProcessMemPidConfigManager::DeletePidConfigInfo(99999999)); | ||
| 118 | -} | ||
| 119 | - | ||
| 120 | -TEST_F(TestProcessMemPidConfigManager, GetAllPersistedPidConfigInfo) | ||
| 121 | -{ | ||
| 122 | - ProcessMemPidInfo pidInfo1{}; | ||
| 123 | - pidInfo1.configInfo.pid = 1111; | ||
| 124 | - pidInfo1.startTime = 1000; | ||
| 125 | - pidInfo1.configInfo.evictThreshold = 80; | ||
| 126 | - | ||
| 127 | - ProcessMemPidInfo pidInfo2{}; | ||
| 128 | - pidInfo2.configInfo.pid = 2222; | ||
| 129 | - pidInfo2.startTime = 2000; | ||
| 130 | - pidInfo2.configInfo.evictThreshold = 90; | ||
| 131 | - | ||
| 132 | - ProcessMemPidConfigManager::PersistPidConfigInfo(pidInfo1); | ||
| 133 | - ProcessMemPidConfigManager::PersistPidConfigInfo(pidInfo2); | ||
| 134 | - | ||
| 135 | - std::vector<ProcessMemPidInfo> pidInfos; | ||
| 136 | - EXPECT_NO_THROW(ProcessMemPidConfigManager::GetAllPersistedPidConfigInfo(pidInfos)); | ||
| 137 | - | ||
| 138 | - ProcessMemPidConfigManager::DeletePidConfigInfo(1111); | ||
| 139 | - ProcessMemPidConfigManager::DeletePidConfigInfo(2222); | ||
| 140 | -} | ||
| 141 | - | ||
| 142 | -TEST_F(TestProcessMemPidConfigManager, QueryPidConfigCallbackNullBuffer) | ||
| 143 | { | 113 | { |
| 144 | UbseByteBuffer buff{}; | 114 | UbseByteBuffer buff{}; |
| 145 | buff.data = nullptr; | 115 | buff.data = nullptr; |
| 146 | buff.len = 0; | 116 | buff.len = 0; |
| 147 | - std::vector<ProcessMemPidInfo> pidInfos; | 117 | + std::vector<ProcessMemNewConfigInfo> configs; |
| 148 | - EXPECT_NO_THROW(ProcessMemPidConfigManager::QueryPidConfigCallback("prefix", "key", buff, &pidInfos)); | 118 | + EXPECT_NO_THROW(ProcessMemPidConfigManager::QueryProcMemConfigCallback("prefix", "key", buff, &configs)); |
| 149 | - EXPECT_TRUE(pidInfos.empty()); | 119 | + EXPECT_TRUE(configs.empty()); |
| 150 | } | 120 | } |
| 151 | 121 | ||
| 152 | -TEST_F(TestProcessMemPidConfigManager, QueryPidConfigCallbackValidBuffer) | 122 | +TEST_F(TestProcessMemPidConfigManager, QueryProcMemConfigCallbackValidBuffer) |
| 153 | { | 123 | { |
| 154 | - ProcessMemPidInfo pidInfo{}; | 124 | + ProcessMemNewConfigInfo config{}; |
| 155 | - pidInfo.configInfo.pid = 3333; | 125 | + config.isPid = true; |
| 156 | - pidInfo.startTime = 3000; | 126 | + config.identifier = "3333"; |
| 157 | - pidInfo.configInfo.evictThreshold = 75; | 127 | + config.maxMemory = 3072000; |
| 128 | + config.remoteRatio = 0.6; | ||
| 129 | + config.startTime = 3000; | ||
| 158 | 130 | ||
| 159 | ubse::serial::UbseSerialization serializer; | 131 | ubse::serial::UbseSerialization serializer; |
| 160 | - auto ret = pidInfo.SerializePidInfo(serializer); | 132 | + auto ret = config.Serialize(serializer); |
| 161 | EXPECT_EQ(ret, UBSE_OK); | 133 | EXPECT_EQ(ret, UBSE_OK); |
| 162 | 134 | ||
| 163 | UbseByteBuffer buff{}; | 135 | UbseByteBuffer buff{}; |
| 164 | buff.data = serializer.GetBuffer(); | 136 | buff.data = serializer.GetBuffer(); |
| 165 | buff.len = serializer.GetLength(); | 137 | buff.len = serializer.GetLength(); |
| 166 | 138 | ||
| 167 | - std::vector<ProcessMemPidInfo> pidInfos; | 139 | + std::vector<ProcessMemNewConfigInfo> configs; |
| 168 | - EXPECT_NO_THROW(ProcessMemPidConfigManager::QueryPidConfigCallback("prefix", "3333", buff, &pidInfos)); | 140 | + EXPECT_NO_THROW(ProcessMemPidConfigManager::QueryProcMemConfigCallback("prefix", "3333", buff, &configs)); |
| 141 | + ASSERT_EQ(configs.size(), 1u); | ||
| 142 | + EXPECT_TRUE(configs[0].isPid); | ||
| 143 | + EXPECT_EQ(configs[0].identifier, "3333"); | ||
| 144 | + EXPECT_EQ(configs[0].maxMemory, 3072000u); | ||
| 145 | + EXPECT_DOUBLE_EQ(configs[0].remoteRatio, 0.6); | ||
| 146 | + EXPECT_EQ(configs[0].startTime, 3000u); | ||
| 169 | } | 147 | } |
| 170 | 148 | ||
| 171 | -// ==================== Storage error path tests ==================== | 149 | +TEST_F(TestProcessMemPidConfigManager, QueryProcMemConfigCallbackInvalidData) |
| 172 | - | ||
| 173 | -TEST_F(TestProcessMemPidConfigManager, PersistPidConfigInfoStoragePutError) | ||
| 174 | -{ | ||
| 175 | - ubse::storage::MockSetStoragePutError(UBSE_ERROR); | ||
| 176 | - | ||
| 177 | - ProcessMemPidInfo pidInfo{}; | ||
| 178 | - pidInfo.configInfo.pid = 4001; | ||
| 179 | - pidInfo.configInfo.evictThreshold = 80; | ||
| 180 | - pidInfo.startTime = 1000; | ||
| 181 | - | ||
| 182 | - EXPECT_NO_THROW(ProcessMemPidConfigManager::PersistPidConfigInfo(pidInfo)); | ||
| 183 | - | ||
| 184 | - ubse::storage::MockSetStoragePutError(UBSE_OK); | ||
| 185 | -} | ||
| 186 | - | ||
| 187 | -TEST_F(TestProcessMemPidConfigManager, DeletePidConfigInfoStorageDeleteError) | ||
| 188 | -{ | ||
| 189 | - ubse::storage::MockSetStorageDeleteError(UBSE_ERROR); | ||
| 190 | - | ||
| 191 | - EXPECT_NO_THROW(ProcessMemPidConfigManager::DeletePidConfigInfo(4002)); | ||
| 192 | - | ||
| 193 | - ubse::storage::MockSetStorageDeleteError(UBSE_OK); | ||
| 194 | -} | ||
| 195 | - | ||
| 196 | -TEST_F(TestProcessMemPidConfigManager, GetAllPersistedPidConfigInfoStorageQueryError) | ||
| 197 | -{ | ||
| 198 | - ubse::storage::MockSetStorageQueryError(UBSE_ERROR); | ||
| 199 | - | ||
| 200 | - std::vector<ProcessMemPidInfo> pidInfos; | ||
| 201 | - EXPECT_NO_THROW(ProcessMemPidConfigManager::GetAllPersistedPidConfigInfo(pidInfos)); | ||
| 202 | - | ||
| 203 | - ubse::storage::MockSetStorageQueryError(UBSE_OK); | ||
| 204 | -} | ||
| 205 | - | ||
| 206 | -// ==================== CheckPidConfigInfo with valid pid ==================== | ||
| 207 | - | ||
| 208 | -TEST_F(TestProcessMemPidConfigManager, CheckPidConfigInfoValidPid) | ||
| 209 | -{ | ||
| 210 | - ProcessMemPidInfo pidInfo{}; | ||
| 211 | - pidInfo.configInfo.pid = 1; | ||
| 212 | - auto exactStartTime = ProcessMemPidConfigManager::GetExactStartTime(1); | ||
| 213 | - pidInfo.startTime = exactStartTime; | ||
| 214 | - bool result = ProcessMemPidConfigManager::CheckPidConfigInfo(pidInfo); | ||
| 215 | - EXPECT_TRUE(result); | ||
| 216 | -} | ||
| 217 | - | ||
| 218 | -// ==================== QueryPidConfigCallback with deserialization error path ==================== | ||
| 219 | - | ||
| 220 | -TEST_F(TestProcessMemPidConfigManager, QueryPidConfigCallbackInvalidData) | ||
| 221 | { | 150 | { |
| 222 | UbseByteBuffer buff{}; | 151 | UbseByteBuffer buff{}; |
| 223 | uint8_t badData = 0xFF; | 152 | uint8_t badData = 0xFF; |
| 224 | buff.data = &badData; | 153 | buff.data = &badData; |
| 225 | buff.len = 1; | 154 | buff.len = 1; |
| 226 | 155 | ||
| 227 | - std::vector<ProcessMemPidInfo> pidInfos; | 156 | + std::vector<ProcessMemNewConfigInfo> configs; |
| 228 | - EXPECT_NO_THROW(ProcessMemPidConfigManager::QueryPidConfigCallback("prefix", "key", buff, &pidInfos)); | 157 | + EXPECT_NO_THROW(ProcessMemPidConfigManager::QueryProcMemConfigCallback("prefix", "key", buff, &configs)); |
| 229 | - EXPECT_TRUE(pidInfos.empty()); | 158 | + EXPECT_TRUE(configs.empty()); |
| 230 | } | 159 | } |
| 231 | } // namespace ubse::ut::process_mem | 160 | } // namespace ubse::ut::process_mem |
| @@ -0,0 +1,1797 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved. | ||
| 3 | + * ubs-engine is licensed under Mulan PSL v2. | ||
| 4 | + * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 5 | + * You may obtain a copy of Mulan PSL v2 at: | ||
| 6 | + * http://license.coscl.org.cn/MulanPSL2 | ||
| 7 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 8 | + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 9 | + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 10 | + * See the Mulan PSL v2 for more details. | ||
| 11 | + */ | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +namespace ubse::ut::process_mem { | ||
| 24 | +using namespace ::process_mem::decision; | ||
| 25 | +using namespace ::process_mem::manager; | ||
| 26 | +using namespace ::process_mem::def; | ||
| 27 | +using namespace ::process_mem::pid::bridge; | ||
| 28 | +using namespace ::process_mem::collect; | ||
| 29 | + | ||
| 30 | +namespace { | ||
| 31 | +constexpr uint64_t GB = 1073741824ULL; | ||
| 32 | +constexpr uint64_t THRESHOLD_BYTES = 50 * GB; | ||
| 33 | +constexpr uint64_t EMERGENCY_BYTES = 5 * GB; | ||
| 34 | + | ||
| 35 | +constexpr uint32_t KB_PER_GB = 1024 * 1024; | ||
| 36 | + | ||
| 37 | +std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo> MakeLentDebts(const std::vector<std::string>& borrowers) | ||
| 38 | +{ | ||
| 39 | + std::vector<ubse::mem::controller::UbseNumaMemoryDebtInfo> debts; | ||
| 40 | + for (const auto& borrower : borrowers) { | ||
| 41 | + ubse::mem::controller::UbseNumaMemoryDebtInfo debt; | ||
| 42 | + debt.name = "debt-" + borrower; | ||
| 43 | + debt.borrowNodeId = borrower; | ||
| 44 | + debt.lentNodeId = "NODE0"; | ||
| 45 | + debt.size = 1 * GB; | ||
| 46 | + debt.remoteNumaId = 5; | ||
| 47 | + debts.push_back(debt); | ||
| 48 | + } | ||
| 49 | + return debts; | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +ubse::mem::controller::UbseNumaMemoryDebtInfo MakeDebt(const std::string& name, const std::string& lender, | ||
| 53 | + int64_t remoteNuma, int32_t pid) | ||
| 54 | +{ | ||
| 55 | + ubse::mem::controller::UbseNumaMemoryDebtInfo debt; | ||
| 56 | + debt.name = name; | ||
| 57 | + debt.borrowNodeId = "NODE0"; | ||
| 58 | + debt.lentNodeId = lender; | ||
| 59 | + debt.size = 1 * GB; | ||
| 60 | + debt.remoteNumaId = remoteNuma; | ||
| 61 | + ProcessMemUsrInfo usr{}; | ||
| 62 | + usr.pid = pid; | ||
| 63 | + usr.srcNuma = 1; | ||
| 64 | + memset(debt.usrInfo, 0, sizeof(debt.usrInfo)); | ||
| 65 | + memcpy(debt.usrInfo, &usr, sizeof(usr)); | ||
| 66 | + return debt; | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +BorrowState MakeBorrow(uint64_t amountGb, const std::string& debtId) | ||
| 70 | +{ | ||
| 71 | + BorrowState borrow; | ||
| 72 | + borrow.currentRemote = amountGb * GB; | ||
| 73 | + BorrowSlot slot; | ||
| 74 | + slot.debtId = debtId; | ||
| 75 | + slot.migratedBytes = amountGb * GB; | ||
| 76 | + slot.capacity = amountGb * GB; | ||
| 77 | + slot.status = BorrowSlotStatus::COMPLETED; | ||
| 78 | + slot.remoteNumaId = 5; | ||
| 79 | + borrow.slots.push_back(slot); | ||
| 80 | + return borrow; | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +void AddManagedPid(pid_t pid, uint64_t maxGb, double ratio, uint64_t vmRssGb, const BorrowState& borrow = {}) | ||
| 84 | +{ | ||
| 85 | + auto& mgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 86 | + mgr.AddNameSourceToManagedPid(pid, "proc" + std::to_string(pid), maxGb * GB, ratio); | ||
| 87 | + PidCollectInfoMap collectMap; | ||
| 88 | + collectMap.entries[pid].vmRssKb = vmRssGb * 1024 * 1024; | ||
| 89 | + mgr.UpdateManagedPidVmRssBatch(collectMap); | ||
| 90 | + if (!borrow.slots.empty()) { | ||
| 91 | + mgr.UpdateManagedPidBorrowState(pid, borrow, ProcessStatus::BORROWED); | ||
| 92 | + } | ||
| 93 | +} | ||
| 94 | + | ||
| 95 | +// nodeFree 口径统一为 MemAvailable 后,mock 直接驱动 memAvailableReader | ||
| 96 | +void MockNodeFreeBytes(uint64_t bytes) | ||
| 97 | +{ | ||
| 98 | + ProcessMemPidDecision::memAvailableReader = [bytes]() { | ||
| 99 | + return bytes / 1024; | ||
| 100 | + }; | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +void MockNodeFree(uint64_t freeGb) | ||
| 104 | +{ | ||
| 105 | + MockNodeFreeBytes(freeGb * GB); | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +std::vector<ReturnRequestItem> DecodeItems(const std::string& payload) | ||
| 109 | +{ | ||
| 110 | + ubse::serial::UbseDeSerialization deserializer{reinterpret_cast<const ubse::serial::base_ptr_type*>(payload.data()), | ||
| 111 | + payload.size()}; | ||
| 112 | + ubse::serial::common_len count = 0; | ||
| 113 | + deserializer >> ubse::serial::array_len_capture(count); | ||
| 114 | + std::vector<ReturnRequestItem> items; | ||
| 115 | + for (ubse::serial::common_len i = 0; i < count; ++i) { | ||
| 116 | + ReturnRequestItem item; | ||
| 117 | + deserializer >> item.name >> item.size; | ||
| 118 | + items.push_back(item); | ||
| 119 | + } | ||
| 120 | + return items; | ||
| 121 | +} | ||
| 122 | +} // namespace | ||
| 123 | + | ||
| 124 | +void TestProcessMemPidDecision::SetUp() | ||
| 125 | +{ | ||
| 126 | + ubse::mem::controller::MockResetAllErrors(); | ||
| 127 | + ubse::com::MockResetRpcState(); | ||
| 128 | + ubse::nodeController::MockSetCurrentNodeId("NODE0"); | ||
| 129 | + | ||
| 130 | + ubse::smap::MockResetMigrateState(); | ||
| 131 | + ProcessMemPidBridge::rmrsMigrateOut = ubse::smap::MockRmrsMigrateOut; | ||
| 132 | + ProcessMemPidBridge::rmrsFreeWithMigrate = [](const std::string&) { | ||
| 133 | + return 0; | ||
| 134 | + }; | ||
| 135 | + | ||
| 136 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 137 | + decision.stopping_ = false; | ||
| 138 | + decision.freeMemoryThresholdBytes_ = THRESHOLD_BYTES; | ||
| 139 | + decision.emergencyThresholdBytes_ = EMERGENCY_BYTES; | ||
| 140 | + decision.fastPollIntervalMs_ = 200; | ||
| 141 | + decision.observeCycles_ = 6; | ||
| 142 | + decision.returnRetryIntervalMs_ = 1; | ||
| 143 | + decision.oomFastPoll_ = false; | ||
| 144 | + decision.oomFastWindowCount_ = 0; | ||
| 145 | + decision.oomFastWindowMin_ = 0; | ||
| 146 | + if (decision.borrowExecutor_ == nullptr || !decision.borrowExecutor_->IsStart()) { | ||
| 147 | + decision.borrowExecutor_ = ubse::task_executor::UbseTaskExecutor::Create("ProcMemDecisionTest", 2, 64); | ||
| 148 | + decision.borrowExecutor_->Start(); | ||
| 149 | + } | ||
| 150 | + if (decision.returnExecutor_ == nullptr || !decision.returnExecutor_->IsStart()) { | ||
| 151 | + decision.returnExecutor_ = ubse::task_executor::UbseTaskExecutor::Create("ProcMemReturnTest", 2, 64); | ||
| 152 | + decision.returnExecutor_->Start(); | ||
| 153 | + } | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +void TestProcessMemPidDecision::TearDown() | ||
| 157 | +{ | ||
| 158 | + ProcessMemPidBridge::rmrsMigrateOut = {}; | ||
| 159 | + ubse::smap::MockResetMigrateState(); | ||
| 160 | + ProcessMemPidBridge::rmrsRemove = {}; | ||
| 161 | + ProcessMemPidBridge::rmrsFreeWithMigrate = {}; | ||
| 162 | + ProcessMemPidBridge::rmrsRemoteToRemote = {}; | ||
| 163 | + ProcessMemPidBridge::rmrsProcessConfigQuery = {}; | ||
| 164 | + ProcessMemPidDecision::memAvailableReader = nullptr; | ||
| 165 | + ProcessMemPidCollect::GetInstance().SetCollectVmRssOverride(nullptr); | ||
| 166 | + | ||
| 167 | + auto& mgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 168 | + auto snapshot = mgr.GetManagedPidCacheSnapshot(); | ||
| 169 | + for (const auto& [pid, entry] : snapshot) { | ||
| 170 | + mgr.RemoveManagedPidEntry(pid); | ||
| 171 | + } | ||
| 172 | + ubse::mem::controller::MockResetAllErrors(); | ||
| 173 | + ubse::task_executor::MockSetExecutorAsync(false); | ||
| 174 | + ubse::com::MockResetRpcState(); | ||
| 175 | +} | ||
| 176 | + | ||
| 177 | +TEST_F(TestProcessMemPidDecision, PassiveReturnNoBroadcastWhenFreeAboveThreshold) | ||
| 178 | +{ | ||
| 179 | + MockNodeFree(60); | ||
| 180 | + ubse::mem::controller::MockSetDebtInfos(MakeLentDebts({"NODE1", "NODE2"})); | ||
| 181 | + | ||
| 182 | + ProcessMemPidDecision::GetInstance().OnDecisionTimer(); | ||
| 183 | + | ||
| 184 | + EXPECT_TRUE(ubse::com::MockGetRpcSendRecords().empty()); | ||
| 185 | +} | ||
| 186 | + | ||
| 187 | +TEST_F(TestProcessMemPidDecision, PassiveReturnBroadcastToAllBorrowers) | ||
| 188 | +{ | ||
| 189 | + MockNodeFree(10); | ||
| 190 | + auto debts = MakeLentDebts({"NODE1", "NODE2", "NODE3"}); | ||
| 191 | + ubse::mem::controller::UbseNumaMemoryDebtInfo otherDebt; | ||
| 192 | + otherDebt.name = "debt-other"; | ||
| 193 | + otherDebt.borrowNodeId = "NODE9"; | ||
| 194 | + otherDebt.lentNodeId = "NODE2"; | ||
| 195 | + otherDebt.size = 1 * GB; | ||
| 196 | + debts.push_back(otherDebt); | ||
| 197 | + ubse::mem::controller::UbseNumaMemoryDebtInfo selfDebt; | ||
| 198 | + selfDebt.name = "debt-self"; | ||
| 199 | + selfDebt.borrowNodeId = "NODE0"; | ||
| 200 | + selfDebt.lentNodeId = "NODE0"; | ||
| 201 | + selfDebt.size = 1 * GB; | ||
| 202 | + debts.push_back(selfDebt); | ||
| 203 | + ubse::mem::controller::MockSetDebtInfos(debts); | ||
| 204 | + | ||
| 205 | + ProcessMemPidDecision::GetInstance().OnDecisionTimer(); | ||
| 206 | + | ||
| 207 | + auto records = ubse::com::MockGetRpcSendRecords(); | ||
| 208 | + ASSERT_EQ(records.size(), 3u); | ||
| 209 | + std::set<std::string> addresses; | ||
| 210 | + for (const auto& record : records) { | ||
| 211 | + addresses.insert(record.address); | ||
| 212 | + auto items = DecodeItems(record.payload); | ||
| 213 | + ASSERT_EQ(items.size(), 1u); | ||
| 214 | + EXPECT_EQ(items[0].name, "debt-" + record.address); | ||
| 215 | + EXPECT_EQ(items[0].size, 1 * GB); | ||
| 216 | + } | ||
| 217 | + EXPECT_EQ(addresses, (std::set<std::string>{"NODE1", "NODE2", "NODE3"})); | ||
| 218 | +} | ||
| 219 | + | ||
| 220 | +TEST_F(TestProcessMemPidDecision, PassiveReturnStopsWhenFreeRecovered) | ||
| 221 | +{ | ||
| 222 | + MockNodeFree(10); | ||
| 223 | + ubse::mem::controller::MockSetDebtInfos(MakeLentDebts({"NODE1", "NODE2"})); | ||
| 224 | + | ||
| 225 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 226 | + decision.OnDecisionTimer(); | ||
| 227 | + EXPECT_EQ(ubse::com::MockGetRpcSendRecords().size(), 2u); | ||
| 228 | + | ||
| 229 | + MockNodeFree(60); | ||
| 230 | + decision.OnDecisionTimer(); | ||
| 231 | + EXPECT_EQ(ubse::com::MockGetRpcSendRecords().size(), 2u); | ||
| 232 | +} | ||
| 233 | + | ||
| 234 | +TEST_F(TestProcessMemPidDecision, BorrowerDirectReturn) | ||
| 235 | +{ | ||
| 236 | + MockNodeFree(60); | ||
| 237 | + ubse::mem::controller::MockSetDebtInfos({MakeDebt("debt-x", "NODE0", 5, 1001)}); | ||
| 238 | + AddManagedPid(1001, 10, 0.5, 4, MakeBorrow(1, "debt-x")); | ||
| 239 | + | ||
| 240 | + std::vector<std::string> freedDebts; | ||
| 241 | + ProcessMemPidBridge::rmrsFreeWithMigrate = [&freedDebts](const std::string& name) { | ||
| 242 | + freedDebts.push_back(name); | ||
| 243 | + return 0; | ||
| 244 | + }; | ||
| 245 | + | ||
| 246 | + ReturnRequestItem item{"debt-x", 1 * GB}; | ||
| 247 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().HandleReturnRequest({item}), UBSE_OK); | ||
| 248 | + | ||
| 249 | + ASSERT_EQ(freedDebts.size(), 1u); | ||
| 250 | + EXPECT_EQ(freedDebts[0], "debt-x"); | ||
| 251 | + | ||
| 252 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 253 | + auto it = snapshot.find(1001); | ||
| 254 | + ASSERT_NE(it, snapshot.end()); | ||
| 255 | + EXPECT_EQ(it->second.borrow.currentRemote, 0u); | ||
| 256 | + EXPECT_TRUE(it->second.borrow.slots.empty()); | ||
| 257 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::IDLE); | ||
| 258 | +} | ||
| 259 | + | ||
| 260 | +TEST_F(TestProcessMemPidDecision, BorrowerRemoteToRemote) | ||
| 261 | +{ | ||
| 262 | + MockNodeFree(10); | ||
| 263 | + ubse::mem::controller::MockSetDebtInfos({MakeDebt("debt-x", "NODE0", 5, 1001)}); | ||
| 264 | + AddManagedPid(1001, 10, 0.5, 4, MakeBorrow(1, "debt-x")); | ||
| 265 | + | ||
| 266 | + std::vector<mempooling::smap::MigrateEscapeMsg> migrates; | ||
| 267 | + ProcessMemPidBridge::rmrsRemoteToRemote = [&migrates](const mempooling::smap::MigrateEscapeMsg& msg) { | ||
| 268 | + migrates.push_back(msg); | ||
| 269 | + return 0; | ||
| 270 | + }; | ||
| 271 | + | ||
| 272 | + ReturnRequestItem item{"debt-x", 1 * GB}; | ||
| 273 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().HandleReturnRequest({item}), UBSE_OK); | ||
| 274 | + | ||
| 275 | + ASSERT_EQ(migrates.size(), 1u); | ||
| 276 | + EXPECT_EQ(migrates[0].count, 1); | ||
| 277 | + EXPECT_EQ(migrates[0].payload[0].pid, 1001); | ||
| 278 | + EXPECT_EQ(migrates[0].payload[0].srcNid, 5); | ||
| 279 | + EXPECT_EQ(migrates[0].payload[0].destNid, 3); | ||
| 280 | + EXPECT_EQ(migrates[0].payload[0].memSize, 1 * 1024 * 1024); | ||
| 281 | + EXPECT_EQ(migrates[0].payload[0].migrateMode, mempooling::smap::MIG_MEMSIZE_MODE); | ||
| 282 | + EXPECT_FALSE(ubse::mem::controller::MockGetLastNumaCreateName().empty()); | ||
| 283 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), "debt-x"); | ||
| 284 | + | ||
| 285 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 286 | + auto it = snapshot.find(1001); | ||
| 287 | + ASSERT_NE(it, snapshot.end()); | ||
| 288 | + EXPECT_EQ(it->second.borrow.currentRemote, 1 * GB); | ||
| 289 | + ASSERT_EQ(it->second.borrow.slots.size(), 1u); | ||
| 290 | + EXPECT_EQ(it->second.borrow.slots[0].debtId, ubse::mem::controller::MockGetLastNumaCreateName()); | ||
| 291 | + EXPECT_EQ(it->second.borrow.slots[0].remoteNumaId, 3); | ||
| 292 | + EXPECT_EQ(it->second.borrow.slots[0].capacity, 1 * GB); | ||
| 293 | + EXPECT_EQ(it->second.borrow.slots[0].migratedBytes, 1 * GB); | ||
| 294 | + EXPECT_EQ(it->second.borrow.slots[0].returnStatus, ReturnStatus::NONE); | ||
| 295 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::BORROWED); | ||
| 296 | +} | ||
| 297 | + | ||
| 298 | +TEST_F(TestProcessMemPidDecision, BorrowerCreateFailRetriesUntilSuccess) | ||
| 299 | +{ | ||
| 300 | + MockNodeFree(10); | ||
| 301 | + ubse::mem::controller::MockSetDebtInfos({MakeDebt("debt-x", "NODE0", 5, 1001)}); | ||
| 302 | + AddManagedPid(1001, 10, 0.5, 4, MakeBorrow(1, "debt-x")); | ||
| 303 | + ubse::mem::controller::MockSetNumaCreateErrorOnce(UBSE_ERROR); | ||
| 304 | + | ||
| 305 | + std::vector<mempooling::smap::MigrateEscapeMsg> migrates; | ||
| 306 | + ProcessMemPidBridge::rmrsRemoteToRemote = [&migrates](const mempooling::smap::MigrateEscapeMsg& msg) { | ||
| 307 | + migrates.push_back(msg); | ||
| 308 | + return 0; | ||
| 309 | + }; | ||
| 310 | + | ||
| 311 | + ReturnRequestItem item{"debt-x", 1 * GB}; | ||
| 312 | + ProcessMemPidDecision::GetInstance().HandleReturnRequest({item}); | ||
| 313 | + | ||
| 314 | + ASSERT_EQ(migrates.size(), 1u); | ||
| 315 | + EXPECT_EQ(migrates[0].payload[0].pid, 1001); | ||
| 316 | + EXPECT_EQ(migrates[0].payload[0].srcNid, 5); | ||
| 317 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), "debt-x"); | ||
| 318 | + | ||
| 319 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 320 | + auto it = snapshot.find(1001); | ||
| 321 | + ASSERT_NE(it, snapshot.end()); | ||
| 322 | + EXPECT_EQ(it->second.borrow.currentRemote, 1 * GB); | ||
| 323 | + ASSERT_EQ(it->second.borrow.slots.size(), 1u); | ||
| 324 | + EXPECT_EQ(it->second.borrow.slots[0].debtId, ubse::mem::controller::MockGetLastNumaCreateName()); | ||
| 325 | + EXPECT_EQ(it->second.borrow.slots[0].remoteNumaId, 3); | ||
| 326 | + EXPECT_EQ(it->second.borrow.slots[0].capacity, 1 * GB); | ||
| 327 | + EXPECT_EQ(it->second.borrow.slots[0].migratedBytes, 1 * GB); | ||
| 328 | + EXPECT_EQ(it->second.borrow.slots[0].returnStatus, ReturnStatus::NONE); | ||
| 329 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::BORROWED); | ||
| 330 | +} | ||
| 331 | + | ||
| 332 | +TEST_F(TestProcessMemPidDecision, BorrowerRemoteToRemoteRetriesUntilSuccess) | ||
| 333 | +{ | ||
| 334 | + MockNodeFree(10); | ||
| 335 | + ubse::mem::controller::MockSetDebtInfos({MakeDebt("debt-x", "NODE0", 5, 1001)}); | ||
| 336 | + AddManagedPid(1001, 10, 0.5, 4, MakeBorrow(1, "debt-x")); | ||
| 337 | + | ||
| 338 | + std::vector<mempooling::smap::MigrateEscapeMsg> migrates; | ||
| 339 | + int migrateCalls = 0; | ||
| 340 | + ProcessMemPidBridge::rmrsRemoteToRemote = [&migrates, | ||
| 341 | + &migrateCalls](const mempooling::smap::MigrateEscapeMsg& msg) { | ||
| 342 | + migrates.push_back(msg); | ||
| 343 | + return (migrateCalls++ == 0) ? -1 : 0; | ||
| 344 | + }; | ||
| 345 | + | ||
| 346 | + ReturnRequestItem item{"debt-x", 1 * GB}; | ||
| 347 | + ProcessMemPidDecision::GetInstance().HandleReturnRequest({item}); | ||
| 348 | + | ||
| 349 | + ASSERT_EQ(migrates.size(), 2u); | ||
| 350 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), "debt-x"); | ||
| 351 | + | ||
| 352 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 353 | + auto it = snapshot.find(1001); | ||
| 354 | + ASSERT_NE(it, snapshot.end()); | ||
| 355 | + EXPECT_EQ(it->second.borrow.currentRemote, 1 * GB); | ||
| 356 | + ASSERT_EQ(it->second.borrow.slots.size(), 1u); | ||
| 357 | + EXPECT_EQ(it->second.borrow.slots[0].debtId, ubse::mem::controller::MockGetLastNumaCreateName()); | ||
| 358 | + EXPECT_EQ(it->second.borrow.slots[0].remoteNumaId, 3); | ||
| 359 | + EXPECT_EQ(it->second.borrow.slots[0].capacity, 1 * GB); | ||
| 360 | + EXPECT_EQ(it->second.borrow.slots[0].migratedBytes, 1 * GB); | ||
| 361 | + EXPECT_EQ(it->second.borrow.slots[0].returnStatus, ReturnStatus::NONE); | ||
| 362 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::BORROWED); | ||
| 363 | +} | ||
| 364 | + | ||
| 365 | +TEST_F(TestProcessMemPidDecision, ReturnDebtEnqueuedAsync) | ||
| 366 | +{ | ||
| 367 | + MockNodeFree(10); | ||
| 368 | + ubse::mem::controller::MockSetDebtInfos( | ||
| 369 | + {MakeDebt("debt-a", "NODE1", 5, 1001), MakeDebt("debt-b", "NODE2", 6, 1001)}); | ||
| 370 | + BorrowState borrow = MakeBorrow(1, "debt-a"); | ||
| 371 | + borrow.slots.push_back(MakeBorrow(1, "debt-b").slots[0]); | ||
| 372 | + borrow.currentRemote = 2 * GB; | ||
| 373 | + AddManagedPid(1001, 10, 0.5, 4, borrow); | ||
| 374 | + | ||
| 375 | + std::mutex gateMutex; | ||
| 376 | + std::condition_variable gateCv; | ||
| 377 | + bool gateOpen = false; | ||
| 378 | + std::atomic<bool> migrateStarted{false}; | ||
| 379 | + ProcessMemPidBridge::rmrsRemoteToRemote = [&](const mempooling::smap::MigrateEscapeMsg&) { | ||
| 380 | + migrateStarted = true; | ||
| 381 | + std::unique_lock<std::mutex> lock(gateMutex); | ||
| 382 | + gateCv.wait(lock, [&] { return gateOpen; }); | ||
| 383 | + return 0; | ||
| 384 | + }; | ||
| 385 | + | ||
| 386 | + ubse::task_executor::MockSetExecutorAsync(true); | ||
| 387 | + ReturnRequestItem itemA{"debt-a", 1 * GB}; | ||
| 388 | + ReturnRequestItem itemB{"debt-b", 1 * GB}; | ||
| 389 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().HandleReturnRequest({itemA, itemB}), UBSE_OK); | ||
| 390 | + | ||
| 391 | + auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5); | ||
| 392 | + while (!migrateStarted && std::chrono::steady_clock::now() < deadline) { | ||
| 393 | + std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||
| 394 | + } | ||
| 395 | + ASSERT_TRUE(migrateStarted); | ||
| 396 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 397 | + EXPECT_EQ(snapshot.at(1001).borrow.currentRemote, 2 * GB); | ||
| 398 | + | ||
| 399 | + { | ||
| 400 | + std::lock_guard<std::mutex> lock(gateMutex); | ||
| 401 | + gateOpen = true; | ||
| 402 | + } | ||
| 403 | + gateCv.notify_all(); | ||
| 404 | + ubse::task_executor::MockWaitExecutorIdle(); | ||
| 405 | + | ||
| 406 | + snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 407 | + auto it = snapshot.find(1001); | ||
| 408 | + ASSERT_NE(it, snapshot.end()); | ||
| 409 | + EXPECT_EQ(it->second.borrow.currentRemote, 2 * GB); | ||
| 410 | + ASSERT_EQ(it->second.borrow.slots.size(), 2u); | ||
| 411 | + EXPECT_NE(it->second.borrow.slots[0].debtId, "debt-a"); | ||
| 412 | + EXPECT_NE(it->second.borrow.slots[1].debtId, "debt-b"); | ||
| 413 | + EXPECT_EQ(it->second.borrow.slots[0].returnStatus, ReturnStatus::NONE); | ||
| 414 | + EXPECT_EQ(it->second.borrow.slots[1].returnStatus, ReturnStatus::NONE); | ||
| 415 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::BORROWED); | ||
| 416 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 2u); | ||
| 417 | + | ||
| 418 | + ubse::task_executor::MockSetExecutorAsync(false); | ||
| 419 | +} | ||
| 420 | + | ||
| 421 | +TEST_F(TestProcessMemPidDecision, TimeoutReturnEnqueuedAndSlotRemoved) | ||
| 422 | +{ | ||
| 423 | + const pid_t pid = 1001; | ||
| 424 | + const std::string debtId = "debt-timeout"; | ||
| 425 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 426 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 427 | + decision.timeoutPer128MbMs_ = 1000; | ||
| 428 | + | ||
| 429 | + BorrowState borrow; | ||
| 430 | + BorrowSlot slot; | ||
| 431 | + slot.debtId = debtId; | ||
| 432 | + slot.capacity = 1 * GB; | ||
| 433 | + slot.migratedBytes = 1 * GB; | ||
| 434 | + slot.remoteNumaId = 3; | ||
| 435 | + slot.status = BorrowSlotStatus::BORROWING; | ||
| 436 | + borrow.slots.push_back(slot); | ||
| 437 | + AddManagedPid(pid, 10, 0.5, 4, borrow); | ||
| 438 | + | ||
| 439 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 440 | + auto stale = snapshot.at(pid).borrow; | ||
| 441 | + stale.slots[0].borrowTime = std::chrono::steady_clock::now() - std::chrono::seconds(60); | ||
| 442 | + infoMgr.UpdateManagedPidBorrowState(pid, stale, ProcessStatus::BORROWING); | ||
| 443 | + | ||
| 444 | + ubse::task_executor::MockSetExecutorAsync(true); | ||
| 445 | + ubse::mem::controller::MockBlockNumaDelete(); | ||
| 446 | + decision.CheckTimeouts(2); | ||
| 447 | + | ||
| 448 | + ASSERT_TRUE(ubse::mem::controller::MockWaitNumaDeleteEntered(5000)); | ||
| 449 | + snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 450 | + EXPECT_TRUE(snapshot.at(pid).borrow.slots.empty()); | ||
| 451 | + EXPECT_EQ(snapshot.at(pid).borrow.currentRemote, 0u); | ||
| 452 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 1u); | ||
| 453 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), debtId); | ||
| 454 | + | ||
| 455 | + ubse::mem::controller::MockReleaseNumaDelete(); | ||
| 456 | + ubse::task_executor::MockWaitExecutorIdle(); | ||
| 457 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 1u); | ||
| 458 | + | ||
| 459 | + ubse::task_executor::MockSetExecutorAsync(false); | ||
| 460 | +} | ||
| 461 | + | ||
| 462 | +TEST_F(TestProcessMemPidDecision, PidExitedFreesAllMatchedDebts) | ||
| 463 | +{ | ||
| 464 | + ubse::mem::controller::MockSetDebtInfos({MakeDebt("debt-a", "NODE1", 5, 1001), MakeDebt("debt-b", "NODE2", 6, 1001), | ||
| 465 | + MakeDebt("debt-c", "NODE1", 5, 1002)}); | ||
| 466 | + | ||
| 467 | + std::vector<std::string> freedDebts; | ||
| 468 | + ProcessMemPidBridge::rmrsFreeWithMigrate = [&freedDebts](const std::string& name) { | ||
| 469 | + freedDebts.push_back(name); | ||
| 470 | + return 0; | ||
| 471 | + }; | ||
| 472 | + | ||
| 473 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().HandlePidExited(1001), UBSE_OK); | ||
| 474 | + | ||
| 475 | + std::set<std::string> freedSet(freedDebts.begin(), freedDebts.end()); | ||
| 476 | + EXPECT_EQ(freedSet, std::set<std::string>({"debt-a", "debt-b"})); | ||
| 477 | +} | ||
| 478 | + | ||
| 479 | +TEST_F(TestProcessMemPidDecision, PidExitedNoDebtNoFree) | ||
| 480 | +{ | ||
| 481 | + ubse::mem::controller::MockSetDebtInfos({MakeDebt("debt-c", "NODE1", 5, 1002)}); | ||
| 482 | + | ||
| 483 | + std::vector<std::string> freedDebts; | ||
| 484 | + ProcessMemPidBridge::rmrsFreeWithMigrate = [&freedDebts](const std::string& name) { | ||
| 485 | + freedDebts.push_back(name); | ||
| 486 | + return 0; | ||
| 487 | + }; | ||
| 488 | + | ||
| 489 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().HandlePidExited(9999), UBSE_OK); | ||
| 490 | + EXPECT_TRUE(freedDebts.empty()); | ||
| 491 | +} | ||
| 492 | + | ||
| 493 | +TEST_F(TestProcessMemPidDecision, PidExitedFreeFailureRetriesUntilSuccess) | ||
| 494 | +{ | ||
| 495 | + ubse::mem::controller::MockSetDebtInfos( | ||
| 496 | + {MakeDebt("debt-a", "NODE1", 5, 1001), MakeDebt("debt-b", "NODE2", 6, 1001)}); | ||
| 497 | + | ||
| 498 | + std::vector<std::string> freedDebts; | ||
| 499 | + int debtAFailCalls = 0; | ||
| 500 | + ProcessMemPidBridge::rmrsFreeWithMigrate = [&freedDebts, &debtAFailCalls](const std::string& name) { | ||
| 501 | + if (name == "debt-a" && debtAFailCalls++ == 0) { | ||
| 502 | + return UBSE_ERROR; | ||
| 503 | + } | ||
| 504 | + freedDebts.push_back(name); | ||
| 505 | + return UBSE_OK; | ||
| 506 | + }; | ||
| 507 | + | ||
| 508 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().HandlePidExited(1001), UBSE_OK); | ||
| 509 | + std::set<std::string> freedSet(freedDebts.begin(), freedDebts.end()); | ||
| 510 | + EXPECT_EQ(freedSet, std::set<std::string>({"debt-a", "debt-b"})); | ||
| 511 | +} | ||
| 512 | + | ||
| 513 | +TEST_F(TestProcessMemPidDecision, PidExitedUbsedDeleteThenRmrsReturn) | ||
| 514 | +{ | ||
| 515 | + ubse::mem::controller::MockSetDebtInfos({MakeDebt("debt-a", "NODE1", 5, 1001)}); | ||
| 516 | + | ||
| 517 | + std::vector<std::string> freedDebts; | ||
| 518 | + ProcessMemPidBridge::rmrsFreeWithMigrate = [&freedDebts](const std::string& name) { | ||
| 519 | + freedDebts.push_back(name); | ||
| 520 | + return 0; | ||
| 521 | + }; | ||
| 522 | + | ||
| 523 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().HandlePidExited(1001), UBSE_OK); | ||
| 524 | + | ||
| 525 | + EXPECT_GE(ubse::mem::controller::MockGetNumaDeleteCallCount(), 1u); | ||
| 526 | + ASSERT_EQ(freedDebts.size(), 1u); | ||
| 527 | + EXPECT_EQ(freedDebts[0], "debt-a"); | ||
| 528 | +} | ||
| 529 | + | ||
| 530 | +TEST_F(TestProcessMemPidDecision, PidExitedUbseDeleteFailRetriesUntilSuccess) | ||
| 531 | +{ | ||
| 532 | + ubse::mem::controller::MockSetDebtInfos({MakeDebt("debt-a", "NODE1", 5, 1001)}); | ||
| 533 | + ubse::mem::controller::MockSetNumaDeleteErrorOnce(UBSE_ERR_TIMEOUT); | ||
| 534 | + | ||
| 535 | + std::vector<std::string> freedDebts; | ||
| 536 | + ProcessMemPidBridge::rmrsFreeWithMigrate = [&freedDebts](const std::string& name) { | ||
| 537 | + freedDebts.push_back(name); | ||
| 538 | + return 0; | ||
| 539 | + }; | ||
| 540 | + | ||
| 541 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().HandlePidExited(1001), UBSE_OK); | ||
| 542 | + | ||
| 543 | + EXPECT_GE(ubse::mem::controller::MockGetNumaDeleteCallCount(), 2u); | ||
| 544 | + ASSERT_EQ(freedDebts.size(), 1u); | ||
| 545 | + EXPECT_EQ(freedDebts[0], "debt-a"); | ||
| 546 | +} | ||
| 547 | + | ||
| 548 | +TEST_F(TestProcessMemPidDecision, RemovePidAliveProcessUsesRmrsDirectly) | ||
| 549 | +{ | ||
| 550 | + pid_t pid = getpid(); | ||
| 551 | + ubse::mem::controller::MockSetDebtInfos({MakeDebt("debt-self", "NODE1", 5, static_cast<int32_t>(pid))}); | ||
| 552 | + | ||
| 553 | + std::vector<std::string> freedDebts; | ||
| 554 | + ProcessMemPidBridge::rmrsFreeWithMigrate = [&freedDebts](const std::string& name) { | ||
| 555 | + freedDebts.push_back(name); | ||
| 556 | + return 0; | ||
| 557 | + }; | ||
| 558 | + | ||
| 559 | + EXPECT_TRUE(ProcessMemPidDecision::GetInstance().EnqueuePidReturn(pid, ReturnScene::EXITED)); | ||
| 560 | + | ||
| 561 | + ASSERT_EQ(freedDebts.size(), 1u); | ||
| 562 | + EXPECT_EQ(freedDebts[0], "debt-self"); | ||
| 563 | + EXPECT_TRUE(ubse::mem::controller::MockGetLastNumaDeleteName().empty()); | ||
| 564 | +} | ||
| 565 | + | ||
| 566 | +TEST_F(TestProcessMemPidDecision, PidReturnEnqueuedAsync) | ||
| 567 | +{ | ||
| 568 | + ubse::mem::controller::MockSetDebtInfos( | ||
| 569 | + {MakeDebt("debt-a", "NODE1", 5, 1001), MakeDebt("debt-b", "NODE2", 6, 1001)}); | ||
| 570 | + BorrowState borrow = MakeBorrow(1, "debt-a"); | ||
| 571 | + borrow.slots.push_back(MakeBorrow(2, "debt-b").slots[0]); | ||
| 572 | + borrow.currentRemote = 3 * GB; | ||
| 573 | + AddManagedPid(1001, 10, 0.5, 4, borrow); | ||
| 574 | + | ||
| 575 | + std::mutex gateMutex; | ||
| 576 | + std::condition_variable gateCv; | ||
| 577 | + bool gateOpen = false; | ||
| 578 | + std::atomic<bool> freeStarted{false}; | ||
| 579 | + std::vector<std::string> freedDebts; | ||
| 580 | + ProcessMemPidBridge::rmrsFreeWithMigrate = [&](const std::string& name) { | ||
| 581 | + freedDebts.push_back(name); | ||
| 582 | + freeStarted = true; | ||
| 583 | + std::unique_lock<std::mutex> lock(gateMutex); | ||
| 584 | + gateCv.wait(lock, [&] { return gateOpen; }); | ||
| 585 | + return 0; | ||
| 586 | + }; | ||
| 587 | + | ||
| 588 | + ubse::task_executor::MockSetExecutorAsync(true); | ||
| 589 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().HandlePidExited(1001), UBSE_OK); | ||
| 590 | + | ||
| 591 | + auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5); | ||
| 592 | + while (!freeStarted && std::chrono::steady_clock::now() < deadline) { | ||
| 593 | + std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||
| 594 | + } | ||
| 595 | + ASSERT_TRUE(freeStarted); | ||
| 596 | + EXPECT_EQ(freedDebts.size(), 1u); | ||
| 597 | + | ||
| 598 | + { | ||
| 599 | + std::lock_guard<std::mutex> lock(gateMutex); | ||
| 600 | + gateOpen = true; | ||
| 601 | + } | ||
| 602 | + gateCv.notify_all(); | ||
| 603 | + ubse::task_executor::MockWaitExecutorIdle(); | ||
| 604 | + | ||
| 605 | + std::set<std::string> freedSet(freedDebts.begin(), freedDebts.end()); | ||
| 606 | + EXPECT_EQ(freedSet, std::set<std::string>({"debt-a", "debt-b"})); | ||
| 607 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 2u); | ||
| 608 | + | ||
| 609 | + ubse::task_executor::MockSetExecutorAsync(false); | ||
| 610 | +} | ||
| 611 | + | ||
| 612 | +TEST_F(TestProcessMemPidDecision, OomDetectorNormalPoll) | ||
| 613 | +{ | ||
| 614 | + ProcessMemPidDecision::memAvailableReader = []() { | ||
| 615 | + return 200 * KB_PER_GB; | ||
| 616 | + }; | ||
| 617 | + | ||
| 618 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 619 | + EXPECT_EQ(decision.OomPollOnce(), 1000u); | ||
| 620 | + EXPECT_FALSE(decision.oomFastPoll_); | ||
| 621 | + EXPECT_EQ(decision.oomFastWindowCount_, 0u); | ||
| 622 | +} | ||
| 623 | + | ||
| 624 | +TEST_F(TestProcessMemPidDecision, OomDetectorFastPoll) | ||
| 625 | +{ | ||
| 626 | + ProcessMemPidDecision::memAvailableReader = []() { | ||
| 627 | + return 100 * KB_PER_GB; | ||
| 628 | + }; | ||
| 629 | + MockNodeFree(10); | ||
| 630 | + | ||
| 631 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 632 | + EXPECT_EQ(decision.OomPollOnce(), 200u); | ||
| 633 | + EXPECT_TRUE(decision.oomFastPoll_); | ||
| 634 | + EXPECT_EQ(decision.oomFastWindowCount_, 1u); | ||
| 635 | + EXPECT_EQ(decision.oomFastWindowMin_, 10 * GB); | ||
| 636 | +} | ||
| 637 | + | ||
| 638 | +TEST_F(TestProcessMemPidDecision, OomEmergencyBorrow) | ||
| 639 | +{ | ||
| 640 | + ProcessMemPidDecision::memAvailableReader = []() { | ||
| 641 | + return 3 * KB_PER_GB; | ||
| 642 | + }; | ||
| 643 | + AddManagedPid(1001, 10, 0.5, 4); | ||
| 644 | + | ||
| 645 | + int migrateCalls = 0; | ||
| 646 | + ProcessMemPidBridge::rmrsMigrateOut = [&migrateCalls](const std::vector<mempooling::smap::MigrateOutPayload>&, | ||
| 647 | + int) { | ||
| 648 | + ++migrateCalls; | ||
| 649 | + return 0; | ||
| 650 | + }; | ||
| 651 | + | ||
| 652 | + ProcessMemPidDecision::GetInstance().OomPollOnce(); | ||
| 653 | + | ||
| 654 | + EXPECT_EQ(migrateCalls, 1); | ||
| 655 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 656 | + auto it = snapshot.find(1001); | ||
| 657 | + ASSERT_NE(it, snapshot.end()); | ||
| 658 | + EXPECT_EQ(it->second.borrow.currentRemote, 2 * GB); | ||
| 659 | + EXPECT_EQ(it->second.borrow.slots.size(), 1u); | ||
| 660 | +} | ||
| 661 | + | ||
| 662 | +TEST_F(TestProcessMemPidDecision, OomEmergencyAllowsExistingDebt) | ||
| 663 | +{ | ||
| 664 | + ProcessMemPidDecision::memAvailableReader = []() { | ||
| 665 | + return 3 * KB_PER_GB; | ||
| 666 | + }; | ||
| 667 | + AddManagedPid(1001, 10, 0.5, 4, MakeBorrow(1, "debt-x")); | ||
| 668 | + | ||
| 669 | + int migrateCalls = 0; | ||
| 670 | + ProcessMemPidBridge::rmrsMigrateOut = [&migrateCalls](const std::vector<mempooling::smap::MigrateOutPayload>&, | ||
| 671 | + int) { | ||
| 672 | + ++migrateCalls; | ||
| 673 | + return 0; | ||
| 674 | + }; | ||
| 675 | + | ||
| 676 | + ProcessMemPidDecision::GetInstance().OomPollOnce(); | ||
| 677 | + | ||
| 678 | + EXPECT_EQ(migrateCalls, 1); | ||
| 679 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 680 | + auto it = snapshot.find(1001); | ||
| 681 | + ASSERT_NE(it, snapshot.end()); | ||
| 682 | + EXPECT_EQ(it->second.borrow.currentRemote, 2 * GB); | ||
| 683 | + EXPECT_EQ(it->second.borrow.slots.size(), 2u); | ||
| 684 | +} | ||
| 685 | + | ||
| 686 | +TEST_F(TestProcessMemPidDecision, OomLenderEmergencyNotify) | ||
| 687 | +{ | ||
| 688 | + ProcessMemPidDecision::memAvailableReader = []() { | ||
| 689 | + return 3 * KB_PER_GB; | ||
| 690 | + }; | ||
| 691 | + ubse::mem::controller::MockSetDebtInfos(MakeLentDebts({"NODE1", "NODE2"})); | ||
| 692 | + | ||
| 693 | + ProcessMemPidDecision::GetInstance().OomPollOnce(); | ||
| 694 | + | ||
| 695 | + EXPECT_EQ(ubse::com::MockGetRpcSendRecords().size(), 2u); | ||
| 696 | +} | ||
| 697 | + | ||
| 698 | +TEST_F(TestProcessMemPidDecision, OomActiveReturnTrigger) | ||
| 699 | +{ | ||
| 700 | + ProcessMemPidDecision::memAvailableReader = []() { | ||
| 701 | + return 100 * KB_PER_GB; | ||
| 702 | + }; | ||
| 703 | + MockNodeFree(60); | ||
| 704 | + AddManagedPid(1001, 10, 0.5, 4, MakeBorrow(2, "debt-a")); | ||
| 705 | + AddManagedPid(1002, 10, 0.5, 4, MakeBorrow(1, "debt-b")); | ||
| 706 | + | ||
| 707 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 708 | + for (int i = 0; i < 5; ++i) { | ||
| 709 | + EXPECT_EQ(decision.OomPollOnce(), 200u); | ||
| 710 | + } | ||
| 711 | + EXPECT_EQ(decision.oomFastWindowCount_, 5u); | ||
| 712 | + | ||
| 713 | + decision.OomPollOnce(); | ||
| 714 | + | ||
| 715 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 2); | ||
| 716 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), "debt-b"); | ||
| 717 | + EXPECT_EQ(decision.oomFastWindowCount_, 0u); | ||
| 718 | + | ||
| 719 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 720 | + for (pid_t pid : {1001, 1002}) { | ||
| 721 | + auto it = snapshot.find(pid); | ||
| 722 | + ASSERT_NE(it, snapshot.end()); | ||
| 723 | + EXPECT_EQ(it->second.borrow.currentRemote, 0u); | ||
| 724 | + EXPECT_TRUE(it->second.borrow.slots.empty()); | ||
| 725 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::IDLE); | ||
| 726 | + } | ||
| 727 | +} | ||
| 728 | + | ||
| 729 | +TEST_F(TestProcessMemPidDecision, OomActiveCounterReset) | ||
| 730 | +{ | ||
| 731 | + ProcessMemPidDecision::memAvailableReader = []() { | ||
| 732 | + return 100 * KB_PER_GB; | ||
| 733 | + }; | ||
| 734 | + MockNodeFree(60); | ||
| 735 | + AddManagedPid(1001, 10, 0.5, 4, MakeBorrow(1, "debt-x")); | ||
| 736 | + | ||
| 737 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 738 | + for (int i = 0; i < 3; ++i) { | ||
| 739 | + decision.OomPollOnce(); | ||
| 740 | + } | ||
| 741 | + MockNodeFree(30); | ||
| 742 | + decision.OomPollOnce(); | ||
| 743 | + EXPECT_EQ(decision.oomFastWindowMin_, 30 * GB); | ||
| 744 | + | ||
| 745 | + for (int i = 0; i < 2; ++i) { | ||
| 746 | + decision.OomPollOnce(); | ||
| 747 | + } | ||
| 748 | + EXPECT_EQ(decision.oomFastWindowCount_, 0u); | ||
| 749 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 0); | ||
| 750 | +} | ||
| 751 | + | ||
| 752 | +TEST_F(TestProcessMemPidDecision, OomActiveReturnSkipOversizeDebt) | ||
| 753 | +{ | ||
| 754 | + ProcessMemPidDecision::memAvailableReader = []() { | ||
| 755 | + return 100 * KB_PER_GB; | ||
| 756 | + }; | ||
| 757 | + MockNodeFree(55); | ||
| 758 | + AddManagedPid(1001, 10, 0.5, 4, MakeBorrow(8, "debt-big")); | ||
| 759 | + AddManagedPid(1002, 10, 0.5, 4, MakeBorrow(3, "debt-ok")); | ||
| 760 | + | ||
| 761 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 762 | + for (int i = 0; i < 6; ++i) { | ||
| 763 | + decision.OomPollOnce(); | ||
| 764 | + } | ||
| 765 | + | ||
| 766 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 1); | ||
| 767 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), "debt-ok"); | ||
| 768 | + | ||
| 769 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 770 | + auto big = snapshot.find(1001); | ||
| 771 | + ASSERT_NE(big, snapshot.end()); | ||
| 772 | + EXPECT_EQ(big->second.borrow.currentRemote, 8 * GB); | ||
| 773 | + auto ok = snapshot.find(1002); | ||
| 774 | + ASSERT_NE(ok, snapshot.end()); | ||
| 775 | + EXPECT_EQ(ok->second.borrow.currentRemote, 0u); | ||
| 776 | +} | ||
| 777 | + | ||
| 778 | +TEST_F(TestProcessMemPidDecision, OomActiveReturnDeleteFailRetriesUntilSuccess) | ||
| 779 | +{ | ||
| 780 | + ProcessMemPidDecision::memAvailableReader = []() { | ||
| 781 | + return 100 * KB_PER_GB; | ||
| 782 | + }; | ||
| 783 | + MockNodeFree(60); | ||
| 784 | + AddManagedPid(1001, 10, 0.5, 4, MakeBorrow(2, "debt-a")); | ||
| 785 | + ubse::mem::controller::MockSetNumaDeleteErrorOnce(UBSE_ERROR); | ||
| 786 | + | ||
| 787 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 788 | + for (int i = 0; i < 6; ++i) { | ||
| 789 | + decision.OomPollOnce(); | ||
| 790 | + } | ||
| 791 | + | ||
| 792 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 2); | ||
| 793 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 794 | + auto it = snapshot.find(1001); | ||
| 795 | + ASSERT_NE(it, snapshot.end()); | ||
| 796 | + EXPECT_EQ(it->second.borrow.currentRemote, 0u); | ||
| 797 | + EXPECT_TRUE(it->second.borrow.slots.empty()); | ||
| 798 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::IDLE); | ||
| 799 | + | ||
| 800 | + for (int i = 0; i < 6; ++i) { | ||
| 801 | + decision.OomPollOnce(); | ||
| 802 | + } | ||
| 803 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 2); | ||
| 804 | +} | ||
| 805 | + | ||
| 806 | +TEST_F(TestProcessMemPidDecision, OomActiveReturnNotExistCleansLedger) | ||
| 807 | +{ | ||
| 808 | + ProcessMemPidDecision::memAvailableReader = []() { | ||
| 809 | + return 100 * KB_PER_GB; | ||
| 810 | + }; | ||
| 811 | + MockNodeFree(60); | ||
| 812 | + AddManagedPid(1001, 10, 0.5, 4, MakeBorrow(2, "debt-a")); | ||
| 813 | + ubse::mem::controller::MockSetNumaDeleteError(UBSE_ERR_NOT_EXIST); | ||
| 814 | + | ||
| 815 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 816 | + for (int i = 0; i < 6; ++i) { | ||
| 817 | + decision.OomPollOnce(); | ||
| 818 | + } | ||
| 819 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 1); | ||
| 820 | + | ||
| 821 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 822 | + auto it = snapshot.find(1001); | ||
| 823 | + ASSERT_NE(it, snapshot.end()); | ||
| 824 | + EXPECT_EQ(it->second.borrow.currentRemote, 0u); | ||
| 825 | + EXPECT_TRUE(it->second.borrow.slots.empty()); | ||
| 826 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::IDLE); | ||
| 827 | +} | ||
| 828 | + | ||
| 829 | +TEST_F(TestProcessMemPidDecision, OomRearrangeMigratedBigFirst) | ||
| 830 | +{ | ||
| 831 | + const pid_t pid = 15980; | ||
| 832 | + BorrowState borrow; | ||
| 833 | + BorrowSlot big; | ||
| 834 | + big.debtId = "debt-big"; | ||
| 835 | + big.capacity = 8 * GB; | ||
| 836 | + big.migratedBytes = 1 * GB; | ||
| 837 | + big.remoteNumaId = 5; | ||
| 838 | + big.status = BorrowSlotStatus::COMPLETED; | ||
| 839 | + BorrowSlot mid; | ||
| 840 | + mid.debtId = "debt-mid"; | ||
| 841 | + mid.capacity = 4 * GB; | ||
| 842 | + mid.migratedBytes = 4 * GB; | ||
| 843 | + mid.remoteNumaId = 5; | ||
| 844 | + mid.status = BorrowSlotStatus::COMPLETED; | ||
| 845 | + BorrowSlot sml; | ||
| 846 | + sml.debtId = "debt-sml"; | ||
| 847 | + sml.capacity = 2 * GB; | ||
| 848 | + sml.migratedBytes = 2 * GB; | ||
| 849 | + sml.remoteNumaId = 5; | ||
| 850 | + sml.status = BorrowSlotStatus::COMPLETED; | ||
| 851 | + BorrowSlot numa9; | ||
| 852 | + numa9.debtId = "debt-n9"; | ||
| 853 | + numa9.capacity = 5 * GB; | ||
| 854 | + numa9.migratedBytes = 3 * GB; | ||
| 855 | + numa9.remoteNumaId = 9; | ||
| 856 | + numa9.status = BorrowSlotStatus::COMPLETED; | ||
| 857 | + BorrowSlot inFlight; | ||
| 858 | + inFlight.debtId = "debt-inflight"; | ||
| 859 | + inFlight.capacity = 3 * GB; | ||
| 860 | + inFlight.migratedBytes = 2 * GB; | ||
| 861 | + inFlight.remoteNumaId = 5; | ||
| 862 | + inFlight.status = BorrowSlotStatus::BORROWING; | ||
| 863 | + BorrowSlot returning; | ||
| 864 | + returning.debtId = "debt-ret"; | ||
| 865 | + returning.capacity = 6 * GB; | ||
| 866 | + returning.migratedBytes = 6 * GB; | ||
| 867 | + returning.remoteNumaId = 5; | ||
| 868 | + returning.status = BorrowSlotStatus::COMPLETED; | ||
| 869 | + returning.returnStatus = ReturnStatus::RETURNING; | ||
| 870 | + borrow.slots = {big, mid, sml, numa9, inFlight, returning}; | ||
| 871 | + borrow.currentRemote = 16 * GB; | ||
| 872 | + AddManagedPid(pid, 32, 0.5, 16, borrow); | ||
| 873 | + | ||
| 874 | + ProcessMemPidDecision::GetInstance().OomRearrangeMigrated(); | ||
| 875 | + | ||
| 876 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 877 | + auto it = snapshot.find(pid); | ||
| 878 | + ASSERT_NE(it, snapshot.end()); | ||
| 879 | + const auto& slots = it->second.borrow.slots; | ||
| 880 | + ASSERT_EQ(slots.size(), 6u); | ||
| 881 | + auto mig = [&slots](const std::string& debtId) -> uint64_t { | ||
| 882 | + for (const auto& s : slots) { | ||
| 883 | + if (s.debtId == debtId) { | ||
| 884 | + return s.migratedBytes; | ||
| 885 | + } | ||
| 886 | + } | ||
| 887 | + return ~0ULL; | ||
| 888 | + }; | ||
| 889 | + EXPECT_EQ(mig("debt-big"), 7 * GB); | ||
| 890 | + EXPECT_EQ(mig("debt-mid"), 0u); | ||
| 891 | + EXPECT_EQ(mig("debt-sml"), 0u); | ||
| 892 | + EXPECT_EQ(mig("debt-n9"), 3 * GB); | ||
| 893 | + EXPECT_EQ(mig("debt-inflight"), 2 * GB); | ||
| 894 | + EXPECT_EQ(mig("debt-ret"), 6 * GB); | ||
| 895 | + EXPECT_EQ(it->second.borrow.currentRemote, 16 * GB); | ||
| 896 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::BORROWED); | ||
| 897 | +} | ||
| 898 | + | ||
| 899 | +TEST_F(TestProcessMemPidDecision, OomActiveReturnUsesMigratedBytes) | ||
| 900 | +{ | ||
| 901 | + ProcessMemPidDecision::memAvailableReader = []() { | ||
| 902 | + return 100 * KB_PER_GB; | ||
| 903 | + }; | ||
| 904 | + MockNodeFree(55); | ||
| 905 | + const pid_t pid = 1003; | ||
| 906 | + BorrowState borrow; | ||
| 907 | + BorrowSlot big; | ||
| 908 | + big.debtId = "debt-big"; | ||
| 909 | + big.capacity = 8 * GB; | ||
| 910 | + big.migratedBytes = 8 * GB; | ||
| 911 | + big.remoteNumaId = 5; | ||
| 912 | + big.status = BorrowSlotStatus::COMPLETED; | ||
| 913 | + BorrowSlot mid; | ||
| 914 | + mid.debtId = "debt-mid"; | ||
| 915 | + mid.capacity = 4 * GB; | ||
| 916 | + mid.migratedBytes = 2 * GB; | ||
| 917 | + mid.remoteNumaId = 5; | ||
| 918 | + mid.status = BorrowSlotStatus::COMPLETED; | ||
| 919 | + BorrowSlot empty; | ||
| 920 | + empty.debtId = "debt-empty"; | ||
| 921 | + empty.capacity = 2 * GB; | ||
| 922 | + empty.migratedBytes = 0; | ||
| 923 | + empty.remoteNumaId = 5; | ||
| 924 | + empty.status = BorrowSlotStatus::COMPLETED; | ||
| 925 | + borrow.slots = {big, mid, empty}; | ||
| 926 | + borrow.currentRemote = 10 * GB; | ||
| 927 | + AddManagedPid(pid, 32, 0.5, 16, borrow); | ||
| 928 | + | ||
| 929 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 930 | + for (int i = 0; i < 6; ++i) { | ||
| 931 | + decision.OomPollOnce(); | ||
| 932 | + } | ||
| 933 | + | ||
| 934 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 2); | ||
| 935 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), "debt-empty"); | ||
| 936 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 937 | + auto it = snapshot.find(pid); | ||
| 938 | + ASSERT_NE(it, snapshot.end()); | ||
| 939 | + EXPECT_EQ(it->second.borrow.currentRemote, 8 * GB); | ||
| 940 | + ASSERT_EQ(it->second.borrow.slots.size(), 1u); | ||
| 941 | + EXPECT_EQ(it->second.borrow.slots[0].debtId, "debt-big"); | ||
| 942 | + EXPECT_EQ(it->second.borrow.slots[0].migratedBytes, 8 * GB); | ||
| 943 | +} | ||
| 944 | + | ||
| 945 | +TEST_F(TestProcessMemPidDecision, UpdateManagedPidSlotReturned) | ||
| 946 | +{ | ||
| 947 | + AddManagedPid(1001, 10, 0.5, 4, MakeBorrow(1, "debt-x")); | ||
| 948 | + auto& mgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 949 | + | ||
| 950 | + EXPECT_EQ(mgr.UpdateManagedPidSlotReturned(1001, "debt-x", 1 * GB), UBSE_OK); | ||
| 951 | + auto snapshot = mgr.GetManagedPidCacheSnapshot(); | ||
| 952 | + auto it = snapshot.find(1001); | ||
| 953 | + ASSERT_NE(it, snapshot.end()); | ||
| 954 | + EXPECT_EQ(it->second.borrow.currentRemote, 0u); | ||
| 955 | + EXPECT_TRUE(it->second.borrow.slots.empty()); | ||
| 956 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::IDLE); | ||
| 957 | + | ||
| 958 | + EXPECT_EQ(mgr.UpdateManagedPidSlotReturned(9999, "x", 1), UBSE_ERR_NOT_EXIST); | ||
| 959 | + EXPECT_EQ(mgr.UpdateManagedPidSlotReturned(1001, "unknown-debt", 1), UBSE_ERR_NOT_EXIST); | ||
| 960 | +} | ||
| 961 | + | ||
| 962 | +ubse::mem::controller::UbseNumaMemoryImportDebtInfo MakeImportDebt(const std::string& name, uint64_t size, | ||
| 963 | + int64_t remoteNuma, int32_t pid, int64_t startTime) | ||
| 964 | +{ | ||
| 965 | + ubse::mem::controller::UbseNumaMemoryImportDebtInfo debt; | ||
| 966 | + debt.name = name; | ||
| 967 | + debt.size = size; | ||
| 968 | + debt.remoteNumaId = remoteNuma; | ||
| 969 | + memset(debt.usrInfo, 0, sizeof(debt.usrInfo)); | ||
| 970 | + ProcessMemUsrInfo usr{}; | ||
| 971 | + usr.pluginId = UsrInfoPluginType::PROCESS_MEM; | ||
| 972 | + usr.pid = pid; | ||
| 973 | + usr.startTime = startTime; | ||
| 974 | + usr.srcNuma = 1; | ||
| 975 | + memcpy(debt.usrInfo, &usr, sizeof(usr)); | ||
| 976 | + return debt; | ||
| 977 | +} | ||
| 978 | + | ||
| 979 | +TEST_F(TestProcessMemPidDecision, RecoverBorrowBindToExistingPid) | ||
| 980 | +{ | ||
| 981 | + pid_t pid = getpid(); | ||
| 982 | + AddManagedPid(pid, 10, 0.5, 2); | ||
| 983 | + auto startTime = ProcessMemPidConfigManager::GetExactStartTime(pid); | ||
| 984 | + ASSERT_NE(startTime, 0u); | ||
| 985 | + | ||
| 986 | + ubse::mem::controller::MockSetImportDebtInfos( | ||
| 987 | + {MakeImportDebt("debt-recover", 1 * GB, 5, pid, static_cast<int64_t>(startTime))}); | ||
| 988 | + | ||
| 989 | + std::vector<std::string> freedDebts; | ||
| 990 | + ProcessMemPidBridge::rmrsFreeWithMigrate = [&freedDebts](const std::string& name) { | ||
| 991 | + freedDebts.push_back(name); | ||
| 992 | + return 0; | ||
| 993 | + }; | ||
| 994 | + | ||
| 995 | + ProcessMemPidDecision::GetInstance().RecoverBorrowFromObmm(); | ||
| 996 | + | ||
| 997 | + EXPECT_TRUE(freedDebts.empty()); | ||
| 998 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 999 | + auto it = snapshot.find(pid); | ||
| 1000 | + ASSERT_NE(it, snapshot.end()); | ||
| 1001 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::BORROWED); | ||
| 1002 | + ASSERT_EQ(it->second.borrow.slots.size(), 1u); | ||
| 1003 | + const auto& slot = it->second.borrow.slots[0]; | ||
| 1004 | + EXPECT_EQ(slot.debtId, "debt-recover"); | ||
| 1005 | + EXPECT_EQ(slot.capacity, 1 * GB); | ||
| 1006 | + EXPECT_EQ(slot.status, BorrowSlotStatus::COMPLETED); | ||
| 1007 | + EXPECT_EQ(slot.remoteNumaId, 5); | ||
| 1008 | + EXPECT_EQ(slot.srcNumaId, 1); | ||
| 1009 | + EXPECT_EQ(slot.migratedBytes, 0u); | ||
| 1010 | + EXPECT_EQ(it->second.borrow.currentRemote, 0u); | ||
| 1011 | +} | ||
| 1012 | + | ||
| 1013 | +TEST_F(TestProcessMemPidDecision, RecoverBorrowOrphanReleasedWhenPidMissing) | ||
| 1014 | +{ | ||
| 1015 | + ubse::mem::controller::MockSetImportDebtInfos({MakeImportDebt("debt-orphan", 1 * GB, 5, 1001, 123456)}); | ||
| 1016 | + | ||
| 1017 | + ProcessMemPidDecision::GetInstance().RecoverBorrowFromObmm(); | ||
| 1018 | + | ||
| 1019 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), "debt-orphan"); | ||
| 1020 | +} | ||
| 1021 | + | ||
| 1022 | +TEST_F(TestProcessMemPidDecision, RecoverBorrowOrphanRetriedUntilSuccess) | ||
| 1023 | +{ | ||
| 1024 | + ProcessMemPidDecision::GetInstance().returnRetryIntervalMs_ = 1; | ||
| 1025 | + ubse::mem::controller::MockSetNumaDeleteErrorOnce(UBSE_ERR_TIMEOUT); | ||
| 1026 | + ubse::mem::controller::MockSetImportDebtInfos({MakeImportDebt("debt-retry", 1 * GB, 5, 1003, 123456)}); | ||
| 1027 | + | ||
| 1028 | + ProcessMemPidDecision::GetInstance().RecoverBorrowFromObmm(); | ||
| 1029 | + | ||
| 1030 | + EXPECT_GE(ubse::mem::controller::MockGetNumaDeleteCallCount(), 2u); | ||
| 1031 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), "debt-retry"); | ||
| 1032 | +} | ||
| 1033 | + | ||
| 1034 | +TEST_F(TestProcessMemPidDecision, RecoverBorrowOrphanNotExistStopsRetry) | ||
| 1035 | +{ | ||
| 1036 | + ProcessMemPidDecision::GetInstance().returnRetryIntervalMs_ = 1; | ||
| 1037 | + ubse::mem::controller::MockSetNumaDeleteErrorOnce(UBSE_ERR_NOT_EXIST); | ||
| 1038 | + ubse::mem::controller::MockSetImportDebtInfos({MakeImportDebt("debt-gone", 1 * GB, 5, 1004, 123456)}); | ||
| 1039 | + | ||
| 1040 | + ProcessMemPidDecision::GetInstance().RecoverBorrowFromObmm(); | ||
| 1041 | + | ||
| 1042 | + EXPECT_EQ(ubse::mem::controller::MockGetNumaDeleteCallCount(), 1u); | ||
| 1043 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), "debt-gone"); | ||
| 1044 | +} | ||
| 1045 | + | ||
| 1046 | +TEST_F(TestProcessMemPidDecision, RecoverBorrowOrphanReleasedOnStartTimeMismatch) | ||
| 1047 | +{ | ||
| 1048 | + pid_t pid = getpid(); | ||
| 1049 | + AddManagedPid(pid, 10, 0.5, 2); | ||
| 1050 | + auto startTime = ProcessMemPidConfigManager::GetExactStartTime(pid); | ||
| 1051 | + ASSERT_NE(startTime, 0u); | ||
| 1052 | + | ||
| 1053 | + ubse::mem::controller::MockSetImportDebtInfos( | ||
| 1054 | + {MakeImportDebt("debt-stale", 1 * GB, 5, pid, static_cast<int64_t>(startTime) + 1)}); | ||
| 1055 | + | ||
| 1056 | + ProcessMemPidDecision::GetInstance().RecoverBorrowFromObmm(); | ||
| 1057 | + | ||
| 1058 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaDeleteName(), "debt-stale"); | ||
| 1059 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1060 | + EXPECT_TRUE(snapshot.at(pid).borrow.slots.empty()); | ||
| 1061 | +} | ||
| 1062 | + | ||
| 1063 | +TEST_F(TestProcessMemPidDecision, RecoverBorrowSkipsNonProcessMemUsrInfo) | ||
| 1064 | +{ | ||
| 1065 | + ubse::mem::controller::UbseNumaMemoryImportDebtInfo other; | ||
| 1066 | + other.name = "debt-other"; | ||
| 1067 | + other.size = 1 * GB; | ||
| 1068 | + other.remoteNumaId = 5; | ||
| 1069 | + memset(other.usrInfo, 0, sizeof(other.usrInfo)); | ||
| 1070 | + | ||
| 1071 | + ubse::mem::controller::UbseNumaMemoryImportDebtInfo badPid; | ||
| 1072 | + badPid.name = "debt-badpid"; | ||
| 1073 | + badPid.size = 1 * GB; | ||
| 1074 | + badPid.remoteNumaId = 5; | ||
| 1075 | + memset(badPid.usrInfo, 0, sizeof(badPid.usrInfo)); | ||
| 1076 | + ProcessMemUsrInfo usr{}; | ||
| 1077 | + usr.pluginId = UsrInfoPluginType::PROCESS_MEM; | ||
| 1078 | + usr.pid = -1; | ||
| 1079 | + memcpy(badPid.usrInfo, &usr, sizeof(usr)); | ||
| 1080 | + | ||
| 1081 | + ubse::mem::controller::MockSetImportDebtInfos({other, badPid}); | ||
| 1082 | + | ||
| 1083 | + ProcessMemPidDecision::GetInstance().RecoverBorrowFromObmm(); | ||
| 1084 | + | ||
| 1085 | + EXPECT_TRUE(ubse::mem::controller::MockGetLastNumaDeleteName().empty()); | ||
| 1086 | +} | ||
| 1087 | + | ||
| 1088 | +TEST_F(TestProcessMemPidDecision, RecoverSmapConfigUnavailable) | ||
| 1089 | +{ | ||
| 1090 | + ProcessMemPidBridge::rmrsProcessConfigQuery = {}; | ||
| 1091 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().RecoverSmapProcessConfig(), 0u); | ||
| 1092 | +} | ||
| 1093 | + | ||
| 1094 | +TEST_F(TestProcessMemPidDecision, RecoverSmapConfigReportsDirtyStates) | ||
| 1095 | +{ | ||
| 1096 | + pid_t pid = getpid(); | ||
| 1097 | + AddManagedPid(pid, 10, 0.5, 2, MakeBorrow(1, "debt-x")); | ||
| 1098 | + AddManagedPid(2001, 10, 0.5, 2); | ||
| 1099 | + ubse::mem::controller::MockSetImportDebtInfos({MakeImportDebt("debt-numa5", 1 * GB, 5, 1000, 0)}); | ||
| 1100 | + | ||
| 1101 | + std::set<int> queriedNumas; | ||
| 1102 | + ProcessMemPidBridge::rmrsProcessConfigQuery = | ||
| 1103 | + [&queriedNumas, pid](int nid, mempooling::smap::ProcessPayload* payloads, int capacity, int* realLen) { | ||
| 1104 | + queriedNumas.insert(nid); | ||
| 1105 | + if (nid != 5) { | ||
| 1106 | + *realLen = 0; | ||
| 1107 | + return 0; | ||
| 1108 | + } | ||
| 1109 | + mempooling::smap::ProcessPayload p1{}; | ||
| 1110 | + p1.pid = pid; | ||
| 1111 | + p1.scanType = 1; | ||
| 1112 | + p1.memSize = 1 * 1024 * 1024; | ||
| 1113 | + mempooling::smap::ProcessPayload p2{}; | ||
| 1114 | + p2.pid = 2002; | ||
| 1115 | + p2.scanType = 1; | ||
| 1116 | + p2.memSize = 2 * 1024 * 1024; | ||
| 1117 | + mempooling::smap::ProcessPayload p3{}; | ||
| 1118 | + p3.pid = 2001; | ||
| 1119 | + p3.scanType = 1; | ||
| 1120 | + p3.memSize = 1 * 1024 * 1024; | ||
| 1121 | + *realLen = 3; | ||
| 1122 | + payloads[0] = p1; | ||
| 1123 | + payloads[1] = p2; | ||
| 1124 | + payloads[2] = p3; | ||
| 1125 | + return 0; | ||
| 1126 | + }; | ||
| 1127 | + | ||
| 1128 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().RecoverSmapProcessConfig(), 2u); | ||
| 1129 | + EXPECT_EQ(queriedNumas, std::set<int>({5})); | ||
| 1130 | +} | ||
| 1131 | + | ||
| 1132 | +TEST_F(TestProcessMemPidDecision, RecoverSmapConfigQueryAllFail) | ||
| 1133 | +{ | ||
| 1134 | + ubse::mem::controller::MockSetImportDebtInfos({MakeImportDebt("debt-numa5", 1 * GB, 5, 1000, 0)}); | ||
| 1135 | + ProcessMemPidBridge::rmrsProcessConfigQuery = [](int, mempooling::smap::ProcessPayload*, int, int*) { | ||
| 1136 | + return -1; | ||
| 1137 | + }; | ||
| 1138 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().RecoverSmapProcessConfig(), 0u); | ||
| 1139 | +} | ||
| 1140 | + | ||
| 1141 | +TEST_F(TestProcessMemPidDecision, RecoverSmapConfigMultiRemoteNumaAccumulates) | ||
| 1142 | +{ | ||
| 1143 | + pid_t pid = getpid(); | ||
| 1144 | + AddManagedPid(pid, 10, 0.5, 2, MakeBorrow(1, "debt-x")); | ||
| 1145 | + ubse::mem::controller::MockSetImportDebtInfos( | ||
| 1146 | + {MakeImportDebt("debt-numa5", 1 * GB, 5, 1000, 0), MakeImportDebt("debt-numa7", 1 * GB, 7, 1000, 0)}); | ||
| 1147 | + ProcessMemPidBridge::rmrsProcessConfigQuery = [pid](int nid, mempooling::smap::ProcessPayload* payloads, int, | ||
| 1148 | + int* realLen) { | ||
| 1149 | + if (nid != 5 && nid != 7) { | ||
| 1150 | + *realLen = 0; | ||
| 1151 | + return 0; | ||
| 1152 | + } | ||
| 1153 | + mempooling::smap::ProcessPayload p{}; | ||
| 1154 | + p.pid = pid; | ||
| 1155 | + p.scanType = 1; | ||
| 1156 | + p.memSize = 1 * 1024 * 1024; | ||
| 1157 | + *realLen = 1; | ||
| 1158 | + payloads[0] = p; | ||
| 1159 | + return 0; | ||
| 1160 | + }; | ||
| 1161 | + | ||
| 1162 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().RecoverSmapProcessConfig(), 1u); | ||
| 1163 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1164 | + const auto& borrow = snapshot.at(pid).borrow; | ||
| 1165 | + ASSERT_EQ(borrow.slots.size(), 1u); | ||
| 1166 | + EXPECT_EQ(borrow.slots[0].migratedBytes, 1 * GB); | ||
| 1167 | + EXPECT_EQ(borrow.currentRemote, 1 * GB); | ||
| 1168 | + EXPECT_EQ(borrow.remoteNumaMigrated.size(), 1u); | ||
| 1169 | + EXPECT_EQ(borrow.remoteNumaMigrated.at(5), 1 * GB); | ||
| 1170 | +} | ||
| 1171 | + | ||
| 1172 | +TEST_F(TestProcessMemPidDecision, RecoverSmapConfigFillsAcrossMultipleSlots) | ||
| 1173 | +{ | ||
| 1174 | + pid_t pid = getpid(); | ||
| 1175 | + BorrowState borrow; | ||
| 1176 | + BorrowSlot s5; | ||
| 1177 | + s5.debtId = "debt-a"; | ||
| 1178 | + s5.migratedBytes = 1 * GB; | ||
| 1179 | + s5.capacity = 1 * GB; | ||
| 1180 | + s5.status = BorrowSlotStatus::COMPLETED; | ||
| 1181 | + s5.remoteNumaId = 5; | ||
| 1182 | + BorrowSlot s7 = s5; | ||
| 1183 | + s7.debtId = "debt-b"; | ||
| 1184 | + s7.remoteNumaId = 7; | ||
| 1185 | + borrow.slots = {s5, s7}; | ||
| 1186 | + borrow.currentRemote = 2 * GB; | ||
| 1187 | + AddManagedPid(pid, 10, 0.5, 2, borrow); | ||
| 1188 | + ubse::mem::controller::MockSetImportDebtInfos( | ||
| 1189 | + {MakeImportDebt("debt-numa5", 1 * GB, 5, 1000, 0), MakeImportDebt("debt-numa7", 1 * GB, 7, 1000, 0)}); | ||
| 1190 | + | ||
| 1191 | + ProcessMemPidBridge::rmrsProcessConfigQuery = [pid](int nid, mempooling::smap::ProcessPayload* payloads, int, | ||
| 1192 | + int* realLen) { | ||
| 1193 | + if (nid != 5 && nid != 7) { | ||
| 1194 | + *realLen = 0; | ||
| 1195 | + return 0; | ||
| 1196 | + } | ||
| 1197 | + mempooling::smap::ProcessPayload p{}; | ||
| 1198 | + p.pid = pid; | ||
| 1199 | + p.scanType = 1; | ||
| 1200 | + p.memSize = 1 * 1024 * 1024; | ||
| 1201 | + *realLen = 1; | ||
| 1202 | + payloads[0] = p; | ||
| 1203 | + return 0; | ||
| 1204 | + }; | ||
| 1205 | + | ||
| 1206 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().RecoverSmapProcessConfig(), 0u); | ||
| 1207 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1208 | + const auto& borrowAfter = snapshot.at(pid).borrow; | ||
| 1209 | + ASSERT_EQ(borrowAfter.slots.size(), 2u); | ||
| 1210 | + EXPECT_EQ(borrowAfter.slots[0].migratedBytes, 1 * GB); | ||
| 1211 | + EXPECT_EQ(borrowAfter.slots[1].migratedBytes, 1 * GB); | ||
| 1212 | + EXPECT_EQ(borrowAfter.currentRemote, 2 * GB); | ||
| 1213 | + EXPECT_EQ(borrowAfter.remoteNumaMigrated.at(5), 1 * GB); | ||
| 1214 | + EXPECT_EQ(borrowAfter.remoteNumaMigrated.at(7), 1 * GB); | ||
| 1215 | +} | ||
| 1216 | + | ||
| 1217 | +TEST_F(TestProcessMemPidDecision, RecoverSmapConfigFillsSlotsByCapacityInOrder) | ||
| 1218 | +{ | ||
| 1219 | + pid_t pid = getpid(); | ||
| 1220 | + BorrowState borrow; | ||
| 1221 | + BorrowSlot s1; | ||
| 1222 | + s1.debtId = "debt-big"; | ||
| 1223 | + s1.migratedBytes = 2 * GB; | ||
| 1224 | + s1.capacity = 2 * GB; | ||
| 1225 | + s1.status = BorrowSlotStatus::COMPLETED; | ||
| 1226 | + s1.remoteNumaId = 5; | ||
| 1227 | + BorrowSlot s2 = s1; | ||
| 1228 | + s2.debtId = "debt-small"; | ||
| 1229 | + s2.migratedBytes = 1 * GB; | ||
| 1230 | + s2.capacity = 1 * GB; | ||
| 1231 | + borrow.slots = {s1, s2}; | ||
| 1232 | + borrow.currentRemote = 3 * GB; | ||
| 1233 | + AddManagedPid(pid, 10, 0.5, 2, borrow); | ||
| 1234 | + ubse::mem::controller::MockSetImportDebtInfos({MakeImportDebt("debt-numa5", 1 * GB, 5, 1000, 0)}); | ||
| 1235 | + | ||
| 1236 | + ProcessMemPidBridge::rmrsProcessConfigQuery = [pid](int nid, mempooling::smap::ProcessPayload* payloads, int, | ||
| 1237 | + int* realLen) { | ||
| 1238 | + if (nid != 5) { | ||
| 1239 | + *realLen = 0; | ||
| 1240 | + return 0; | ||
| 1241 | + } | ||
| 1242 | + mempooling::smap::ProcessPayload p{}; | ||
| 1243 | + p.pid = pid; | ||
| 1244 | + p.scanType = 1; | ||
| 1245 | + p.memSize = 1536 * 1024; | ||
| 1246 | + *realLen = 1; | ||
| 1247 | + payloads[0] = p; | ||
| 1248 | + return 0; | ||
| 1249 | + }; | ||
| 1250 | + | ||
| 1251 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().RecoverSmapProcessConfig(), 0u); | ||
| 1252 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1253 | + const auto& borrowAfter = snapshot.at(pid).borrow; | ||
| 1254 | + ASSERT_EQ(borrowAfter.slots.size(), 2u); | ||
| 1255 | + EXPECT_EQ(borrowAfter.slots[0].migratedBytes, 1536 * 1024 * 1024u); | ||
| 1256 | + EXPECT_EQ(borrowAfter.slots[1].migratedBytes, 0u); | ||
| 1257 | + EXPECT_EQ(borrowAfter.currentRemote, 1536 * 1024 * 1024u); | ||
| 1258 | +} | ||
| 1259 | + | ||
| 1260 | +TEST_F(TestProcessMemPidDecision, RecoverSmapConfigFillsRemainderWhenCapacityExceeded) | ||
| 1261 | +{ | ||
| 1262 | + pid_t pid = getpid(); | ||
| 1263 | + BorrowState borrow; | ||
| 1264 | + BorrowSlot slot; | ||
| 1265 | + slot.debtId = "debt-2g"; | ||
| 1266 | + slot.capacity = 2 * GB; | ||
| 1267 | + slot.migratedBytes = slot.capacity; | ||
| 1268 | + slot.status = BorrowSlotStatus::COMPLETED; | ||
| 1269 | + slot.remoteNumaId = 5; | ||
| 1270 | + borrow.slots = {slot}; | ||
| 1271 | + borrow.currentRemote = slot.capacity; | ||
| 1272 | + AddManagedPid(pid, 10, 0.5, 2, borrow); | ||
| 1273 | + ubse::mem::controller::MockSetImportDebtInfos({MakeImportDebt("debt-numa5", 2 * GB, 5, 1000, 0)}); | ||
| 1274 | + | ||
| 1275 | + ProcessMemPidBridge::rmrsProcessConfigQuery = [pid](int nid, mempooling::smap::ProcessPayload* payloads, int, | ||
| 1276 | + int* realLen) { | ||
| 1277 | + if (nid != 5) { | ||
| 1278 | + *realLen = 0; | ||
| 1279 | + return 0; | ||
| 1280 | + } | ||
| 1281 | + mempooling::smap::ProcessPayload p{}; | ||
| 1282 | + p.pid = pid; | ||
| 1283 | + p.scanType = 1; | ||
| 1284 | + p.memSize = 1536 * 1024; | ||
| 1285 | + *realLen = 1; | ||
| 1286 | + payloads[0] = p; | ||
| 1287 | + return 0; | ||
| 1288 | + }; | ||
| 1289 | + | ||
| 1290 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().RecoverSmapProcessConfig(), 0u); | ||
| 1291 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1292 | + const auto& borrowAfter = snapshot.at(pid).borrow; | ||
| 1293 | + ASSERT_EQ(borrowAfter.slots.size(), 1u); | ||
| 1294 | + EXPECT_EQ(borrowAfter.slots[0].migratedBytes, 1536 * 1024 * 1024u); | ||
| 1295 | + EXPECT_EQ(borrowAfter.currentRemote, 1536 * 1024 * 1024u); | ||
| 1296 | + EXPECT_EQ(borrowAfter.remoteNumaMigrated.at(5), 1536 * 1024 * 1024u); | ||
| 1297 | +} | ||
| 1298 | + | ||
| 1299 | +TEST_F(TestProcessMemPidDecision, RecoverSmapConfigIgnoresHamProcesses) | ||
| 1300 | +{ | ||
| 1301 | + ubse::mem::controller::MockSetImportDebtInfos({MakeImportDebt("debt-numa5", 1 * GB, 5, 1000, 0)}); | ||
| 1302 | + ProcessMemPidBridge::rmrsProcessConfigQuery = [](int nid, mempooling::smap::ProcessPayload* payloads, int, | ||
| 1303 | + int* realLen) { | ||
| 1304 | + if (nid != 5) { | ||
| 1305 | + *realLen = 0; | ||
| 1306 | + return 0; | ||
| 1307 | + } | ||
| 1308 | + mempooling::smap::ProcessPayload p{}; | ||
| 1309 | + p.pid = 3001; | ||
| 1310 | + p.scanType = 0; | ||
| 1311 | + p.memSize = 4 * 1024 * 1024; | ||
| 1312 | + *realLen = 1; | ||
| 1313 | + payloads[0] = p; | ||
| 1314 | + return 0; | ||
| 1315 | + }; | ||
| 1316 | + | ||
| 1317 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().RecoverSmapProcessConfig(), 0u); | ||
| 1318 | +} | ||
| 1319 | + | ||
| 1320 | +TEST_F(TestProcessMemPidDecision, MultiCycleBorrowingLedgerVisibleAndNoDuplicateBorrow) | ||
| 1321 | +{ | ||
| 1322 | + const pid_t pid = 15970; | ||
| 1323 | + constexpr uint64_t maxGb = 32; | ||
| 1324 | + constexpr double ratio = 0.5; | ||
| 1325 | + constexpr uint64_t vmRssGb = 20; | ||
| 1326 | + AddManagedPid(pid, maxGb, ratio, vmRssGb); | ||
| 1327 | + | ||
| 1328 | + ProcessMemPidBridge::rmrsMigrateOut = [](const std::vector<mempooling::smap::MigrateOutPayload>&, int) { | ||
| 1329 | + return 0; | ||
| 1330 | + }; | ||
| 1331 | + | ||
| 1332 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 1333 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 1334 | + | ||
| 1335 | + ubse::task_executor::MockSetExecutorAsync(true); | ||
| 1336 | + ubse::mem::controller::MockBlockNumaCreate(); | ||
| 1337 | + | ||
| 1338 | + MockNodeFree(10); | ||
| 1339 | + std::thread round1Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1340 | + round1Thread.join(); | ||
| 1341 | + ASSERT_TRUE(ubse::mem::controller::MockWaitNumaCreateEntered(5000)); | ||
| 1342 | + | ||
| 1343 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1344 | + auto it = snapshot.find(pid); | ||
| 1345 | + ASSERT_NE(it, snapshot.end()); | ||
| 1346 | + ASSERT_EQ(it->second.borrow.slots.size(), 1u); | ||
| 1347 | + EXPECT_EQ(it->second.borrow.slots[0].migratedBytes, 10 * GB); | ||
| 1348 | + EXPECT_FALSE(it->second.borrow.slots[0].debtId.empty()); | ||
| 1349 | + EXPECT_EQ(it->second.borrow.slots[0].status, BorrowSlotStatus::BORROWING); | ||
| 1350 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::BORROWING); | ||
| 1351 | + std::string firstDebtId = it->second.borrow.slots[0].debtId; | ||
| 1352 | + EXPECT_EQ(decision.GetPendingBorrowTotal(), 10 * GB); | ||
| 1353 | + | ||
| 1354 | + std::thread round2Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1355 | + round2Thread.join(); | ||
| 1356 | + snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1357 | + it = snapshot.find(pid); | ||
| 1358 | + ASSERT_NE(it, snapshot.end()); | ||
| 1359 | + ASSERT_EQ(it->second.borrow.slots.size(), 1u); | ||
| 1360 | + EXPECT_EQ(it->second.borrow.slots[0].debtId, firstDebtId); | ||
| 1361 | + EXPECT_EQ(it->second.borrow.slots[0].status, BorrowSlotStatus::BORROWING); | ||
| 1362 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaCreateName(), firstDebtId); | ||
| 1363 | + EXPECT_EQ(decision.GetPendingBorrowTotal(), 10 * GB); | ||
| 1364 | + | ||
| 1365 | + ubse::mem::controller::MockReleaseNumaCreate(); | ||
| 1366 | + ubse::task_executor::MockWaitExecutorIdle(); | ||
| 1367 | + snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1368 | + it = snapshot.find(pid); | ||
| 1369 | + ASSERT_NE(it, snapshot.end()); | ||
| 1370 | + ASSERT_EQ(it->second.borrow.slots.size(), 1u); | ||
| 1371 | + EXPECT_EQ(it->second.borrow.slots[0].debtId, firstDebtId); | ||
| 1372 | + EXPECT_EQ(it->second.borrow.slots[0].migratedBytes, 10 * GB); | ||
| 1373 | + EXPECT_EQ(it->second.borrow.slots[0].capacity, 10 * GB); | ||
| 1374 | + EXPECT_EQ(it->second.borrow.slots[0].remoteNumaId, 3); | ||
| 1375 | + EXPECT_EQ(it->second.borrow.slots[0].status, BorrowSlotStatus::COMPLETED); | ||
| 1376 | + EXPECT_EQ(it->second.borrow.currentRemote, 10 * GB); | ||
| 1377 | + EXPECT_EQ(it->second.processStatus, ProcessStatus::BORROWED); | ||
| 1378 | + EXPECT_EQ(decision.GetPendingBorrowTotal(), 0u); | ||
| 1379 | + | ||
| 1380 | + std::thread round3Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1381 | + round3Thread.join(); | ||
| 1382 | + snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1383 | + it = snapshot.find(pid); | ||
| 1384 | + ASSERT_NE(it, snapshot.end()); | ||
| 1385 | + ASSERT_EQ(it->second.borrow.slots.size(), 1u); | ||
| 1386 | + EXPECT_EQ(it->second.borrow.slots[0].debtId, firstDebtId); | ||
| 1387 | + EXPECT_EQ(it->second.borrow.slots[0].status, BorrowSlotStatus::COMPLETED); | ||
| 1388 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaCreateName(), firstDebtId); | ||
| 1389 | + EXPECT_EQ(it->second.borrow.currentRemote, 10 * GB); | ||
| 1390 | + | ||
| 1391 | + ubse::task_executor::MockSetExecutorAsync(false); | ||
| 1392 | +} | ||
| 1393 | + | ||
| 1394 | +TEST_F(TestProcessMemPidDecision, NodeFreeChangeConsidersInFlightBorrow) | ||
| 1395 | +{ | ||
| 1396 | + const pid_t pid = 15970; | ||
| 1397 | + constexpr uint64_t maxGb = 32; | ||
| 1398 | + constexpr double ratio = 0.5; | ||
| 1399 | + AddManagedPid(pid, maxGb, ratio, 40); | ||
| 1400 | + | ||
| 1401 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 1402 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 1403 | + | ||
| 1404 | + ubse::task_executor::MockSetExecutorAsync(true); | ||
| 1405 | + ubse::mem::controller::MockBlockNumaCreate(); | ||
| 1406 | + | ||
| 1407 | + MockNodeFree(40); | ||
| 1408 | + std::thread round1Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1409 | + round1Thread.join(); | ||
| 1410 | + ASSERT_TRUE(ubse::mem::controller::MockWaitNumaCreateEntered(5000)); | ||
| 1411 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1412 | + ASSERT_EQ(snapshot.at(pid).borrow.slots.size(), 1u); | ||
| 1413 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].migratedBytes, 10 * GB); | ||
| 1414 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].status, BorrowSlotStatus::BORROWING); | ||
| 1415 | + std::string firstDebtId = snapshot.at(pid).borrow.slots[0].debtId; | ||
| 1416 | + EXPECT_EQ(decision.GetPendingBorrowTotal(), 10 * GB); | ||
| 1417 | + | ||
| 1418 | + MockNodeFree(35); | ||
| 1419 | + std::thread round2Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1420 | + round2Thread.join(); | ||
| 1421 | + auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5); | ||
| 1422 | + while (std::chrono::steady_clock::now() < deadline) { | ||
| 1423 | + auto snap = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1424 | + if (snap.at(pid).borrow.slots.size() >= 2 && | ||
| 1425 | + ubse::mem::controller::MockGetLastNumaCreateName() != firstDebtId) { | ||
| 1426 | + break; | ||
| 1427 | + } | ||
| 1428 | + std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||
| 1429 | + } | ||
| 1430 | + snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1431 | + ASSERT_EQ(snapshot.at(pid).borrow.slots.size(), 2u); | ||
| 1432 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].migratedBytes, 10 * GB); | ||
| 1433 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].status, BorrowSlotStatus::BORROWING); | ||
| 1434 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].migratedBytes, 5 * GB); | ||
| 1435 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].status, BorrowSlotStatus::BORROWING); | ||
| 1436 | + EXPECT_FALSE(snapshot.at(pid).borrow.slots[1].debtId.empty()); | ||
| 1437 | + EXPECT_EQ(decision.GetPendingBorrowTotal(), 15 * GB); | ||
| 1438 | + | ||
| 1439 | + ubse::mem::controller::MockReleaseNumaCreate(); | ||
| 1440 | + deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5); | ||
| 1441 | + while (std::chrono::steady_clock::now() < deadline) { | ||
| 1442 | + auto snap = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1443 | + if (snap.at(pid).borrow.slots.size() >= 2 && | ||
| 1444 | + snap.at(pid).borrow.slots[0].status == BorrowSlotStatus::COMPLETED && | ||
| 1445 | + snap.at(pid).borrow.slots[1].status == BorrowSlotStatus::COMPLETED) { | ||
| 1446 | + break; | ||
| 1447 | + } | ||
| 1448 | + std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||
| 1449 | + } | ||
| 1450 | + snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1451 | + ASSERT_EQ(snapshot.at(pid).borrow.slots.size(), 2u); | ||
| 1452 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].status, BorrowSlotStatus::COMPLETED); | ||
| 1453 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].status, BorrowSlotStatus::COMPLETED); | ||
| 1454 | + EXPECT_EQ(snapshot.at(pid).borrow.currentRemote, 15 * GB); | ||
| 1455 | + EXPECT_EQ(decision.GetPendingBorrowTotal(), 0u); | ||
| 1456 | + | ||
| 1457 | + MockNodeFree(60); | ||
| 1458 | + std::thread round3Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1459 | + round3Thread.join(); | ||
| 1460 | + ubse::task_executor::MockWaitExecutorIdle(); | ||
| 1461 | + snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1462 | + ASSERT_EQ(snapshot.at(pid).borrow.slots.size(), 2u); | ||
| 1463 | + EXPECT_EQ(snapshot.at(pid).borrow.currentRemote, 15 * GB); | ||
| 1464 | + EXPECT_EQ(decision.GetPendingBorrowTotal(), 0u); | ||
| 1465 | + | ||
| 1466 | + ubse::task_executor::MockSetExecutorAsync(false); | ||
| 1467 | +} | ||
| 1468 | + | ||
| 1469 | +TEST_F(TestProcessMemPidDecision, NextCycleBorrowsFullAmountNoReuse) | ||
| 1470 | +{ | ||
| 1471 | + const pid_t pid = 15970; | ||
| 1472 | + constexpr uint64_t maxGb = 32; | ||
| 1473 | + constexpr double ratio = 0.5; | ||
| 1474 | + AddManagedPid(pid, maxGb, ratio, 20); | ||
| 1475 | + | ||
| 1476 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 1477 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 1478 | + ubse::task_executor::MockSetExecutorAsync(true); | ||
| 1479 | + | ||
| 1480 | + constexpr uint64_t gb2_5 = 2 * GB + GB / 2; | ||
| 1481 | + ubse::mem::controller::MockSetNumaCreateDesc(3, 7, 3 * GB); | ||
| 1482 | + MockNodeFreeBytes(THRESHOLD_BYTES - gb2_5); | ||
| 1483 | + std::thread round1Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1484 | + round1Thread.join(); | ||
| 1485 | + ubse::task_executor::MockWaitExecutorIdle(); | ||
| 1486 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1487 | + ASSERT_EQ(snapshot.at(pid).borrow.slots.size(), 1u); | ||
| 1488 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].capacity, 3 * GB); | ||
| 1489 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].migratedBytes, gb2_5); | ||
| 1490 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].remoteNumaId, 3); | ||
| 1491 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].status, BorrowSlotStatus::COMPLETED); | ||
| 1492 | + EXPECT_EQ(snapshot.at(pid).borrow.currentRemote, gb2_5); | ||
| 1493 | + ASSERT_EQ(ubse::smap::MockGetMigrateCallCount(), 1u); | ||
| 1494 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 3), gb2_5 / 1024); | ||
| 1495 | + std::string firstDebtId = snapshot.at(pid).borrow.slots[0].debtId; | ||
| 1496 | + | ||
| 1497 | + ubse::mem::controller::MockSetNumaCreateDesc(9, 11, 0); | ||
| 1498 | + ubse::mem::controller::MockBlockNumaCreate(); | ||
| 1499 | + MockNodeFreeBytes(THRESHOLD_BYTES - 4 * GB - GB / 2); | ||
| 1500 | + std::thread round2Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1501 | + round2Thread.join(); | ||
| 1502 | + ASSERT_TRUE(ubse::mem::controller::MockWaitNumaCreateEntered(5000)); | ||
| 1503 | + | ||
| 1504 | + ASSERT_EQ(ubse::smap::MockGetMigrateCallCount(), 1u); | ||
| 1505 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 3), gb2_5 / 1024); | ||
| 1506 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaCreateSize(), 4 * GB + GB / 2); | ||
| 1507 | + snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1508 | + ASSERT_EQ(snapshot.at(pid).borrow.slots.size(), 2u); | ||
| 1509 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].status, BorrowSlotStatus::COMPLETED); | ||
| 1510 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].capacity, 3 * GB); | ||
| 1511 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].migratedBytes, gb2_5); | ||
| 1512 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].status, BorrowSlotStatus::BORROWING); | ||
| 1513 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].migratedBytes, 4 * GB + GB / 2); | ||
| 1514 | + EXPECT_FALSE(snapshot.at(pid).borrow.slots[1].debtId.empty()); | ||
| 1515 | + EXPECT_NE(snapshot.at(pid).borrow.slots[1].debtId, firstDebtId); | ||
| 1516 | + EXPECT_EQ(snapshot.at(pid).processStatus, ProcessStatus::BORROWING); | ||
| 1517 | + EXPECT_EQ(decision.GetPendingBorrowTotal(), 4 * GB + GB / 2); | ||
| 1518 | + | ||
| 1519 | + ubse::mem::controller::MockReleaseNumaCreate(); | ||
| 1520 | + ubse::task_executor::MockWaitExecutorIdle(); | ||
| 1521 | + ASSERT_EQ(ubse::smap::MockGetMigrateCallCount(), 2u); | ||
| 1522 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 3), gb2_5 / 1024); | ||
| 1523 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 9), (4 * GB + GB / 2) / 1024); | ||
| 1524 | + snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1525 | + ASSERT_EQ(snapshot.at(pid).borrow.slots.size(), 2u); | ||
| 1526 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].migratedBytes, gb2_5); | ||
| 1527 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].status, BorrowSlotStatus::COMPLETED); | ||
| 1528 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].capacity, 4 * GB + GB / 2); | ||
| 1529 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].migratedBytes, 4 * GB + GB / 2); | ||
| 1530 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].remoteNumaId, 9); | ||
| 1531 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].status, BorrowSlotStatus::COMPLETED); | ||
| 1532 | + EXPECT_EQ(snapshot.at(pid).borrow.currentRemote, gb2_5 + 4 * GB + GB / 2); | ||
| 1533 | + EXPECT_EQ(decision.GetPendingBorrowTotal(), 0u); | ||
| 1534 | + EXPECT_EQ(snapshot.at(pid).processStatus, ProcessStatus::BORROWED); | ||
| 1535 | + | ||
| 1536 | + ubse::task_executor::MockSetExecutorAsync(false); | ||
| 1537 | +} | ||
| 1538 | + | ||
| 1539 | +TEST_F(TestProcessMemPidDecision, ReturningSlotExcludedFromMigrateTargets) | ||
| 1540 | +{ | ||
| 1541 | + const pid_t pid = 15970; | ||
| 1542 | + constexpr uint64_t maxGb = 32; | ||
| 1543 | + constexpr double ratio = 0.5; | ||
| 1544 | + constexpr uint64_t gb2_5 = 2 * GB + GB / 2; | ||
| 1545 | + AddManagedPid(pid, maxGb, ratio, 20); | ||
| 1546 | + | ||
| 1547 | + BorrowState borrow; | ||
| 1548 | + BorrowSlot slot; | ||
| 1549 | + slot.debtId = "debt-returning"; | ||
| 1550 | + slot.capacity = 3 * GB; | ||
| 1551 | + slot.migratedBytes = gb2_5; | ||
| 1552 | + slot.remoteNumaId = 3; | ||
| 1553 | + slot.status = BorrowSlotStatus::COMPLETED; | ||
| 1554 | + slot.returnStatus = ReturnStatus::RETURNING; | ||
| 1555 | + borrow.currentRemote = gb2_5; | ||
| 1556 | + borrow.slots.push_back(slot); | ||
| 1557 | + AddManagedPid(pid, maxGb, ratio, 20, borrow); | ||
| 1558 | + | ||
| 1559 | + ubse::mem::controller::MockSetNumaCreateDesc(3, 7, 0); | ||
| 1560 | + MockNodeFreeBytes(THRESHOLD_BYTES - gb2_5); | ||
| 1561 | + | ||
| 1562 | + ProcessMemPidDecision::GetInstance().OnDecisionTimer(); | ||
| 1563 | + | ||
| 1564 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaCreateSize(), gb2_5); | ||
| 1565 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 3), gb2_5 / 1024); | ||
| 1566 | + | ||
| 1567 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1568 | + ASSERT_EQ(snapshot.at(pid).borrow.slots.size(), 2u); | ||
| 1569 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].migratedBytes, gb2_5); | ||
| 1570 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].returnStatus, ReturnStatus::RETURNING); | ||
| 1571 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].migratedBytes, gb2_5); | ||
| 1572 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].status, BorrowSlotStatus::COMPLETED); | ||
| 1573 | +} | ||
| 1574 | + | ||
| 1575 | +TEST_F(TestProcessMemPidDecision, BorrowingSlotExcludedFromMigrateTargets) | ||
| 1576 | +{ | ||
| 1577 | + const pid_t pid = 15970; | ||
| 1578 | + constexpr uint64_t maxGb = 32; | ||
| 1579 | + constexpr double ratio = 0.5; | ||
| 1580 | + constexpr uint64_t gb2_5 = 2 * GB + GB / 2; | ||
| 1581 | + BorrowState inFlight; | ||
| 1582 | + BorrowSlot slot; | ||
| 1583 | + slot.debtId = "debt-inflight"; | ||
| 1584 | + slot.migratedBytes = gb2_5; | ||
| 1585 | + slot.status = BorrowSlotStatus::BORROWING; | ||
| 1586 | + slot.borrowTime = std::chrono::steady_clock::now(); | ||
| 1587 | + inFlight.slots.push_back(slot); | ||
| 1588 | + AddManagedPid(pid, maxGb, ratio, 20, inFlight); | ||
| 1589 | + | ||
| 1590 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 1591 | + auto& infoMgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 1592 | + ubse::task_executor::MockSetExecutorAsync(true); | ||
| 1593 | + | ||
| 1594 | + constexpr uint64_t gb2 = 2 * GB; | ||
| 1595 | + ubse::mem::controller::MockSetNumaCreateDesc(9, 11, 0); | ||
| 1596 | + MockNodeFreeBytes(THRESHOLD_BYTES - 4 * GB - GB / 2); | ||
| 1597 | + std::thread roundThread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1598 | + roundThread.join(); | ||
| 1599 | + ubse::task_executor::MockWaitExecutorIdle(); | ||
| 1600 | + EXPECT_EQ(ubse::mem::controller::MockGetLastNumaCreateSize(), gb2); | ||
| 1601 | + auto snapshot = infoMgr.GetManagedPidCacheSnapshot(); | ||
| 1602 | + ASSERT_EQ(snapshot.at(pid).borrow.slots.size(), 2u); | ||
| 1603 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].capacity, 0u); | ||
| 1604 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].migratedBytes, gb2_5); | ||
| 1605 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].remoteNumaId, -1); | ||
| 1606 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].status, BorrowSlotStatus::BORROWING); | ||
| 1607 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].capacity, gb2); | ||
| 1608 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].migratedBytes, gb2); | ||
| 1609 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].remoteNumaId, 9); | ||
| 1610 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].status, BorrowSlotStatus::COMPLETED); | ||
| 1611 | + EXPECT_EQ(snapshot.at(pid).borrow.currentRemote, gb2); | ||
| 1612 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 3), 0u); | ||
| 1613 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 9), gb2 / 1024); | ||
| 1614 | + auto calls = ubse::smap::MockGetMigrateCalls(); | ||
| 1615 | + ASSERT_EQ(calls.size(), 1u); | ||
| 1616 | + EXPECT_EQ(calls[0].first, 9); | ||
| 1617 | + EXPECT_EQ(calls[0].second, gb2 / 1024); | ||
| 1618 | + EXPECT_EQ(decision.GetPendingBorrowTotal(), gb2_5); | ||
| 1619 | + EXPECT_EQ(snapshot.at(pid).processStatus, ProcessStatus::BORROWING); | ||
| 1620 | + | ||
| 1621 | + ubse::task_executor::MockSetExecutorAsync(false); | ||
| 1622 | +} | ||
| 1623 | + | ||
| 1624 | +TEST_F(TestProcessMemPidDecision, CollectCycleRebalanceAfterMaxOvercommit) | ||
| 1625 | +{ | ||
| 1626 | + const pid_t pid = getpid(); | ||
| 1627 | + constexpr uint64_t maxGb = 32; | ||
| 1628 | + constexpr double ratio = 0.5; | ||
| 1629 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 1630 | + auto& mgr = ProcessMemPidInfoManager::GetInstance(); | ||
| 1631 | + | ||
| 1632 | + mgr.Init(); | ||
| 1633 | + AddManagedPid(pid, maxGb, ratio, 40); | ||
| 1634 | + | ||
| 1635 | + ubse::mem::controller::MockSetNumaCreateDesc(3, 7, 5 * GB); | ||
| 1636 | + MockNodeFree(45); | ||
| 1637 | + std::thread round1Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1638 | + round1Thread.join(); | ||
| 1639 | + ubse::task_executor::MockWaitExecutorIdle(); | ||
| 1640 | + | ||
| 1641 | + ubse::mem::controller::MockSetNumaCreateDesc(9, 9, 15 * GB); | ||
| 1642 | + MockNodeFree(35); | ||
| 1643 | + std::thread round2Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1644 | + round2Thread.join(); | ||
| 1645 | + ubse::task_executor::MockWaitExecutorIdle(); | ||
| 1646 | + | ||
| 1647 | + auto snapshot = mgr.GetManagedPidCacheSnapshot(); | ||
| 1648 | + ASSERT_EQ(snapshot.at(pid).borrow.slots.size(), 2u); | ||
| 1649 | + EXPECT_EQ(snapshot.at(pid).borrow.currentRemote, 20 * GB); | ||
| 1650 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 3), 5 * GB / 1024); | ||
| 1651 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 9), 15 * GB / 1024); | ||
| 1652 | + | ||
| 1653 | + ProcessMemPidCollect::GetInstance().SetCollectVmRssOverride([pid](PidCollectInfoMap& results, uint64_t) { | ||
| 1654 | + results.entries[pid] = {3 * GB / 1024, false}; | ||
| 1655 | + }); | ||
| 1656 | + | ||
| 1657 | + ASSERT_EQ(ProcessMemPidCollect::GetInstance().CycleCollectNumaInfo(), UBSE_OK); | ||
| 1658 | + auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5); | ||
| 1659 | + while (std::chrono::steady_clock::now() < deadline) { | ||
| 1660 | + auto snap = mgr.GetManagedPidCacheSnapshot(); | ||
| 1661 | + if (snap.at(pid).borrow.currentRemote < 20 * GB) { | ||
| 1662 | + break; | ||
| 1663 | + } | ||
| 1664 | + std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||
| 1665 | + } | ||
| 1666 | + mgr.UnInit(); | ||
| 1667 | + | ||
| 1668 | + snapshot = mgr.GetManagedPidCacheSnapshot(); | ||
| 1669 | + ASSERT_EQ(snapshot.at(pid).borrow.slots.size(), 2u); | ||
| 1670 | + ASSERT_LT(snapshot.at(pid).borrow.currentRemote, 20 * GB); | ||
| 1671 | + EXPECT_EQ(snapshot.at(pid).vmRss, 3 * GB); | ||
| 1672 | + uint64_t expectedRemote = static_cast<uint64_t>(snapshot.at(pid).vmRss * ratio); | ||
| 1673 | + EXPECT_EQ(expectedRemote, 3 * GB / 2); | ||
| 1674 | + EXPECT_EQ(snapshot.at(pid).borrow.currentRemote, expectedRemote); | ||
| 1675 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[0].migratedBytes, 0u); | ||
| 1676 | + EXPECT_EQ(snapshot.at(pid).borrow.slots[1].migratedBytes, expectedRemote); | ||
| 1677 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 3), 0u); | ||
| 1678 | + uint64_t expectedTarget9Kb = expectedRemote / 1024 / 4 * 4; | ||
| 1679 | + EXPECT_EQ(ubse::smap::MockGetMigrateTargetKb(pid, 9), expectedTarget9Kb); | ||
| 1680 | + auto calls = ubse::smap::MockGetMigrateCalls(); | ||
| 1681 | + ASSERT_EQ(calls.size(), 4u); | ||
| 1682 | + EXPECT_EQ(calls[1].first, 3); | ||
| 1683 | + EXPECT_EQ(calls[1].second, 5 * GB / 1024); | ||
| 1684 | + EXPECT_EQ(calls[2].first, 9); | ||
| 1685 | + EXPECT_EQ(calls[2].second, 15 * GB / 1024); | ||
| 1686 | + EXPECT_EQ(calls[3].first, 9); | ||
| 1687 | + EXPECT_EQ(calls[3].second, expectedTarget9Kb); | ||
| 1688 | +} | ||
| 1689 | + | ||
| 1690 | +TEST_F(TestProcessMemPidDecision, BorrowingPidEligibleWithChangingNodeFree) | ||
| 1691 | +{ | ||
| 1692 | + const pid_t pidA = 15970; | ||
| 1693 | + constexpr uint64_t maxGb = 32; | ||
| 1694 | + constexpr double ratio = 0.5; | ||
| 1695 | + AddManagedPid(pidA, maxGb, ratio, 40); | ||
| 1696 | + | ||
| 1697 | + ProcessMemPidBridge::rmrsMigrateOut = [](const std::vector<mempooling::smap::MigrateOutPayload>&, int) { | ||
| 1698 | + return 0; | ||
| 1699 | + }; | ||
| 1700 | + | ||
| 1701 | + auto& decision = ProcessMemPidDecision::GetInstance(); | ||
| 1702 | + | ||
| 1703 | + ubse::mem::controller::MockBlockNumaCreate(); | ||
| 1704 | + MockNodeFree(40); | ||
| 1705 | + std::thread decisionThread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1706 | + ASSERT_TRUE(ubse::mem::controller::MockWaitNumaCreateEntered(5000)); | ||
| 1707 | + | ||
| 1708 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1709 | + ASSERT_EQ(snapshot.at(pidA).borrow.slots.size(), 1u); | ||
| 1710 | + EXPECT_EQ(snapshot.at(pidA).borrow.slots[0].migratedBytes, 10 * GB); | ||
| 1711 | + EXPECT_EQ(snapshot.at(pidA).borrow.slots[0].status, BorrowSlotStatus::BORROWING); | ||
| 1712 | + EXPECT_EQ(snapshot.at(pidA).processStatus, ProcessStatus::BORROWING); | ||
| 1713 | + | ||
| 1714 | + std::thread round2Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1715 | + round2Thread.join(); | ||
| 1716 | + snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1717 | + ASSERT_EQ(snapshot.at(pidA).borrow.slots.size(), 1u); | ||
| 1718 | + EXPECT_EQ(snapshot.at(pidA).borrow.slots[0].status, BorrowSlotStatus::BORROWING); | ||
| 1719 | + EXPECT_EQ(decision.GetPendingBorrowTotal(), 10 * GB); | ||
| 1720 | + | ||
| 1721 | + auto candidates = decision.BuildCandidates(2); | ||
| 1722 | + ASSERT_EQ(candidates.size(), 1u); | ||
| 1723 | + EXPECT_EQ(candidates[0].pid, pidA); | ||
| 1724 | + EXPECT_TRUE(candidates[0].hasActiveBorrow); | ||
| 1725 | + EXPECT_EQ(candidates[0].canMigrate, 10 * GB); | ||
| 1726 | + | ||
| 1727 | + ubse::mem::controller::MockReleaseNumaCreate(); | ||
| 1728 | + decisionThread.join(); | ||
| 1729 | + snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1730 | + ASSERT_EQ(snapshot.at(pidA).borrow.slots.size(), 1u); | ||
| 1731 | + EXPECT_EQ(snapshot.at(pidA).borrow.slots[0].status, BorrowSlotStatus::COMPLETED); | ||
| 1732 | + EXPECT_EQ(snapshot.at(pidA).borrow.currentRemote, 10 * GB); | ||
| 1733 | + | ||
| 1734 | + MockNodeFree(45); | ||
| 1735 | + std::thread round3Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1736 | + round3Thread.join(); | ||
| 1737 | + snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1738 | + ASSERT_EQ(snapshot.at(pidA).borrow.slots.size(), 2u); | ||
| 1739 | + EXPECT_EQ(snapshot.at(pidA).borrow.slots[1].migratedBytes, 5 * GB); | ||
| 1740 | + EXPECT_EQ(snapshot.at(pidA).borrow.slots[1].status, BorrowSlotStatus::COMPLETED); | ||
| 1741 | + EXPECT_EQ(snapshot.at(pidA).borrow.currentRemote, 15 * GB); | ||
| 1742 | + | ||
| 1743 | + MockNodeFree(40); | ||
| 1744 | + std::thread round4Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1745 | + round4Thread.join(); | ||
| 1746 | + snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1747 | + ASSERT_EQ(snapshot.at(pidA).borrow.slots.size(), 3u); | ||
| 1748 | + EXPECT_EQ(snapshot.at(pidA).borrow.slots[2].migratedBytes, 5 * GB); | ||
| 1749 | + EXPECT_EQ(snapshot.at(pidA).borrow.slots[2].status, BorrowSlotStatus::COMPLETED); | ||
| 1750 | + EXPECT_EQ(snapshot.at(pidA).borrow.currentRemote, 20 * GB); | ||
| 1751 | + | ||
| 1752 | + std::thread round5Thread([&decision]() { decision.OnDecisionTimer(); }); | ||
| 1753 | + round5Thread.join(); | ||
| 1754 | + snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1755 | + ASSERT_EQ(snapshot.at(pidA).borrow.slots.size(), 3u); | ||
| 1756 | + EXPECT_EQ(snapshot.at(pidA).borrow.currentRemote, 20 * GB); | ||
| 1757 | +} | ||
| 1758 | + | ||
| 1759 | +TEST_F(TestProcessMemPidDecision, ShortageDeductsPendingBorrow) | ||
| 1760 | +{ | ||
| 1761 | + const pid_t pidA = 15970; | ||
| 1762 | + const pid_t pidB = 15971; | ||
| 1763 | + const pid_t pidC = 15972; | ||
| 1764 | + AddManagedPid(pidA, 32, 0.5, 40); | ||
| 1765 | + BorrowState borrowA; | ||
| 1766 | + BorrowSlot inFlight; | ||
| 1767 | + inFlight.debtId = "debt-a"; | ||
| 1768 | + inFlight.migratedBytes = 15 * GB; | ||
| 1769 | + inFlight.capacity = 15 * GB; | ||
| 1770 | + inFlight.status = BorrowSlotStatus::BORROWING; | ||
| 1771 | + inFlight.borrowTime = std::chrono::steady_clock::now() + std::chrono::hours(1); | ||
| 1772 | + borrowA.slots = {inFlight}; | ||
| 1773 | + ProcessMemPidInfoManager::GetInstance().UpdateManagedPidBorrowState(pidA, borrowA, ProcessStatus::BORROWING); | ||
| 1774 | + AddManagedPid(pidB, 32, 0.5, 30); | ||
| 1775 | + AddManagedPid(pidC, 32, 0.5, 30); | ||
| 1776 | + | ||
| 1777 | + ProcessMemPidBridge::rmrsMigrateOut = [](const std::vector<mempooling::smap::MigrateOutPayload>&, int) { | ||
| 1778 | + return 0; | ||
| 1779 | + }; | ||
| 1780 | + MockNodeFree(10); | ||
| 1781 | + | ||
| 1782 | + ProcessMemPidDecision::GetInstance().OnDecisionTimer(); | ||
| 1783 | + | ||
| 1784 | + auto snapshot = ProcessMemPidInfoManager::GetInstance().GetManagedPidCacheSnapshot(); | ||
| 1785 | + ASSERT_EQ(snapshot.at(pidB).borrow.slots.size(), 1u); | ||
| 1786 | + EXPECT_EQ(snapshot.at(pidB).borrow.slots[0].status, BorrowSlotStatus::COMPLETED); | ||
| 1787 | + ASSERT_EQ(snapshot.at(pidC).borrow.slots.size(), 1u); | ||
| 1788 | + EXPECT_EQ(snapshot.at(pidC).borrow.slots[0].status, BorrowSlotStatus::COMPLETED); | ||
| 1789 | + std::multiset<uint64_t> migrated = {snapshot.at(pidB).borrow.slots[0].migratedBytes, | ||
| 1790 | + snapshot.at(pidC).borrow.slots[0].migratedBytes}; | ||
| 1791 | + EXPECT_EQ(migrated, (std::multiset<uint64_t>{15 * GB, 10 * GB})); | ||
| 1792 | + ASSERT_EQ(snapshot.at(pidA).borrow.slots.size(), 1u); | ||
| 1793 | + EXPECT_EQ(snapshot.at(pidA).borrow.slots[0].debtId, "debt-a"); | ||
| 1794 | + EXPECT_EQ(snapshot.at(pidA).borrow.slots[0].status, BorrowSlotStatus::BORROWING); | ||
| 1795 | + EXPECT_EQ(ProcessMemPidDecision::GetInstance().GetPendingBorrowTotal(), 15 * GB); | ||
| 1796 | +} | ||
| 1797 | +} // namespace ubse::ut::process_mem | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved. | ||
| 3 | + * ubs-engine is licensed under Mulan PSL v2. | ||
| 4 | + * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 5 | + * You may obtain a copy of Mulan PSL v2 at: | ||
| 6 | + * http://license.coscl.org.cn/MulanPSL2 | ||
| 7 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 8 | + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 9 | + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 10 | + * See the Mulan PSL v2 for more details. | ||
| 11 | + */ | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| 38 | +namespace ubse::ut::process_mem { | ||
| 39 | + | ||
| 40 | +class TestProcessMemPidDecision : public testing::Test { | ||
| 41 | +public: | ||
| 42 | + TestProcessMemPidDecision() = default; | ||
| 43 | + | ||
| 44 | + void SetUp() override; | ||
| 45 | + void TearDown() override; | ||
| 46 | +}; | ||
| 47 | +} // namespace ubse::ut::process_mem | ||
| 48 | + | ||
| @@ -16,6 +16,10 @@ | |||
| 16 | 16 | ||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 19 | namespace ubse::ut::process_mem { | 23 | namespace ubse::ut::process_mem { |
| 20 | 24 | ||
| 21 | class TestProcessMemPidInfoManager : public testing::Test { | 25 | class TestProcessMemPidInfoManager : public testing::Test { |
| @@ -19,138 +19,210 @@ void TestProcessMemPidManagerDef::SetUp() {} | |||
| 19 | 19 | ||
| 20 | void TestProcessMemPidManagerDef::TearDown() {} | 20 | void TestProcessMemPidManagerDef::TearDown() {} |
| 21 | 21 | ||
| 22 | -TEST_F(TestProcessMemPidManagerDef, SerializeConfigInfoWithoutSrcNuma) | 22 | +TEST_F(TestProcessMemPidManagerDef, ProcessMemNewConfigInfoDefaultValues) |
| 23 | { | 23 | { |
| 24 | - ProcessMemPidConfigInfo configInfo{}; | 24 | + ProcessMemNewConfigInfo config{}; |
| 25 | - configInfo.pid = 1234; | 25 | + EXPECT_TRUE(config.isPid); |
| 26 | - configInfo.evictThreshold = 80; | 26 | + EXPECT_TRUE(config.identifier.empty()); |
| 27 | - configInfo.targetEvictThreshold = 70; | 27 | + EXPECT_EQ(config.maxMemory, 0u); |
| 28 | - configInfo.reclaimThreshold = 50; | 28 | + EXPECT_DOUBLE_EQ(config.remoteRatio, 0.0); |
| 29 | - configInfo.expectedMemoryUsage = 1024000; | 29 | + EXPECT_EQ(config.startTime, 0u); |
| 30 | - configInfo.srcNumaId = std::nullopt; | 30 | +} |
| 31 | + | ||
| 32 | +TEST_F(TestProcessMemPidManagerDef, SerializeProcessMemNewConfigInfoPidMode) | ||
| 33 | +{ | ||
| 34 | + ProcessMemNewConfigInfo config{}; | ||
| 35 | + config.isPid = true; | ||
| 36 | + config.identifier = "1234"; | ||
| 37 | + config.maxMemory = 1024000; | ||
| 38 | + config.remoteRatio = 0.5; | ||
| 39 | + config.startTime = 9999; | ||
| 31 | 40 | ||
| 32 | ubse::serial::UbseSerialization serializer; | 41 | ubse::serial::UbseSerialization serializer; |
| 33 | - auto ret = configInfo.SerializeConfigInfo(serializer); | 42 | + auto ret = config.Serialize(serializer); |
| 34 | EXPECT_EQ(ret, UBSE_OK); | 43 | EXPECT_EQ(ret, UBSE_OK); |
| 35 | 44 | ||
| 36 | ubse::serial::UbseDeSerialization deserializer(serializer.GetBuffer(), serializer.GetLength()); | 45 | ubse::serial::UbseDeSerialization deserializer(serializer.GetBuffer(), serializer.GetLength()); |
| 37 | - ProcessMemPidConfigInfo deserialized{}; | 46 | + ProcessMemNewConfigInfo deserialized{}; |
| 38 | - ret = deserialized.DeserializeConfigInfo(deserializer); | 47 | + ret = deserialized.Deserialize(deserializer); |
| 39 | EXPECT_EQ(ret, UBSE_OK); | 48 | EXPECT_EQ(ret, UBSE_OK); |
| 40 | 49 | ||
| 41 | - EXPECT_EQ(deserialized.pid, 1234); | 50 | + EXPECT_TRUE(deserialized.isPid); |
| 42 | - EXPECT_EQ(deserialized.evictThreshold, 80); | 51 | + EXPECT_EQ(deserialized.identifier, "1234"); |
| 43 | - EXPECT_EQ(deserialized.targetEvictThreshold, 70); | 52 | + EXPECT_EQ(deserialized.maxMemory, 1024000u); |
| 44 | - EXPECT_EQ(deserialized.reclaimThreshold, 50); | 53 | + EXPECT_DOUBLE_EQ(deserialized.remoteRatio, 0.5); |
| 45 | - EXPECT_EQ(deserialized.expectedMemoryUsage, 1024000); | 54 | + EXPECT_EQ(deserialized.startTime, 9999u); |
| 46 | - EXPECT_FALSE(deserialized.srcNumaId.has_value()); | ||
| 47 | } | 55 | } |
| 48 | 56 | ||
| 49 | -TEST_F(TestProcessMemPidManagerDef, SerializeConfigInfoWithSrcNuma) | 57 | +TEST_F(TestProcessMemPidManagerDef, SerializeProcessMemNewConfigInfoNameMode) |
| 50 | { | 58 | { |
| 51 | - ProcessMemPidConfigInfo configInfo{}; | 59 | + ProcessMemNewConfigInfo config{}; |
| 52 | - configInfo.pid = 5678; | 60 | + config.isPid = false; |
| 53 | - configInfo.evictThreshold = 90; | 61 | + config.identifier = "myproc"; |
| 54 | - configInfo.targetEvictThreshold = 60; | 62 | + config.maxMemory = 2048000; |
| 55 | - configInfo.reclaimThreshold = 40; | 63 | + config.remoteRatio = 0.3; |
| 56 | - configInfo.expectedMemoryUsage = 2048000; | 64 | + config.startTime = 0; |
| 57 | - configInfo.srcNumaId = 2; | ||
| 58 | 65 | ||
| 59 | ubse::serial::UbseSerialization serializer; | 66 | ubse::serial::UbseSerialization serializer; |
| 60 | - auto ret = configInfo.SerializeConfigInfo(serializer); | 67 | + auto ret = config.Serialize(serializer); |
| 61 | EXPECT_EQ(ret, UBSE_OK); | 68 | EXPECT_EQ(ret, UBSE_OK); |
| 62 | 69 | ||
| 63 | ubse::serial::UbseDeSerialization deserializer(serializer.GetBuffer(), serializer.GetLength()); | 70 | ubse::serial::UbseDeSerialization deserializer(serializer.GetBuffer(), serializer.GetLength()); |
| 64 | - ProcessMemPidConfigInfo deserialized{}; | 71 | + ProcessMemNewConfigInfo deserialized{}; |
| 65 | - ret = deserialized.DeserializeConfigInfo(deserializer); | 72 | + ret = deserialized.Deserialize(deserializer); |
| 66 | EXPECT_EQ(ret, UBSE_OK); | 73 | EXPECT_EQ(ret, UBSE_OK); |
| 67 | 74 | ||
| 68 | - EXPECT_EQ(deserialized.pid, 5678); | 75 | + EXPECT_FALSE(deserialized.isPid); |
| 69 | - EXPECT_EQ(deserialized.evictThreshold, 90); | 76 | + EXPECT_EQ(deserialized.identifier, "myproc"); |
| 70 | - EXPECT_EQ(deserialized.targetEvictThreshold, 60); | 77 | + EXPECT_EQ(deserialized.maxMemory, 2048000u); |
| 71 | - EXPECT_EQ(deserialized.reclaimThreshold, 40); | 78 | + EXPECT_DOUBLE_EQ(deserialized.remoteRatio, 0.3); |
| 72 | - EXPECT_EQ(deserialized.expectedMemoryUsage, 2048000); | ||
| 73 | - EXPECT_TRUE(deserialized.srcNumaId.has_value()); | ||
| 74 | - EXPECT_EQ(deserialized.srcNumaId.value(), 2u); | ||
| 75 | } | 79 | } |
| 76 | 80 | ||
| 77 | -TEST_F(TestProcessMemPidManagerDef, SerializePidInfoWithoutSrcNuma) | 81 | +TEST_F(TestProcessMemPidManagerDef, SerializeProcessMemNewConfigInfoZeroValues) |
| 78 | { | 82 | { |
| 79 | - ProcessMemPidInfo pidInfo{}; | 83 | + ProcessMemNewConfigInfo config{}; |
| 80 | - pidInfo.ppid = 100; | 84 | + config.isPid = true; |
| 81 | - pidInfo.startTime = 9999; | 85 | + config.identifier = "0"; |
| 82 | - pidInfo.configInfo.pid = 1234; | 86 | + config.maxMemory = 0; |
| 83 | - pidInfo.configInfo.evictThreshold = 80; | 87 | + config.remoteRatio = 0.0; |
| 84 | - pidInfo.configInfo.targetEvictThreshold = 70; | ||
| 85 | - pidInfo.configInfo.reclaimThreshold = 50; | ||
| 86 | - pidInfo.configInfo.expectedMemoryUsage = 1024000; | ||
| 87 | - pidInfo.configInfo.srcNumaId = std::nullopt; | ||
| 88 | 88 | ||
| 89 | ubse::serial::UbseSerialization serializer; | 89 | ubse::serial::UbseSerialization serializer; |
| 90 | - auto ret = pidInfo.SerializePidInfo(serializer); | 90 | + auto ret = config.Serialize(serializer); |
| 91 | EXPECT_EQ(ret, UBSE_OK); | 91 | EXPECT_EQ(ret, UBSE_OK); |
| 92 | 92 | ||
| 93 | ubse::serial::UbseDeSerialization deserializer(serializer.GetBuffer(), serializer.GetLength()); | 93 | ubse::serial::UbseDeSerialization deserializer(serializer.GetBuffer(), serializer.GetLength()); |
| 94 | - ProcessMemPidInfo deserialized{}; | 94 | + ProcessMemNewConfigInfo deserialized{}; |
| 95 | - ret = deserialized.DeserializePidInfo(deserializer); | 95 | + ret = deserialized.Deserialize(deserializer); |
| 96 | EXPECT_EQ(ret, UBSE_OK); | 96 | EXPECT_EQ(ret, UBSE_OK); |
| 97 | - | 97 | + EXPECT_TRUE(deserialized.isPid); |
| 98 | - EXPECT_EQ(deserialized.startTime, 9999); | 98 | + EXPECT_EQ(deserialized.identifier, "0"); |
| 99 | - EXPECT_EQ(deserialized.configInfo.pid, 1234); | ||
| 100 | - EXPECT_EQ(deserialized.configInfo.evictThreshold, 80); | ||
| 101 | - EXPECT_FALSE(deserialized.configInfo.srcNumaId.has_value()); | ||
| 102 | } | 99 | } |
| 103 | 100 | ||
| 104 | -TEST_F(TestProcessMemPidManagerDef, SerializePidInfoWithSrcNuma) | 101 | +TEST_F(TestProcessMemPidManagerDef, ProcessMemDisplayEntryDefaultValues) |
| 105 | { | 102 | { |
| 106 | - ProcessMemPidInfo pidInfo{}; | 103 | + ProcessMemDisplayEntry entry{}; |
| 107 | - pidInfo.ppid = 200; | 104 | + EXPECT_EQ(entry.pid, 0); |
| 108 | - pidInfo.startTime = 12345; | 105 | + EXPECT_TRUE(entry.name.empty()); |
| 109 | - pidInfo.configInfo.pid = 5678; | 106 | + EXPECT_EQ(entry.maxMemory, 0u); |
| 110 | - pidInfo.configInfo.evictThreshold = 90; | 107 | + EXPECT_DOUBLE_EQ(entry.remoteRatio, 0.0); |
| 111 | - pidInfo.configInfo.srcNumaId = 3; | 108 | +} |
| 109 | + | ||
| 110 | +TEST_F(TestProcessMemPidManagerDef, SerializeProcessMemDisplayEntry) | ||
| 111 | +{ | ||
| 112 | + ProcessMemDisplayEntry entry{}; | ||
| 113 | + entry.pid = 42; | ||
| 114 | + entry.name = "myproc"; | ||
| 115 | + entry.maxMemory = 4096000; | ||
| 116 | + entry.remoteRatio = 0.75; | ||
| 112 | 117 | ||
| 113 | ubse::serial::UbseSerialization serializer; | 118 | ubse::serial::UbseSerialization serializer; |
| 114 | - auto ret = pidInfo.SerializePidInfo(serializer); | 119 | + auto ret = entry.Serialize(serializer); |
| 115 | EXPECT_EQ(ret, UBSE_OK); | 120 | EXPECT_EQ(ret, UBSE_OK); |
| 116 | 121 | ||
| 117 | ubse::serial::UbseDeSerialization deserializer(serializer.GetBuffer(), serializer.GetLength()); | 122 | ubse::serial::UbseDeSerialization deserializer(serializer.GetBuffer(), serializer.GetLength()); |
| 118 | - ProcessMemPidInfo deserialized{}; | 123 | + ProcessMemDisplayEntry deserialized{}; |
| 119 | - ret = deserialized.DeserializePidInfo(deserializer); | 124 | + ret = deserialized.Deserialize(deserializer); |
| 120 | EXPECT_EQ(ret, UBSE_OK); | 125 | EXPECT_EQ(ret, UBSE_OK); |
| 121 | 126 | ||
| 122 | - EXPECT_EQ(deserialized.startTime, 12345); | 127 | + EXPECT_EQ(deserialized.pid, 42); |
| 123 | - EXPECT_EQ(deserialized.configInfo.pid, 5678); | 128 | + EXPECT_EQ(deserialized.name, "myproc"); |
| 124 | - EXPECT_TRUE(deserialized.configInfo.srcNumaId.has_value()); | 129 | + EXPECT_EQ(deserialized.maxMemory, 4096000u); |
| 125 | - EXPECT_EQ(deserialized.configInfo.srcNumaId.value(), 3u); | 130 | + EXPECT_DOUBLE_EQ(deserialized.remoteRatio, 0.75); |
| 126 | } | 131 | } |
| 127 | 132 | ||
| 128 | -TEST_F(TestProcessMemPidManagerDef, ProcessMemPidInfoDefaultValues) | 133 | +TEST_F(TestProcessMemPidManagerDef, ParsePidFromIdentifierValid) |
| 129 | { | 134 | { |
| 130 | - ProcessMemPidInfo pidInfo{}; | 135 | + auto pid = ParsePidFromIdentifier("1234"); |
| 131 | - EXPECT_EQ(pidInfo.ppid, 0); | 136 | + ASSERT_TRUE(pid.has_value()); |
| 132 | - EXPECT_EQ(pidInfo.startTime, 0); | 137 | + EXPECT_EQ(pid.value(), 1234); |
| 133 | - EXPECT_EQ(pidInfo.configInfo.pid, 0); | ||
| 134 | - EXPECT_EQ(pidInfo.processStatus, ProcessStatus::IDLE); | ||
| 135 | - EXPECT_EQ(pidInfo.memBorrowInfo.remoteNumaId, -1); | ||
| 136 | - EXPECT_EQ(pidInfo.memBorrowInfo.exportSlotId, -1); | ||
| 137 | - EXPECT_EQ(pidInfo.memBorrowInfo.importSocketId, -1); | ||
| 138 | - EXPECT_TRUE(pidInfo.memBorrowInfo.debtInfos.empty()); | ||
| 139 | } | 138 | } |
| 140 | 139 | ||
| 141 | -TEST_F(TestProcessMemPidManagerDef, BorrowInfoDefaultValues) | 140 | +TEST_F(TestProcessMemPidManagerDef, ParsePidFromIdentifierInvalid) |
| 142 | { | 141 | { |
| 143 | - BorrowInfo borrowInfo{}; | 142 | + EXPECT_FALSE(ParsePidFromIdentifier("not_a_pid").has_value()); |
| 144 | - EXPECT_EQ(borrowInfo.remoteNumaId, -1); | 143 | + EXPECT_FALSE(ParsePidFromIdentifier("").has_value()); |
| 145 | - EXPECT_EQ(borrowInfo.exportSlotId, -1); | 144 | + EXPECT_FALSE(ParsePidFromIdentifier("999999999999999999999").has_value()); |
| 146 | - EXPECT_EQ(borrowInfo.importSocketId, -1); | ||
| 147 | - EXPECT_TRUE(borrowInfo.debtInfos.empty()); | ||
| 148 | } | 145 | } |
| 149 | 146 | ||
| 150 | -TEST_F(TestProcessMemPidManagerDef, DebtInfoDefaultValues) | 147 | +TEST_F(TestProcessMemPidManagerDef, ParsePidFromIdentifierNonPositive) |
| 151 | { | 148 | { |
| 152 | - DebtInfo debtInfo{}; | 149 | + EXPECT_FALSE(ParsePidFromIdentifier("0").has_value()); |
| 153 | - EXPECT_EQ(debtInfo.status, BorrowStatus::COMPLETED); | 150 | + EXPECT_FALSE(ParsePidFromIdentifier("-1").has_value()); |
| 151 | +} | ||
| 152 | + | ||
| 153 | +TEST_F(TestProcessMemPidManagerDef, ManagedPidEntryDefaultValues) | ||
| 154 | +{ | ||
| 155 | + ManagedPidEntry entry{}; | ||
| 156 | + EXPECT_EQ(entry.pid, 0); | ||
| 157 | + EXPECT_EQ(entry.parentPid, 0); | ||
| 158 | + EXPECT_FALSE(entry.isChild); | ||
| 159 | + EXPECT_EQ(entry.sources, 0u); | ||
| 160 | + EXPECT_TRUE(entry.nameConfigName.empty()); | ||
| 161 | + EXPECT_EQ(entry.maxMemory, 0u); | ||
| 162 | + EXPECT_DOUBLE_EQ(entry.remoteRatio, 0.0); | ||
| 163 | + EXPECT_EQ(entry.vmRss, 0u); | ||
| 164 | + EXPECT_EQ(entry.processStatus, ProcessStatus::IDLE); | ||
| 165 | + EXPECT_TRUE(entry.borrow.slots.empty()); | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +TEST_F(TestProcessMemPidManagerDef, ConfigSourceBitValues) | ||
| 169 | +{ | ||
| 170 | + EXPECT_EQ(static_cast<uint8_t>(ConfigSource::PID_CONFIG), 1u); | ||
| 171 | + EXPECT_EQ(static_cast<uint8_t>(ConfigSource::NAME_CONFIG), 2u); | ||
| 172 | +} | ||
| 173 | + | ||
| 174 | +TEST_F(TestProcessMemPidManagerDef, BorrowSlotDefaultValues) | ||
| 175 | +{ | ||
| 176 | + BorrowSlot slot{}; | ||
| 177 | + EXPECT_EQ(slot.migratedBytes, 0u); | ||
| 178 | + EXPECT_EQ(slot.exportSlotId, -1); | ||
| 179 | + EXPECT_TRUE(slot.debtId.empty()); | ||
| 180 | + EXPECT_EQ(slot.srcNumaId, -1); | ||
| 181 | + EXPECT_EQ(slot.remoteNumaId, -1); | ||
| 182 | + EXPECT_EQ(slot.status, BorrowSlotStatus::BORROWING); | ||
| 183 | +} | ||
| 184 | + | ||
| 185 | +TEST_F(TestProcessMemPidManagerDef, BorrowStateDefaultValues) | ||
| 186 | +{ | ||
| 187 | + BorrowState state{}; | ||
| 188 | + EXPECT_EQ(state.currentRemote, 0u); | ||
| 189 | + EXPECT_TRUE(state.slots.empty()); | ||
| 190 | + EXPECT_TRUE(state.remoteNumaMigrated.empty()); | ||
| 191 | +} | ||
| 192 | + | ||
| 193 | +TEST_F(TestProcessMemPidManagerDef, BorrowCandidateDefaultValues) | ||
| 194 | +{ | ||
| 195 | + BorrowCandidate candidate{}; | ||
| 196 | + EXPECT_EQ(candidate.pid, 0); | ||
| 197 | + EXPECT_FALSE(candidate.isChild); | ||
| 198 | + EXPECT_EQ(candidate.actual, 0u); | ||
| 199 | + EXPECT_EQ(candidate.maxMemory, 0u); | ||
| 200 | + EXPECT_DOUBLE_EQ(candidate.remoteRatio, 0.0); | ||
| 201 | + EXPECT_EQ(candidate.currentRemote, 0u); | ||
| 202 | + EXPECT_EQ(candidate.canMigrate, 0u); | ||
| 203 | +} | ||
| 204 | + | ||
| 205 | +TEST_F(TestProcessMemPidManagerDef, BorrowSlotStatusDistinct) | ||
| 206 | +{ | ||
| 207 | + EXPECT_NE(BorrowSlotStatus::BORROWING, BorrowSlotStatus::COMPLETED); | ||
| 208 | + EXPECT_NE(BorrowSlotStatus::COMPLETED, BorrowSlotStatus::FAILED); | ||
| 209 | + EXPECT_NE(BorrowSlotStatus::FAILED, BorrowSlotStatus::BORROWING); | ||
| 210 | +} | ||
| 211 | + | ||
| 212 | +TEST_F(TestProcessMemPidManagerDef, KeyPrefixConstants) | ||
| 213 | +{ | ||
| 214 | + EXPECT_FALSE(PROC_MEM_PID_KEY_PREFIX.empty()); | ||
| 215 | + EXPECT_EQ(PROC_MEM_PID_KEY_PREFIX.find("procMem_pid_"), 0u); | ||
| 216 | + EXPECT_FALSE(PROC_MEM_NAME_KEY_PREFIX.empty()); | ||
| 217 | + EXPECT_EQ(PROC_MEM_NAME_KEY_PREFIX.find("procMem_name_"), 0u); | ||
| 218 | + EXPECT_NE(PROC_MEM_PID_KEY_PREFIX, PROC_MEM_NAME_KEY_PREFIX); | ||
| 219 | +} | ||
| 220 | + | ||
| 221 | +TEST_F(TestProcessMemPidManagerDef, ProcessStatusEnumValues) | ||
| 222 | +{ | ||
| 223 | + EXPECT_NE(ProcessStatus::IDLE, ProcessStatus::BORROWING); | ||
| 224 | + EXPECT_NE(ProcessStatus::BORROWING, ProcessStatus::BORROWED); | ||
| 225 | + EXPECT_NE(ProcessStatus::IDLE, ProcessStatus::BORROWED); | ||
| 154 | } | 226 | } |
| 155 | 227 | ||
| 156 | TEST_F(TestProcessMemPidManagerDef, ProcessMemUsrInfoStaticAssert) | 228 | TEST_F(TestProcessMemPidManagerDef, ProcessMemUsrInfoStaticAssert) |
| @@ -167,173 +239,4 @@ TEST_F(TestProcessMemPidManagerDef, ProcessMemUsrInfoDefaultValues) | |||
| 167 | EXPECT_EQ(usrInfo.startTime, 0); | 239 | EXPECT_EQ(usrInfo.startTime, 0); |
| 168 | EXPECT_EQ(usrInfo.srcNuma, -1); | 240 | EXPECT_EQ(usrInfo.srcNuma, -1); |
| 169 | } | 241 | } |
| 170 | - | ||
| 171 | -TEST_F(TestProcessMemPidManagerDef, SerializeDeserializeRoundTrip) | ||
| 172 | -{ | ||
| 173 | - ProcessMemPidConfigInfo configInfo{}; | ||
| 174 | - configInfo.pid = 9999; | ||
| 175 | - configInfo.evictThreshold = 85; | ||
| 176 | - configInfo.targetEvictThreshold = 75; | ||
| 177 | - configInfo.reclaimThreshold = 55; | ||
| 178 | - configInfo.expectedMemoryUsage = 4096000; | ||
| 179 | - configInfo.srcNumaId = 5; | ||
| 180 | - | ||
| 181 | - ubse::serial::UbseSerialization serializer; | ||
| 182 | - auto ret = configInfo.SerializeConfigInfo(serializer); | ||
| 183 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 184 | - | ||
| 185 | - ubse::serial::UbseDeSerialization deserializer(serializer.GetBuffer(), serializer.GetLength()); | ||
| 186 | - ProcessMemPidConfigInfo deserialized{}; | ||
| 187 | - ret = deserialized.DeserializeConfigInfo(deserializer); | ||
| 188 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 189 | - | ||
| 190 | - EXPECT_EQ(deserialized.pid, configInfo.pid); | ||
| 191 | - EXPECT_EQ(deserialized.evictThreshold, configInfo.evictThreshold); | ||
| 192 | - EXPECT_EQ(deserialized.targetEvictThreshold, configInfo.targetEvictThreshold); | ||
| 193 | - EXPECT_EQ(deserialized.reclaimThreshold, configInfo.reclaimThreshold); | ||
| 194 | - EXPECT_EQ(deserialized.expectedMemoryUsage, configInfo.expectedMemoryUsage); | ||
| 195 | - EXPECT_EQ(deserialized.srcNumaId.value(), configInfo.srcNumaId.value()); | ||
| 196 | -} | ||
| 197 | - | ||
| 198 | -// ==================== Edge case tests ==================== | ||
| 199 | - | ||
| 200 | -TEST_F(TestProcessMemPidManagerDef, SerializeConfigInfoZeroPid) | ||
| 201 | -{ | ||
| 202 | - ProcessMemPidConfigInfo configInfo{}; | ||
| 203 | - configInfo.pid = 0; | ||
| 204 | - configInfo.evictThreshold = 0; | ||
| 205 | - configInfo.targetEvictThreshold = 0; | ||
| 206 | - configInfo.reclaimThreshold = 0; | ||
| 207 | - configInfo.expectedMemoryUsage = 0; | ||
| 208 | - configInfo.srcNumaId = std::nullopt; | ||
| 209 | - | ||
| 210 | - ubse::serial::UbseSerialization serializer; | ||
| 211 | - auto ret = configInfo.SerializeConfigInfo(serializer); | ||
| 212 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 213 | - | ||
| 214 | - ubse::serial::UbseDeSerialization deserializer(serializer.GetBuffer(), serializer.GetLength()); | ||
| 215 | - ProcessMemPidConfigInfo deserialized{}; | ||
| 216 | - ret = deserialized.DeserializeConfigInfo(deserializer); | ||
| 217 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 218 | - | ||
| 219 | - EXPECT_EQ(deserialized.pid, 0); | ||
| 220 | - EXPECT_EQ(deserialized.evictThreshold, 0); | ||
| 221 | - EXPECT_EQ(deserialized.targetEvictThreshold, 0); | ||
| 222 | - EXPECT_EQ(deserialized.reclaimThreshold, 0); | ||
| 223 | - EXPECT_EQ(deserialized.expectedMemoryUsage, 0u); | ||
| 224 | - EXPECT_FALSE(deserialized.srcNumaId.has_value()); | ||
| 225 | -} | ||
| 226 | - | ||
| 227 | -TEST_F(TestProcessMemPidManagerDef, SerializeConfigInfoLargeValues) | ||
| 228 | -{ | ||
| 229 | - ProcessMemPidConfigInfo configInfo{}; | ||
| 230 | - configInfo.pid = 65535; | ||
| 231 | - configInfo.evictThreshold = 100; | ||
| 232 | - configInfo.targetEvictThreshold = 100; | ||
| 233 | - configInfo.reclaimThreshold = 100; | ||
| 234 | - configInfo.expectedMemoryUsage = UINT64_MAX; | ||
| 235 | - configInfo.srcNumaId = 0; | ||
| 236 | - | ||
| 237 | - ubse::serial::UbseSerialization serializer; | ||
| 238 | - auto ret = configInfo.SerializeConfigInfo(serializer); | ||
| 239 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 240 | - | ||
| 241 | - ubse::serial::UbseDeSerialization deserializer(serializer.GetBuffer(), serializer.GetLength()); | ||
| 242 | - ProcessMemPidConfigInfo deserialized{}; | ||
| 243 | - ret = deserialized.DeserializeConfigInfo(deserializer); | ||
| 244 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 245 | - | ||
| 246 | - EXPECT_EQ(deserialized.pid, 65535); | ||
| 247 | - EXPECT_EQ(deserialized.evictThreshold, 100); | ||
| 248 | - EXPECT_EQ(deserialized.expectedMemoryUsage, UINT64_MAX); | ||
| 249 | - EXPECT_TRUE(deserialized.srcNumaId.has_value()); | ||
| 250 | - EXPECT_EQ(deserialized.srcNumaId.value(), 0u); | ||
| 251 | -} | ||
| 252 | - | ||
| 253 | -TEST_F(TestProcessMemPidManagerDef, SerializePidInfoZeroConfigInfo) | ||
| 254 | -{ | ||
| 255 | - ProcessMemPidInfo pidInfo{}; | ||
| 256 | - pidInfo.ppid = 0; | ||
| 257 | - pidInfo.startTime = 0; | ||
| 258 | - pidInfo.configInfo.pid = 0; | ||
| 259 | - pidInfo.configInfo.evictThreshold = 0; | ||
| 260 | - pidInfo.configInfo.targetEvictThreshold = 0; | ||
| 261 | - pidInfo.configInfo.reclaimThreshold = 0; | ||
| 262 | - pidInfo.configInfo.expectedMemoryUsage = 0; | ||
| 263 | - pidInfo.configInfo.srcNumaId = std::nullopt; | ||
| 264 | - | ||
| 265 | - ubse::serial::UbseSerialization serializer; | ||
| 266 | - auto ret = pidInfo.SerializePidInfo(serializer); | ||
| 267 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 268 | - | ||
| 269 | - ubse::serial::UbseDeSerialization deserializer(serializer.GetBuffer(), serializer.GetLength()); | ||
| 270 | - ProcessMemPidInfo deserialized{}; | ||
| 271 | - ret = deserialized.DeserializePidInfo(deserializer); | ||
| 272 | - EXPECT_EQ(ret, UBSE_OK); | ||
| 273 | - | ||
| 274 | - EXPECT_EQ(deserialized.startTime, 0); | ||
| 275 | - EXPECT_EQ(deserialized.configInfo.pid, 0); | ||
| 276 | - EXPECT_FALSE(deserialized.configInfo.srcNumaId.has_value()); | ||
| 277 | -} | ||
| 278 | - | ||
| 279 | -TEST_F(TestProcessMemPidManagerDef, ProcessStatusEnumValues) | ||
| 280 | -{ | ||
| 281 | - EXPECT_NE(ProcessStatus::IDLE, ProcessStatus::FAULT); | ||
| 282 | - EXPECT_NE(ProcessStatus::BORROWING, ProcessStatus::REPAYING); | ||
| 283 | - EXPECT_NE(ProcessStatus::WAIT_BORROW, ProcessStatus::INACTIVE); | ||
| 284 | -} | ||
| 285 | - | ||
| 286 | -TEST_F(TestProcessMemPidManagerDef, BorrowStatusEnumValues) | ||
| 287 | -{ | ||
| 288 | - EXPECT_NE(BorrowStatus::COMPLETED, BorrowStatus::CREATING); | ||
| 289 | -} | ||
| 290 | - | ||
| 291 | -TEST_F(TestProcessMemPidManagerDef, ConstantsAreValid) | ||
| 292 | -{ | ||
| 293 | - EXPECT_EQ(PROCESS_MEM_MODULE_CODE, 123u); | ||
| 294 | - EXPECT_EQ(PROCESS_MEM_SERVICE_ID, 11u); | ||
| 295 | - EXPECT_EQ(INVALID_REMOTE_NUMA, 0u); | ||
| 296 | -} | ||
| 297 | - | ||
| 298 | -TEST_F(TestProcessMemPidManagerDef, ProcessMemNamePrefix) | ||
| 299 | -{ | ||
| 300 | - EXPECT_FALSE(PROCESS_MEM_NAME_PREFIX.empty()); | ||
| 301 | - EXPECT_EQ(PROCESS_MEM_NAME_PREFIX.find("ProcessMem"), 0u); | ||
| 302 | -} | ||
| 303 | - | ||
| 304 | -TEST_F(TestProcessMemPidManagerDef, PidCollectInfoDefault) | ||
| 305 | -{ | ||
| 306 | - PidCollectInfo info{}; | ||
| 307 | - EXPECT_TRUE(info.numaMemDistribution.empty()); | ||
| 308 | - EXPECT_TRUE(info.childrenInfo.empty()); | ||
| 309 | -} | ||
| 310 | - | ||
| 311 | -TEST_F(TestProcessMemPidManagerDef, DoubleSerializeDeserialize) | ||
| 312 | -{ | ||
| 313 | - ProcessMemPidConfigInfo configInfo{}; | ||
| 314 | - configInfo.pid = 1234; | ||
| 315 | - configInfo.evictThreshold = 80; | ||
| 316 | - configInfo.expectedMemoryUsage = 1024000; | ||
| 317 | - configInfo.srcNumaId = 1; | ||
| 318 | - | ||
| 319 | - // First serialize-deserialize | ||
| 320 | - ubse::serial::UbseSerialization serializer1; | ||
| 321 | - configInfo.SerializeConfigInfo(serializer1); | ||
| 322 | - ubse::serial::UbseDeSerialization deserializer1(serializer1.GetBuffer(), serializer1.GetLength()); | ||
| 323 | - ProcessMemPidConfigInfo intermediate{}; | ||
| 324 | - intermediate.DeserializeConfigInfo(deserializer1); | ||
| 325 | - | ||
| 326 | - // Second serialize-deserialize | ||
| 327 | - ubse::serial::UbseSerialization serializer2; | ||
| 328 | - intermediate.SerializeConfigInfo(serializer2); | ||
| 329 | - ubse::serial::UbseDeSerialization deserializer2(serializer2.GetBuffer(), serializer2.GetLength()); | ||
| 330 | - ProcessMemPidConfigInfo final_{}; | ||
| 331 | - final_.DeserializeConfigInfo(deserializer2); | ||
| 332 | - | ||
| 333 | - EXPECT_EQ(final_.pid, configInfo.pid); | ||
| 334 | - EXPECT_EQ(final_.evictThreshold, configInfo.evictThreshold); | ||
| 335 | - EXPECT_EQ(final_.expectedMemoryUsage, configInfo.expectedMemoryUsage); | ||
| 336 | - EXPECT_TRUE(final_.srcNumaId.has_value()); | ||
| 337 | - EXPECT_EQ(final_.srcNumaId.value(), configInfo.srcNumaId.value()); | ||
| 338 | -} | ||
| 339 | } // namespace ubse::ut::process_mem | 242 | } // namespace ubse::ut::process_mem |
| @@ -20,10 +20,10 @@ void TestProcessMemPlugin::SetUp() {} | |||
| 20 | 20 | ||
| 21 | void TestProcessMemPlugin::TearDown() {} | 21 | void TestProcessMemPlugin::TearDown() {} |
| 22 | 22 | ||
| 23 | -TEST_F(TestProcessMemPlugin, UbsePluginInitFailNoLibrary) | 23 | +TEST_F(TestProcessMemPlugin, UbsePluginInitToleratesMissingLibrary) |
| 24 | { | 24 | { |
| 25 | uint32_t ret = UbsePluginInit(123); | 25 | uint32_t ret = UbsePluginInit(123); |
| 26 | - EXPECT_NE(ret, UBSE_OK); | 26 | + EXPECT_EQ(ret, UBSE_OK); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | TEST_F(TestProcessMemPlugin, UbsePluginDeInit) | 29 | TEST_F(TestProcessMemPlugin, UbsePluginDeInit) |