1#!/usr/bin/env python
2#
3# Copyright (C) 2020 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import logging
19import os
20import sys
21import unittest
22
23import target_file_utils
24import proc_utils as utils
25
26from proc_tests import ProcAsoundTests
27from proc_tests import ProcCmdlineTest
28from proc_tests import ProcCpuFileTests
29from proc_tests import ProcFsFileTests
30from proc_tests import ProcKmsgTest
31from proc_tests import ProcMapsTest
32from proc_tests import ProcMiscTest
33from proc_tests import ProcMemInfoTest
34from proc_tests import ProcModulesTest
35from proc_tests import ProcQtaguidCtrlTest
36from proc_tests import ProcRemoveUidRangeTest
37from proc_tests import ProcSimpleFileTests
38from proc_tests import ProcShowUidStatTest
39from proc_tests import ProcStatTest
40from proc_tests import ProcUidIoStatsTest
41from proc_tests import ProcUidTimeInStateTest
42from proc_tests import ProcUidConcurrentTimeTests
43from proc_tests import ProcUidCpuPowerTests
44from proc_tests import ProcVersionTest
45from proc_tests import ProcVmallocInfoTest
46from proc_tests import ProcVmstatTest
47from proc_tests import ProcZoneInfoTest
48
49TEST_OBJECTS = {
50    ProcAsoundTests.ProcAsoundCardsTest(),
51    ProcCmdlineTest.ProcCmdlineTest(),
52    ProcCpuFileTests.ProcCpuInfoTest(),
53    ProcCpuFileTests.ProcLoadavgTest(),
54    ProcFsFileTests.ProcDiskstatsTest(),
55    ProcFsFileTests.ProcFilesystemsTest(),
56    ProcFsFileTests.ProcMountsTest(),
57    ProcFsFileTests.ProcSwapsTest(),
58    ProcKmsgTest.ProcKmsgTest(),
59    ProcMapsTest.ProcMapsTest(),
60    ProcMiscTest.ProcMisc(),
61    ProcMemInfoTest.ProcMemInfoTest(),
62    ProcModulesTest.ProcModulesTest(),
63    ProcQtaguidCtrlTest.ProcQtaguidCtrlTest(),
64    ProcRemoveUidRangeTest.ProcRemoveUidRangeTest(),
65    ProcSimpleFileTests.ProcCorePattern(),
66    ProcSimpleFileTests.ProcCorePipeLimit(),
67    ProcSimpleFileTests.ProcDirtyBackgroundBytes(),
68    ProcSimpleFileTests.ProcDirtyBackgroundRatio(),
69    ProcSimpleFileTests.ProcDirtyExpireCentisecs(),
70    ProcSimpleFileTests.ProcDmesgRestrict(),
71    ProcSimpleFileTests.ProcDomainname(),
72    ProcSimpleFileTests.ProcDropCaches(),
73    ProcSimpleFileTests.ProcExtraFreeKbytes(),
74    ProcSimpleFileTests.ProcHostname(),
75    ProcSimpleFileTests.ProcHungTaskTimeoutSecs(),
76    ProcSimpleFileTests.ProcKptrRestrictTest(),
77    ProcSimpleFileTests.ProcMaxMapCount(),
78    ProcSimpleFileTests.ProcMmapMinAddrTest(),
79    ProcSimpleFileTests.ProcMmapRndBitsTest(),
80    ProcSimpleFileTests.ProcModulesDisabled(),
81    ProcSimpleFileTests.ProcOverCommitMemoryTest(),
82    ProcSimpleFileTests.ProcPageCluster(),
83    ProcSimpleFileTests.ProcPanicOnOops(),
84    ProcSimpleFileTests.ProcPerfEventMaxSampleRate(),
85    ProcSimpleFileTests.ProcPerfEventParanoid(),
86    ProcSimpleFileTests.ProcPidMax(),
87    ProcSimpleFileTests.ProcPipeMaxSize(),
88    ProcSimpleFileTests.ProcProtectedHardlinks(),
89    ProcSimpleFileTests.ProcProtectedSymlinks(),
90    ProcSimpleFileTests.ProcRandomizeVaSpaceTest(),
91    ProcSimpleFileTests.ProcSchedChildRunsFirst(),
92    ProcSimpleFileTests.ProcSchedLatencyNS(),
93    ProcSimpleFileTests.ProcSchedRTPeriodUS(),
94    ProcSimpleFileTests.ProcSchedRTRuntimeUS(),
95    ProcSimpleFileTests.ProcSchedTunableScaling(),
96    ProcSimpleFileTests.ProcSchedWakeupGranularityNS(),
97    ProcShowUidStatTest.ProcShowUidStatTest(),
98    ProcSimpleFileTests.ProcSuidDumpable(),
99    ProcSimpleFileTests.ProcSysKernelRandomBootId(),
100    ProcSimpleFileTests.ProcSysRqTest(),
101    ProcSimpleFileTests.ProcUptime(),
102    ProcStatTest.ProcStatTest(),
103    ProcUidIoStatsTest.ProcUidIoStatsTest(),
104    ProcUidTimeInStateTest.ProcUidTimeInStateTest(),
105    ProcUidConcurrentTimeTests.ProcUidConcurrentActiveTimeTest(),
106    ProcUidConcurrentTimeTests.ProcUidConcurrentPolicyTimeTest(),
107    ProcUidCpuPowerTests.ProcUidCpuPowerTimeInStateTest(),
108    ProcUidCpuPowerTests.ProcUidCpuPowerConcurrentActiveTimeTest(),
109    ProcUidCpuPowerTests.ProcUidCpuPowerConcurrentPolicyTimeTest(),
110    ProcVersionTest.ProcVersionTest(),
111    ProcVmallocInfoTest.ProcVmallocInfoTest(),
112    ProcVmstatTest.ProcVmstat(),
113    ProcZoneInfoTest.ProcZoneInfoTest(),
114}
115
116TEST_OBJECTS_64 = {
117    ProcSimpleFileTests.ProcMmapRndCompatBitsTest(),
118}
119
120
121class VtsKernelProcFileApiTest(unittest.TestCase):
122    """Test cases which check content of proc files.
123
124    Attributes:
125        _PROC_SYS_ABI_SWP_FILE_PATH: the path of a file which decides behaviour of SWP instruction.
126    """
127
128    _PROC_SYS_ABI_SWP_FILE_PATH = "/proc/sys/abi/swp"
129
130    def setUp(self):
131        """Initializes tests.
132
133        Data file path, device, remote shell instance and temporary directory
134        are initialized.
135        """
136        serial_number = os.environ.get("ANDROID_SERIAL")
137        self.assertTrue(serial_number, "$ANDROID_SERIAL is empty.")
138        self.dut = utils.AndroidDevice(serial_number)
139
140    def testProcPagetypeinfo(self):
141        # TODO(b/109884074): make mandatory once incident_helper is in AOSP.
142        out, err, r_code = self.dut.shell.Execute("which incident_helper")
143        if r_code != 0:
144            logging.info("incident_helper not present")
145            return
146
147        filepath = "/proc/pagetypeinfo"
148        # Check that incident_helper can parse /proc/pagetypeinfo.
149        out, err, r_code = self.dut.shell.Execute(
150                "cat %s | incident_helper -s 2001" % filepath)
151        self.assertEqual(
152                r_code, 0,
153            "Failed to parse %s." % filepath)
154
155    def testProcSysrqTrigger(self):
156        filepath = "/proc/sysrq-trigger"
157
158        # This command only performs a best effort attempt to remount all
159        # filesystems. Check that it doesn't throw an error.
160        self.dut.shell.Execute("echo u > %s" % filepath)
161
162        # Reboot the device.
163        self.dut.shell.Execute("echo b > %s" % filepath)
164        self.assertTrue(self.dut.IsShutdown(10), "Device is still alive.")
165        self.assertTrue(self.dut.WaitForBootCompletion(300))
166        self.assertTrue(self.dut.Root())
167
168    def testProcUidProcstatSet(self):
169
170        def UidIOStats(uid):
171            """Returns I/O stats for a given uid.
172
173            Args:
174                uid, uid number.
175
176            Returns:
177                list of I/O numbers.
178            """
179            stats_path = "/proc/uid_io/stats"
180            out, err, r_code = self.dut.shell.Execute(
181                    "cat %s | grep '^%d'" % (stats_path, uid))
182            return out.split()
183
184        def CheckStatsInState(state):
185            """Sets VTS (root uid) into a given state and checks the stats.
186
187            Args:
188                state, boolean. Use False for foreground,
189                and True for background.
190            """
191            state = 1 if state else 0
192            filepath = "/proc/uid_procstat/set"
193            root_uid = 0
194
195            # fg write chars are at index 2, and bg write chars are at 6.
196            wchar_index = 6 if state else 2
197            old_wchar = UidIOStats(root_uid)[wchar_index]
198            self.dut.shell.Execute("echo %d %s > %s" % (root_uid, state, filepath))
199            # This should increase the number of write syscalls.
200            self.dut.shell.Execute("echo foo")
201            self.assertLess(
202                int(old_wchar),
203                int(UidIOStats(root_uid)[wchar_index]),
204                "Number of write syscalls has not increased.")
205
206        CheckStatsInState(False)
207        CheckStatsInState(True)
208
209    def testProcPerUidTimes(self):
210        # TODO: make these files mandatory once they're in AOSP
211        try:
212            filepaths = self.dut.FindFiles('/proc/uid', 'time_in_state')
213        except:
214            logging.info("/proc/uid/ directory does not exist and is optional")
215            return
216
217        if not filepaths:
218            logging.info('per-UID time_in_state files do not exist and are optional')
219            return
220
221        for filepath in filepaths:
222            self.assertTrue(self.dut.Exists(filepath),
223                            '%s does not exist.' % filepath)
224            permission = self.dut.GetPermission(filepath)
225            self.assertTrue(target_file_utils.IsReadOnly(permission))
226            file_content = self.dut.ReadFileContent(filepath)
227
228    def testProcSysAbiSwpInstruction(self):
229        """Tests /proc/sys/abi/swp.
230
231        /proc/sys/abi/swp sets the execution behaviour for the obsoleted ARM instruction
232        SWP. As per the setting in /proc/sys/abi/swp, the usage of SWP{B}
233        can either generate an undefined instruction abort or use software emulation
234        or hardware execution.
235        """
236        if not ('arm' in self.cpu_abi(self.dut) and self.is64Bit(self.dut)):
237            logging.info("file not present on non-ARM64 device")
238            return
239
240        filepath = '/proc/sys/abi/swp'
241
242        self.assertTrue(self.dut.Exists(filepath), '%s does not exist.' % filepath)
243        permission = self.dut.GetPermission(filepath)
244        self.assertTrue(target_file_utils.IsReadWrite(permission))
245
246        file_content = self.dut.ReadFileContent(filepath)
247        try:
248            swp_state = int(file_content)
249        except ValueError as e:
250            self.fail("Failed to parse %s" % filepath)
251        self.assertTrue(swp_state >= 0 and swp_state <= 2,
252                        "%s contains incorrect value: %d"
253                        % (filepath, swp_state))
254
255    def cpu_abi(self, dut):
256        """CPU ABI (Application Binary Interface) of the device."""
257        out = dut._GetProp("ro.product.cpu.abi")
258        if not out:
259            return "unknown"
260
261        cpu_abi = out.lower()
262        return cpu_abi
263
264    def is64Bit(self, dut):
265        """True if device is 64 bit."""
266        out, _, _ = dut.shell.Execute('uname -m')
267        return "64" in out
268
269
270def run_proc_file_test(test_object):
271    """Reads from the file and checks that it parses and the content is valid.
272
273    Args:
274        test_object: inherits KernelProcFileTestBase, contains the test functions
275    """
276    def test(self):
277        if test_object in TEST_OBJECTS_64 and not self.is64Bit(self.dut):
278            logging.info("Skip test for 64-bit kernel.")
279            return
280        test_object.set_api_level(self.dut)
281        filepath = test_object.get_path()
282        if not self.dut.Exists(filepath) and test_object.file_optional(dut=self.dut):
283            logging.info("%s does not exist and is optional." % filepath)
284            return
285        self.assertTrue(self.dut.Exists(filepath), '%s does not exist.' % filepath)
286        permission = self.dut.GetPermission(filepath)
287        self.assertTrue(test_object.get_permission_checker())
288        self.assertTrue(test_object.get_permission_checker()(permission),
289                        "%s: File has invalid permissions (%s)" % (filepath,
290                                                                   permission))
291
292        logging.info("Testing format of %s", filepath)
293        self.assertTrue(
294            test_object.prepare_test(self.dut), "Setup failed!")
295
296        if not test_object.test_format():
297            return
298
299        file_content = self.dut.ReadFileContent(filepath)
300        try:
301            parse_result = test_object.parse_contents(file_content)
302        except (SyntaxError, ValueError, IndexError) as e:
303            self.fail("Failed to parse! " + str(e))
304        self.assertTrue(
305            test_object.result_correct(parse_result), "Results not valid!")
306    return test
307
308
309if __name__ == "__main__":
310    try:
311        for test_object in TEST_OBJECTS.union(TEST_OBJECTS_64):
312            test_func = run_proc_file_test(test_object)
313            setattr(VtsKernelProcFileApiTest, 'test_' +
314                    test_object.__class__.__name__, test_func)
315        suite = unittest.TestLoader().loadTestsFromTestCase(
316            VtsKernelProcFileApiTest)
317        results = unittest.TextTestRunner(verbosity=2).run(suite)
318    finally:
319        if results.failures:
320            sys.exit(1)
321