1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright (C) 2024 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import pytest
17import re
18import time
19import subprocess
20from utils import *
21
22PSS_INDEX = 2
23SWAP_PSS_INDEX = 8
24
25def ParseSmapsOutput(output):
26    memory_data = {}
27    for line in output.split("\n"):
28        if re.search(r"\d", line) is None:
29            continue
30        key = re.search(r'(/|\[|[a-zA-Z])(.*$)', line)
31        key = key.group().strip()
32        memory_data[key] = [int(val) for val in re.findall(r'\d+', line)]
33    return memory_data
34
35@print_check_result
36def CheckSmapsTotalPss(memory_data):
37    PSS_SUMMARY = memory_data["Summary"][PSS_INDEX]
38    SWAPPSS_SUMMARY = memory_data["Summary"][SWAP_PSS_INDEX]
39    return sum([val[PSS_INDEX] for key, val in memory_data.items() if key != "Summary"]) + SWAPPSS_SUMMARY == PSS_SUMMARY
40
41@print_check_result
42def CheckMemAddrss(output):
43    ret = re.search(r"\s([a-zA-Z0-9]{8,})\s", output)
44    return ret is not None
45
46@print_check_result
47def CheckPseft(output):
48    result = re.search("UID\s+PID\s+TID  PPID TCNT STIME TTY\s+TIME CMD\n([^\n]+\n){1,}", output)
49    return result is not None
50
51@print_check_result
52def CheckMountInfo(output):
53    result = re.search("/proc/\d+/mountinfo\n\n([^\n]+\n){4,}", output)
54    return result is not None
55
56def CheckSmaps(output):
57    result = re.search(r"/proc/\d+/maps", output)
58    return result is not None
59
60def CheckMemSmapsWithRoot(output):
61    memory_data = ParseSmapsOutput(output)
62    ret = all(check(memory_data) for check in [CheckSmapsTotalPss])
63    return ret
64
65def CheckMemSmapsVWithRoot(output):
66    memory_data = ParseSmapsOutput(output)
67    return CheckSmapsTotalPss(memory_data) and CheckMemAddrss(output)
68
69def CheckHelpOutput(output):
70    return "usage:" in output
71
72def CheckNetCmd(output):
73    cmds = ["iptables -L -nvx", "ip6tables -L -nvx", "iptables -t nat -L -nvx", "iptables -t mangle -L -nvx",
74            "ip6tables -t mangle -L -nvx", "iptables -t raw -L -nvx", "ip6tables -t raw -L -nvx",
75            "ip link", "ip -4 addr show", "ip -6 addr show", "ip rule show", "ip -6 rule show"]
76    ret = all([item in output for item in cmds])
77    return ret
78
79class TestHidumperPermission:
80    @classmethod
81    def setup_class(cls):
82        if not IsRootVersion():
83            subprocess.check_call("hdc shell aa start -a EntryAbility -b com.example.myapplication", shell=True)
84
85    @classmethod
86    def teardown_class(cls):
87        if not IsRootVersion():
88            subprocess.check_call("hdc shell aa force-stop -b com.example.myapplication", shell=True)
89
90    @pytest.mark.L0
91    def test_mem_smaps(self):
92        processName = "render_service"
93        pid = GetPidByProcessName(processName)
94        if (IsRootVersion()):
95            CheckFunc = CheckMemSmapsWithRoot
96        else:
97            CheckFunc = CheckHelpOutput
98        command = f"hidumper --mem-smaps {pid}"
99        # 校验命令行输出
100        CheckCmd(command, CheckFunc)
101        # 校验命令行重定向输出
102        CheckCmdRedirect(command, CheckFunc)
103        # 校验命令行输出到zip文件
104        if IsRootVersion():
105            CheckCmdZip(command, CheckFunc)
106
107    @pytest.mark.L0
108    def test_mem_smaps_v(self):
109        processName = "render_service"
110        pid = GetPidByProcessName(processName)
111        if (IsRootVersion()):
112            CheckFunc = CheckMemSmapsVWithRoot
113        else:
114            CheckFunc = CheckHelpOutput
115        command = f"hidumper --mem-smaps {pid} -v"
116        # 校验命令行输出
117        CheckCmd(command, CheckFunc)
118        # 校验命令行重定向输出
119        CheckCmdRedirect(command, CheckFunc)
120        # 校验命令行输出到zip文件
121        if IsRootVersion():
122            CheckCmdZip(command, CheckFunc)
123
124    @pytest.mark.L0
125    def test_process_all(self):
126        if IsRootVersion():
127            CheckFunc = lambda x : all([CheckSmaps(x), CheckPseft(x), CheckMountInfo(x)])
128        else:
129            CheckFunc = lambda x : all([not CheckSmaps(x), CheckPseft(x), CheckMountInfo(x)])
130            pid = GetPidByProcessName("com.example.myapplication")
131            if pid == "":
132                pytest.skip("test application not found")
133        command = f"hidumper -p"
134        # 校验命令行输出
135        CheckCmd(command, CheckFunc)
136        # 校验命令行重定向输出
137        CheckCmdRedirect(command, CheckFunc)
138        # 校验命令行输出到zip文件
139        CheckCmdZip(command, CheckFunc)
140
141    @pytest.mark.L0
142    def test_process_pid(self):
143        command = None
144        pid = 1
145        if IsRootVersion():
146            CheckFunc = lambda x : all([CheckSmaps(x), CheckPseft(x), CheckMountInfo(x)])
147        else:
148            CheckFunc = lambda x : all([not CheckSmaps(x), CheckPseft(x), CheckMountInfo(x)])
149            pid = GetPidByProcessName("com.example.myapplication")
150            if pid == "":
151                pytest.skip("test application not found")
152        command = f"hidumper -p {pid}"
153        # 校验命令行输出
154        CheckCmd(command, CheckFunc)
155        # 校验命令行重定向输出
156        CheckCmdRedirect(command, CheckFunc)
157        # 校验命令行输出到zip文件
158        CheckCmdZip(command, CheckFunc)
159
160    @pytest.mark.L0
161    def test_hidumper_help(self):
162        if not IsRootVersion():
163            output = subprocess.check_output("hdc shell \"hidumper -h |grep mem\"", shell=True, text=True, encoding="utf-8")
164            assert "--mem [pid]" in output
165
166    @pytest.mark.L0
167    def test_process_with_non_debug_pid(self):
168        if not IsRootVersion():
169            output = subprocess.check_output("hdc shell \"hidumper -p 1\"", shell=True, text=True, encoding="utf-8")
170            assert "only support debug application" in output
171
172    @pytest.mark.L0
173    def test_output_append(self):
174        if IsRootVersion():
175            command = "hdc shell \"hidumper --mem 1 >> /data/log/hidumper.log\""
176            # 校验命令行输出到zip文件
177            subprocess.check_call(command, shell=True)
178            output = subprocess.check_output("hdc shell ls -l /data/log/hidumper.log", shell=True).decode()
179            assert int(output.strip().split()[4]) > 0
180        else:
181            pytest.skip("test only in root mode")
182
183    @pytest.mark.L0
184    def test_net(self):
185        if IsRootVersion():
186            command = "hdc shell \"hidumper --net\""
187            subprocess.check_call(command, shell=True)
188            output = subprocess.check_output("hdc shell ls -l /data/log/hidumper.log", shell=True).decode()
189            assert int(output.strip().split()[4]) > 0
190        else:
191            pytest.skip("test only in root mode")
192