• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 ProcRemoveUidRangeTest
36from proc_tests import ProcSimpleFileTests
37from proc_tests import ProcShowUidStatTest
38from proc_tests import ProcStatTest
39from proc_tests import ProcUidIoStatsTest
40from proc_tests import ProcUidTimeInStateTest
41from proc_tests import ProcUidConcurrentTimeTests
42from proc_tests import ProcUidCpuPowerTests
43from proc_tests import ProcVersionTest
44from proc_tests import ProcVmallocInfoTest
45from proc_tests import ProcVmstatTest
46from proc_tests import ProcZoneInfoTest
47
48TEST_OBJECTS = {
49    ProcAsoundTests.ProcAsoundCardsTest(),
50    ProcCmdlineTest.ProcCmdlineTest(),
51    ProcCpuFileTests.ProcCpuInfoTest(),
52    ProcCpuFileTests.ProcLoadavgTest(),
53    ProcFsFileTests.ProcDiskstatsTest(),
54    ProcFsFileTests.ProcFilesystemsTest(),
55    ProcFsFileTests.ProcMountsTest(),
56    ProcFsFileTests.ProcSwapsTest(),
57    ProcKmsgTest.ProcKmsgTest(),
58    ProcMapsTest.ProcMapsTest(),
59    ProcMiscTest.ProcMisc(),
60    ProcMemInfoTest.ProcMemInfoTest(),
61    ProcModulesTest.ProcModulesTest(),
62    ProcRemoveUidRangeTest.ProcRemoveUidRangeTest(),
63    ProcSimpleFileTests.ProcCorePattern(),
64    ProcSimpleFileTests.ProcCorePipeLimit(),
65    ProcSimpleFileTests.ProcDirtyBackgroundBytes(),
66    ProcSimpleFileTests.ProcDirtyBackgroundRatio(),
67    ProcSimpleFileTests.ProcDirtyExpireCentisecs(),
68    ProcSimpleFileTests.ProcDmesgRestrict(),
69    ProcSimpleFileTests.ProcDomainname(),
70    ProcSimpleFileTests.ProcDropCaches(),
71    ProcSimpleFileTests.ProcExtraFreeKbytes(),
72    ProcSimpleFileTests.ProcHostname(),
73    ProcSimpleFileTests.ProcHungTaskTimeoutSecs(),
74    ProcSimpleFileTests.ProcKptrRestrictTest(),
75    ProcSimpleFileTests.ProcMaxMapCount(),
76    ProcSimpleFileTests.ProcMmapMinAddrTest(),
77    ProcSimpleFileTests.ProcMmapRndBitsTest(),
78    ProcSimpleFileTests.ProcModulesDisabled(),
79    ProcSimpleFileTests.ProcOverCommitMemoryTest(),
80    ProcSimpleFileTests.ProcPageCluster(),
81    ProcSimpleFileTests.ProcPanicOnOops(),
82    ProcSimpleFileTests.ProcPerfEventMaxSampleRate(),
83    ProcSimpleFileTests.ProcPerfEventParanoid(),
84    ProcSimpleFileTests.ProcPidMax(),
85    ProcSimpleFileTests.ProcPipeMaxSize(),
86    ProcSimpleFileTests.ProcProtectedHardlinks(),
87    ProcSimpleFileTests.ProcProtectedSymlinks(),
88    ProcSimpleFileTests.ProcRandomizeVaSpaceTest(),
89    ProcSimpleFileTests.ProcSchedChildRunsFirst(),
90    ProcSimpleFileTests.ProcSchedRTPeriodUS(),
91    ProcSimpleFileTests.ProcSchedRTRuntimeUS(),
92    ProcShowUidStatTest.ProcShowUidStatTest(),
93    ProcSimpleFileTests.ProcSuidDumpable(),
94    ProcSimpleFileTests.ProcSysKernelRandomBootId(),
95    ProcSimpleFileTests.ProcSysRqTest(),
96    ProcSimpleFileTests.ProcUptime(),
97    ProcStatTest.ProcStatTest(),
98    ProcUidIoStatsTest.ProcUidIoStatsTest(),
99    ProcUidTimeInStateTest.ProcUidTimeInStateTest(),
100    ProcUidConcurrentTimeTests.ProcUidConcurrentActiveTimeTest(),
101    ProcUidConcurrentTimeTests.ProcUidConcurrentPolicyTimeTest(),
102    ProcUidCpuPowerTests.ProcUidCpuPowerTimeInStateTest(),
103    ProcUidCpuPowerTests.ProcUidCpuPowerConcurrentActiveTimeTest(),
104    ProcUidCpuPowerTests.ProcUidCpuPowerConcurrentPolicyTimeTest(),
105    ProcVersionTest.ProcVersionTest(),
106    ProcVmallocInfoTest.ProcVmallocInfoTest(),
107    ProcVmstatTest.ProcVmstat(),
108    ProcZoneInfoTest.ProcZoneInfoTest(),
109}
110
111TEST_OBJECTS_64 = {
112    ProcSimpleFileTests.ProcMmapRndCompatBitsTest(),
113}
114
115
116class VtsKernelProcFileApiTest(unittest.TestCase):
117    """Test cases which check content of proc files.
118
119    Attributes:
120        _PROC_SYS_ABI_SWP_FILE_PATH: the path of a file which decides behaviour of SWP instruction.
121    """
122
123    _PROC_SYS_ABI_SWP_FILE_PATH = "/proc/sys/abi/swp"
124
125    def setUp(self):
126        """Initializes tests.
127
128        Data file path, device, remote shell instance and temporary directory
129        are initialized.
130        """
131        serial_number = os.environ.get("ANDROID_SERIAL")
132        self.assertTrue(serial_number, "$ANDROID_SERIAL is empty.")
133        self.dut = utils.AndroidDevice(serial_number)
134
135    def testProcPagetypeinfo(self):
136        # TODO(b/109884074): make mandatory once incident_helper is in AOSP.
137        out, err, r_code = self.dut.shell.Execute("which incident_helper")
138        if r_code != 0:
139            logging.info("incident_helper not present")
140            return
141
142        filepath = "/proc/pagetypeinfo"
143        # Check that incident_helper can parse /proc/pagetypeinfo.
144        out, err, r_code = self.dut.shell.Execute(
145                "cat %s | incident_helper -s 2001" % filepath)
146        self.assertEqual(
147                r_code, 0,
148            "Failed to parse %s." % filepath)
149
150    def testProcUidProcstatSet(self):
151
152        def UidIOStats(uid):
153            """Returns I/O stats for a given uid.
154
155            Args:
156                uid, uid number.
157
158            Returns:
159                list of I/O numbers, can be blank if uid not found.
160            """
161            stats_path = "/proc/uid_io/stats"
162            out, err, r_code = self.dut.shell.Execute(
163                    "cat %s | grep '^%d'" % (stats_path, uid))
164            # On a properly running system, out is a line with 11 fields that
165            # looks like this:
166            # "0 9006642940 84253078 9751207936 1064480768 0 0 0 0 1048 0"
167            # where the stat at each index corresponds to the following:
168            #     0 : uid
169            #     1 : fg_rchar
170            #     2 : fg_wchar
171            #     3 : fg_rbytes
172            #     4 : fg_wbytes
173            #     5 : bg_rchar
174            #     6 : bg_wchar
175            #     7 : bg_rbytes
176            #     8 : bg_wbytes
177            #     9 : fg_fsync
178            #     10: bg_fsync
179            return out.split()
180
181        def GetUidIoStat(uid, index):
182            """Returns the I/O stat at the given index for a given uid.
183
184            Args:
185                uid, uid number.
186                index, index of the desired I/O stat in the array.
187
188            Returns:
189                value of the I/O stat at the given index.
190            """
191            stats = UidIOStats(uid)
192            # UidIOStats() can return a blank line if the entries are not found
193            # so we need to check the length of the return to prevent a list
194            # index out of range exception.
195            arr_len = len(stats)
196
197            self.assertTrue(arr_len == 11,
198                            "Array len returned by UidIOStats() unexpected: %d" %
199                            arr_len)
200            self.assertTrue(index < 11,
201                            "Index passed into GetUidIoStat out of bounds: %d" %
202                            index)
203
204            return int(stats[index])
205
206
207        def GetWcharCount(uid, state):
208            """Returns the wchar count (bytes written) for a given uid.
209
210            Args:
211                uid, uid number.
212                state, boolean. Use False for foreground,
213                and True for background.
214
215            Returns:
216                wchar, the number of bytes written by a uid in the given state.
217            """
218            # fg write chars are at index 2, and bg write chars are at 6.
219            wchar_index = 6 if state else 2
220
221            return GetUidIoStat(uid, wchar_index)
222
223        def GetFsyncCount(uid, state):
224            """Returns the fsync syscall count by a given uid.
225
226            Args:
227                uid, uid number.
228                state, boolean. Use False for foreground,
229                and True for background.
230
231            Returns:
232                fsync, the number of calls to fsync by a uid in the given state.
233            """
234            # fg write chars are at index 2, and bg write chars are at 6.
235            fsync_index = 10 if state else 9
236
237            return GetUidIoStat(uid, fsync_index)
238
239        def CheckStatsInState(state):
240            """Sets VTS (root uid) into a given state and checks the stats.
241
242            Args:
243                state, boolean. Use False for foreground,
244                and True for background.
245            """
246            state = 1 if state else 0
247            filepath = "/proc/uid_procstat/set"
248            root_uid = 0
249
250            old_wchar = GetWcharCount(root_uid, state)
251            old_fsync = GetFsyncCount(root_uid, state)
252            self.dut.shell.Execute("echo %d %s > %s" % (root_uid, state, filepath))
253
254            # This should increase the number of write syscalls.
255            self.dut.shell.Execute("echo foo")
256            new_wchar = GetWcharCount(root_uid, state)
257
258            # This should increase the number of fsync syscalls.
259            self.dut.shell.Execute("fsync /tmp")
260            new_fsync = GetFsyncCount(root_uid, state)
261
262            self.assertLess(
263                old_wchar,
264                new_wchar,
265                "Number of write syscalls has not increased.")
266
267            self.assertLess(
268                old_fsync,
269                new_fsync,
270                "Number of fsync syscalls has not increased.")
271
272        CheckStatsInState(False)
273        CheckStatsInState(True)
274
275    def testProcPerUidTimes(self):
276        # TODO: make these files mandatory once they're in AOSP
277        try:
278            filepaths = self.dut.FindFiles('/proc/uid', 'time_in_state')
279        except:
280            logging.info("/proc/uid/ directory does not exist and is optional")
281            return
282
283        if not filepaths:
284            logging.info('per-UID time_in_state files do not exist and are optional')
285            return
286
287        for filepath in filepaths:
288            self.assertTrue(self.dut.Exists(filepath),
289                            '%s does not exist.' % filepath)
290            permission = self.dut.GetPermission(filepath)
291            self.assertTrue(target_file_utils.IsReadOnly(permission))
292            file_content = self.dut.ReadFileContent(filepath)
293
294    def testProcSysAbiSwpInstruction(self):
295        """Tests /proc/sys/abi/swp.
296
297        /proc/sys/abi/swp sets the execution behaviour for the obsoleted ARM instruction
298        SWP. As per the setting in /proc/sys/abi/swp, the usage of SWP{B}
299        can either generate an undefined instruction abort or use software emulation
300        or hardware execution.
301        """
302        if not ('arm' in self.cpu_abi(self.dut) and self.is64Bit(self.dut)):
303            logging.info("file not present on non-ARM64 device")
304            return
305
306        filepath = '/proc/sys/abi/swp'
307
308        self.assertTrue(self.dut.Exists(filepath), '%s does not exist.' % filepath)
309        permission = self.dut.GetPermission(filepath)
310        self.assertTrue(target_file_utils.IsReadWrite(permission))
311
312        file_content = self.dut.ReadFileContent(filepath)
313        try:
314            swp_state = int(file_content)
315        except ValueError as e:
316            self.fail("Failed to parse %s" % filepath)
317        self.assertTrue(swp_state >= 0 and swp_state <= 2,
318                        "%s contains incorrect value: %d"
319                        % (filepath, swp_state))
320
321    def cpu_abi(self, dut):
322        """CPU ABI (Application Binary Interface) of the device."""
323        out = dut._GetProp("ro.product.cpu.abi")
324        if not out:
325            return "unknown"
326
327        cpu_abi = out.lower()
328        return cpu_abi
329
330    def is64Bit(self, dut):
331        """True if device is 64 bit."""
332        out, _, _ = dut.shell.Execute('uname -m')
333        return "64" in out
334
335
336def run_proc_file_test(test_object):
337    """Reads from the file and checks that it parses and the content is valid.
338
339    Args:
340        test_object: inherits KernelProcFileTestBase, contains the test functions
341    """
342    def test(self):
343        if test_object in TEST_OBJECTS_64 and not self.is64Bit(self.dut):
344            logging.info("Skip test for 64-bit kernel.")
345            return
346        test_object.set_api_level(self.dut)
347        filepath = test_object.get_path()
348        if not self.dut.Exists(filepath) and test_object.file_optional(dut=self.dut):
349            logging.info("%s does not exist and is optional." % filepath)
350            return
351        self.assertTrue(self.dut.Exists(filepath), '%s does not exist.' % filepath)
352        permission = self.dut.GetPermission(filepath)
353        self.assertTrue(test_object.get_permission_checker())
354        self.assertTrue(test_object.get_permission_checker()(permission),
355                        "%s: File has invalid permissions (%s)" % (filepath,
356                                                                   permission))
357
358        logging.info("Testing format of %s", filepath)
359        self.assertTrue(
360            test_object.prepare_test(self.dut), "Setup failed!")
361
362        if not test_object.test_format():
363            return
364
365        file_content = self.dut.ReadFileContent(filepath)
366        try:
367            parse_result = test_object.parse_contents(file_content)
368        except (SyntaxError, ValueError, IndexError) as e:
369            self.fail("Failed to parse! " + str(e))
370        self.assertTrue(
371            test_object.result_correct(parse_result), "Results not valid!")
372    return test
373
374
375if __name__ == "__main__":
376    try:
377        for test_object in TEST_OBJECTS.union(TEST_OBJECTS_64):
378            test_func = run_proc_file_test(test_object)
379            setattr(VtsKernelProcFileApiTest, 'test_' +
380                    test_object.__class__.__name__, test_func)
381        suite = unittest.TestLoader().loadTestsFromTestCase(
382            VtsKernelProcFileApiTest)
383        results = unittest.TextTestRunner(verbosity=2).run(suite)
384    finally:
385        if results.failures:
386            sys.exit(1)
387