1# SPDX-License-Identifier: Apache-2.0
2#
3# Copyright (C) 2017, ARM Limited and contributors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# 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, WITHOUT
13# 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# Parser for dumpsys gfxinfo output
18
19import ast, re
20
21def get_value(token):
22    try:
23        v = ast.literal_eval(token)
24    except:
25        return token
26    return v
27
28class GfxInfo(object):
29    """
30    Class for parsing and accessing GfxInfo output
31    """
32    __properties = {}
33
34    def __init__(self, path):
35        """
36        Initialize gfxinfo parser object
37
38        :param path: Path to file containing output of gfxinfo
39        """
40        self.path = path
41        self.parse_gfxinfo()
42
43    def parse_gfxinfo(self):
44        """
45        Parser for gfxinfo output, creates a name/value pair
46        """
47        with open(self.path) as f:
48            content = f.readlines()
49
50        parsing_histogram = False
51
52        # gfxinfo has several statistics of the format
53        # <string>: <value>
54        for line in content:
55            line = line.rstrip()
56            # Ignore any lines that aren't of this format
57            if not ':' in line or line.endswith(':'):
58                continue
59
60            tokens = line.split(':')
61            # convert <string> into a key by replacing spaces with '_'
62            tokens = [t.strip() for t in tokens]
63            tokens[0] = tokens[0].replace(' ', '_').lower()
64
65            # Parse janky_frames. Ex: "Janky frames: 44 (26.99%)"
66            if tokens[0] == 'janky_frames':
67                (frames, pc) = tokens[1].split(' ')
68                self.__properties["janky_frames"] = get_value(frames)
69                pc = re.sub('[\(\)\%]', '', pc)
70                self.__properties["janky_frames_pc"] = get_value(pc)
71                continue
72            # Parse 'nth_percentile: <int>ms' into nth_percentile_ms=<int>
73            if tokens[1].endswith('ms'):
74                tokens[0] = tokens[0] + '_ms'
75                tokens[1] = tokens[1][:-2]
76            # Begin parsing the histogram of frame times
77            if tokens[0] == 'histogram':
78                parsing_histogram = True
79                self.__properties['histogram'] = []
80                line = line[11:]
81            # Extracts the frame time data into an array of tuples
82            # Ex: 5ms=50 6ms=60 7ms=70 ... -> [(5, 50), (6, 60), (7, 70), ...]
83            if parsing_histogram:
84                h_tokens = [x.rstrip() for x in line.split(' ')]
85                if 'ms=' in line:
86                    for h_value in [x.split('=') for x in h_tokens]:
87                        self.__properties['histogram'].append((int(h_value[0][:-2]), int(h_value[1])))
88                else:
89                    parsing_histogram = False
90                continue
91
92            # Regular parsing
93            self.__properties[tokens[0]] = get_value(tokens[1])
94
95    def __dir__(self):
96        """
97        List all available attributes including ones parsed from
98        gfxinfo output
99        """
100        return self.__properties.keys()
101
102    def __getattr__(self, name):
103        """
104        Get the gfxinfo property using the period operator
105        Ex: obj.number_missed_vsync
106        """
107        return self.__properties[name]
108
109    def __getitem__(self, name):
110        """
111        Get the gfxinfo property using the [] opertator
112        Useful for attributes like "50th_percentile" that can't
113        be fetched with the period operator
114        """
115        return self.__properties[name]
116