#!/usr/bin/python # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import argparse import itertools import json import sqlite3 class SqliteWriter(object): def __init__(self): self.sample_count = 0 self.dso_map = {} self.pid_map = {} self.tid_map = {} self.symbol_map = {} def open(self, out): self._conn = sqlite3.connect(out) self._c = self._conn.cursor() # Ensure tables exist # The sample replicates pid and tid. try: self._c.execute('''CREATE TABLE pids (id integer PRIMARY KEY AUTOINCREMENT, name text)''') self._c.execute('''CREATE TABLE tids (id integer PRIMARY KEY AUTOINCREMENT, name text)''') self._c.execute('''CREATE TABLE syms (id integer PRIMARY KEY AUTOINCREMENT, name text)''') self._c.execute('''CREATE TABLE dsos (id integer PRIMARY KEY AUTOINCREMENT, name text)''') self._c.execute('''CREATE TABLE samples (id integer PRIMARY KEY AUTOINCREMENT, pid_id int not null, tid_id int not null) ''') self._c.execute('''CREATE TABLE stacks (sample_id int not null, depth int not null, dso_id int not null, sym_id int not null, offset int not null, primary key (sample_id, depth)) ''') except sqlite3.OperationalError: pass # ignore def close(self): self._conn.commit() self._conn.close() def insert_into_tmp_or_get(self, name, table_dict, table_dict_tmp): if name in table_dict: return table_dict[name] if name in table_dict_tmp: return table_dict_tmp[name] index = len(table_dict) + len(table_dict_tmp) table_dict_tmp[name] = index return index def prepare(self): self.dso_tmp_map = {} self.pid_tmp_map = {} self.tid_tmp_map = {} self.symbol_tmp_map = {} self.samples_tmp_list = [] self.stacks_tmp_list = [] def write_sqlite_index_table(self, table_dict, table_name): for key, value in table_dict.iteritems(): self._c.execute("insert into {tn} values (?,?)".format(tn=table_name), (value, key)) def flush(self): self.write_sqlite_index_table(self.pid_tmp_map, 'pids') self.write_sqlite_index_table(self.tid_tmp_map, 'tids') self.write_sqlite_index_table(self.dso_tmp_map, 'dsos') self.write_sqlite_index_table(self.symbol_tmp_map, 'syms') for sample in self.samples_tmp_list: self._c.execute("insert into samples values (?,?,?)", sample) for stack in self.stacks_tmp_list: self._c.execute("insert into stacks values (?,?,?,?,?)", stack) self.pid_map.update(self.pid_tmp_map) self.tid_map.update(self.tid_tmp_map) self.dso_map.update(self.dso_tmp_map) self.symbol_map.update(self.symbol_tmp_map) self.dso_tmp_map = {} self.pid_tmp_map = {} self.tid_tmp_map = {} self.symbol_tmp_map = {} self.samples_tmp_list = [] self.stacks_tmp_list = [] def add_sample(self, sample, tid_name_map): sample_id = self.sample_count self.sample_count = self.sample_count + 1 def get_name(pid, name_map): if pid in name_map: return name_map[pid] pid_str = str(pid) if pid_str in name_map: return name_map[pid_str] if pid == 0: return "[kernel]" return "[unknown]" pid_name = get_name(sample[0], tid_name_map) pid_id = self.insert_into_tmp_or_get(pid_name, self.pid_map, self.pid_tmp_map) tid_name = get_name(sample[1], tid_name_map) tid_id = self.insert_into_tmp_or_get(tid_name, self.tid_map, self.tid_tmp_map) self.samples_tmp_list.append((sample_id, pid_id, tid_id)) stack_depth = 0 for entry in sample[2]: sym_id = self.insert_into_tmp_or_get(entry[0], self.symbol_map, self.symbol_tmp_map) dso = entry[2] if dso is None: dso = "None" dso_id = self.insert_into_tmp_or_get(dso, self.dso_map, self.dso_tmp_map) self.stacks_tmp_list.append((sample_id, stack_depth, dso_id, sym_id, entry[1])) stack_depth = stack_depth + 1 if __name__ == "__main__": parser = argparse.ArgumentParser(description='''Process a set of perfprofd JSON files produced by perf_proto_stack.py into SQLite database''') parser.add_argument('file', help='JSON files to parse and combine', metavar='file', nargs='+') parser.add_argument('--sqlite-out', help='SQLite database output', type=str, default='sqlite.db') args = parser.parse_args() if args is not None: sql_out = SqliteWriter() sql_out.open(args.sqlite_out) sql_out.prepare() for f in args.file: print 'Processing %s' % (f) fp = open(f, 'r') data = json.load(fp) fp.close() for sample in data['samples']: sql_out.add_sample(sample, data['names']) sql_out.flush() sql_out.close()