blob: 819e4ca2331447874dc5bae92ef8b484c3d93243 [file] [log] [blame]
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
#include <cassert>
#include <cstring>
#include <iostream>
#include <ostream>
#include <stack>
#include "tree.h"
#include "profiler_data.h" // part of window_manager
namespace profiler = window_manager::profiler;
struct Profile {
profiler::Symbol* symbols;
profiler::Sample* samples;
int num_symbols;
int num_samples;
};
bool LoadProfileFromFile(const char* filename, Profile* pf) {
FILE* fp = fopen(filename, "rb");
if (fp == NULL) {
return false;
}
int result = 0;
int max_num_symbols;
result += fread(&max_num_symbols, sizeof(max_num_symbols), 1, fp);
result += fread(&pf->num_symbols, sizeof(pf->num_symbols), 1, fp);
result += fread(&pf->num_samples, sizeof(pf->num_samples), 1, fp);
pf->symbols = new profiler::Symbol[pf->num_symbols];
pf->samples = new profiler::Sample[pf->num_samples];
if (pf->symbols == NULL || pf->samples == NULL) {
return false;
}
result += fread(pf->symbols, sizeof(pf->symbols[0]), pf->num_symbols, fp);
result += fseek(fp,
sizeof(pf->symbols[0]) * (max_num_symbols - pf->num_symbols),
SEEK_CUR);
result += fread(pf->samples, sizeof(pf->samples[0]), pf->num_samples, fp);
fclose(fp);
if (result != 3 + pf->num_symbols + pf->num_samples) {
return false;
}
return true;
}
int BuildTreeFromProfile(const Profile& pf, TreeNode* current) {
using std::stack;
stack<TreeNode*> tree_stack;
stack<profiler::Sample*> data_stack;
int frame = 0;
for (int i = 0; i < pf.num_samples; i++) {
const profiler::Sample& sample = pf.samples[i];
const profiler::Symbol& symbol = pf.symbols[sample.symbol_id];
switch (sample.flag) {
case profiler::MARK_FLAG_BEGIN: {
TreeNode* node = current->GetChild(sample.symbol_id);
// The markers from frame to frame might be different, if we see a new
// marker, add it to the tree.
if (node == NULL) {
node = new TreeNode(symbol.name);
current->AddChild(sample.symbol_id, node);
}
tree_stack.push(current);
data_stack.push(const_cast<profiler::Sample*>(&sample));
current = node;
break;
}
case profiler::MARK_FLAG_END: {
// The user might start profiling somewhere in the middle, so an
// end marker might appear before a start.
if (data_stack.empty()) {
continue;
}
profiler::Sample* start_sample = data_stack.top();
data_stack.pop();
assert(start_sample->symbol_id == sample.symbol_id);
current->data()[frame].count += 1;
current->data()[frame].total_time += sample.time - start_sample->time;
current = tree_stack.top();
tree_stack.pop();
// Simple way to find frame boundary is to assume everything in a frame
// are enclosed in the root marker.
if (tree_stack.empty()) {
frame++;
}
break;
}
case profiler::MARK_FLAG_TAP: {
// TODO: implement
break;
}
default: {
assert(false);
}
}
}
return frame;
}
// Filename of the profile should be passed in argv[1].
int main(int argc, char** argv) {
using namespace std;
if (argc != 2 && (argc != 3 || strcmp(argv[2], "detail") != 0)) {
cerr << "Usage: " << argv[0] << " profile-filename [detail]" << endl;
return -1;
}
Profile pf;
bool result = LoadProfileFromFile(argv[1], &pf);
if (!result) {
cerr << "Failed to load profile " << argv[1] << endl;
return -1;
}
ostream& output = cout;
output << "number of symbols: " << pf.num_symbols << endl;
output << "number of samples: " << pf.num_samples << endl;
TreeNode root("");
int frame = BuildTreeFromProfile(pf, &root);
TreeVisitor* visitor = NULL;
if (argc == 3) {
visitor = new DetailTreeVisitor(output);
} else {
visitor = new TreeVisitor(output);
}
// Indices -2 and -1 are special rows used to signal visitor to output other
// information such as column headers.
for (int i = -2; i < frame; i++) {
visitor->set_row(i);
root.Accept(0, visitor);
output << endl;
}
delete visitor;
return 0;
}