• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2#
3# Copyright (C) 2018 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
17import argparse
18import itertools
19import json
20import sqlite3
21
22class SqliteWriter(object):
23    def __init__(self):
24        self.sample_count = 0
25        self.dso_map = {}
26        self.pid_map = {}
27        self.tid_map = {}
28        self.symbol_map = {}
29
30    def open(self, out):
31        self._conn = sqlite3.connect(out)
32        self._c = self._conn.cursor()
33        # Ensure tables exist
34        # The sample replicates pid and tid.
35        try:
36            self._c.execute('''CREATE TABLE pids (id integer PRIMARY KEY AUTOINCREMENT,
37                                                  name text)''')
38            self._c.execute('''CREATE TABLE tids (id integer PRIMARY KEY AUTOINCREMENT,
39                                                  name text)''')
40            self._c.execute('''CREATE TABLE syms (id integer PRIMARY KEY AUTOINCREMENT,
41                                                  name text)''')
42            self._c.execute('''CREATE TABLE dsos (id integer PRIMARY KEY AUTOINCREMENT,
43                                                  name text)''')
44            self._c.execute('''CREATE TABLE samples (id integer PRIMARY KEY AUTOINCREMENT,
45                                                     pid_id int not null,
46                                                     tid_id int not null)
47                                                     ''')
48            self._c.execute('''CREATE TABLE stacks (sample_id int not null,
49                                                    depth int not null,
50                                                    dso_id int not null,
51                                                    sym_id int not null,
52                                                    offset int not null,
53                                                    primary key (sample_id, depth))
54                                                    ''')
55        except sqlite3.OperationalError:
56            pass # ignore
57
58    def close(self):
59        self._conn.commit()
60        self._conn.close()
61
62    def insert_into_tmp_or_get(self, name, table_dict, table_dict_tmp):
63        if name in table_dict:
64            return table_dict[name]
65        if name in table_dict_tmp:
66            return table_dict_tmp[name]
67        index = len(table_dict) + len(table_dict_tmp)
68        table_dict_tmp[name] = index
69        return index
70
71    def prepare(self):
72        self.dso_tmp_map = {}
73        self.pid_tmp_map = {}
74        self.tid_tmp_map = {}
75        self.symbol_tmp_map = {}
76        self.samples_tmp_list = []
77        self.stacks_tmp_list = []
78
79    def write_sqlite_index_table(self, table_dict, table_name):
80        for key, value in table_dict.iteritems():
81            self._c.execute("insert into {tn} values (?,?)".format(tn=table_name), (value,key))
82
83    def flush(self):
84        self.write_sqlite_index_table(self.pid_tmp_map, 'pids')
85        self.write_sqlite_index_table(self.tid_tmp_map, 'tids')
86        self.write_sqlite_index_table(self.dso_tmp_map, 'dsos')
87        self.write_sqlite_index_table(self.symbol_tmp_map, 'syms')
88
89        for sample in self.samples_tmp_list:
90            self._c.execute("insert into samples values (?,?,?)", sample)
91        for stack in self.stacks_tmp_list:
92            self._c.execute("insert into stacks values (?,?,?,?,?)", stack)
93
94        self.pid_map.update(self.pid_tmp_map)
95        self.tid_map.update(self.tid_tmp_map)
96        self.dso_map.update(self.dso_tmp_map)
97        self.symbol_map.update(self.symbol_tmp_map)
98
99        self.dso_tmp_map = {}
100        self.pid_tmp_map = {}
101        self.tid_tmp_map = {}
102        self.symbol_tmp_map = {}
103        self.samples_tmp_list = []
104        self.stacks_tmp_list = []
105
106    def add_sample(self, sample, tid_name_map):
107        sample_id = self.sample_count
108        self.sample_count = self.sample_count + 1
109
110        def get_name(pid, name_map):
111            if pid in name_map:
112                return name_map[pid]
113            pid_str = str(pid)
114            if pid_str in name_map:
115                return name_map[pid_str]
116            if pid == 0:
117                return "[kernel]"
118            return "[unknown]"
119
120        pid_name = get_name(sample[0], tid_name_map)
121        pid_id = self.insert_into_tmp_or_get(pid_name, self.pid_map, self.pid_tmp_map)
122        tid_name = get_name(sample[1], tid_name_map)
123        tid_id = self.insert_into_tmp_or_get(tid_name, self.tid_map, self.tid_tmp_map)
124
125        self.samples_tmp_list.append((sample_id, pid_id, tid_id))
126
127        stack_depth = 0
128        for entry in sample[2]:
129            sym_id = self.insert_into_tmp_or_get(entry[0], self.symbol_map, self.symbol_tmp_map)
130            dso = entry[2]
131            if dso is None:
132                dso = "None"
133            dso_id = self.insert_into_tmp_or_get(dso, self.dso_map, self.dso_tmp_map)
134
135            self.stacks_tmp_list.append((sample_id, stack_depth, dso_id, sym_id, entry[1]))
136
137            stack_depth = stack_depth + 1
138
139
140if __name__ == "__main__":
141    parser = argparse.ArgumentParser(description='''Process a set of perfprofd JSON files produced
142                                                    by perf_proto_stack.py into SQLite database''')
143
144    parser.add_argument('file', help='JSON files to parse and combine', metavar='file', nargs='+')
145
146    parser.add_argument('--sqlite-out', help='SQLite database output', type=str,
147                        default='sqlite.db')
148
149    args = parser.parse_args()
150    if args is not None:
151        sql_out = SqliteWriter()
152        sql_out.open(args.sqlite_out)
153        sql_out.prepare()
154
155        for f in args.file:
156            print 'Processing %s' % (f)
157            fp = open(f, 'r')
158            data = json.load(fp)
159            fp.close()
160
161            for sample in data['samples']:
162                sql_out.add_sample(sample, data['names'])
163
164            sql_out.flush()
165
166        sql_out.close()
167