aboutsummaryrefslogtreecommitdiff
path: root/libpp/profile.cpp
blob: f1175085c58e42c65c64a8a37bb42ca44bd5567e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/**
 * @file profile.cpp
 * Encapsulation for samples files over all profile classes
 * belonging to the same binary image
 *
 * @remark Copyright 2002 OProfile authors
 * @remark Read the file COPYING
 *
 * @author Philippe Elie
 * @author John Levon
 */

#include <unistd.h>
#include <cstring>

#include <iostream>
#include <string>
#include <sstream>
#include <cstring>

#include <cerrno>

#include "op_exception.h"
#include "op_header.h"
#include "op_config.h"
#include "op_sample_file.h"
#include "profile.h"
#include "op_bfd.h"
#include "cverb.h"
#include "populate_for_spu.h"

using namespace std;

profile_t::profile_t()
	: start_offset(0)
{
}


// static member
count_type profile_t::sample_count(string const & filename)
{
	odb_t samples_db;

	open_sample_file(filename, samples_db);

	count_type count = 0;

	odb_node_nr_t node_nr, pos;
	odb_node_t * node = odb_get_iterator(&samples_db, &node_nr);
	for (pos = 0; pos < node_nr; ++pos)
		count += node[pos].value;

	odb_close(&samples_db);

	return count;
}

//static member
enum profile_type profile_t::is_spu_sample_file(string const & filename)
{
	profile_type retval;
	odb_t samples_db;
	open_sample_file(filename, samples_db);
	opd_header const & hdr =
		*static_cast<opd_header *>(odb_get_data(&samples_db));
	retval = hdr.spu_profile ? cell_spu_profile: normal_profile;
	odb_close(&samples_db);
	return retval;
}

//static member
void profile_t::open_sample_file(string const & filename, odb_t & db)
{
	// Check first if the sample file version is ok else odb_open() can
	// fail and the error message will be obscure.
	opd_header head = read_header(filename);

	if (head.version != OPD_VERSION) {
		ostringstream os;
		os << "oprofpp: samples files version mismatch, are you "
		   << "running a daemon and post-profile tools with version "
		   <<  "mismatch ?\n";
		throw op_fatal_error(os.str());
	}

	int rc = odb_open(&db, filename.c_str(), ODB_RDONLY,
		sizeof(struct opd_header));

	if (rc)
		throw op_fatal_error(filename + ": " + strerror(rc));
}

void profile_t::add_sample_file(string const & filename)
{
	odb_t samples_db;

	open_sample_file(filename, samples_db);

	opd_header const & head =
		*static_cast<opd_header *>(odb_get_data(&samples_db));

	// if we already read a sample file header pointer is non null
	if (file_header.get())
		op_check_header(head, *file_header, filename);
	else
		file_header.reset(new opd_header(head));

	odb_node_nr_t node_nr, pos;
	odb_node_t * node = odb_get_iterator(&samples_db, &node_nr);

	for (pos = 0; pos < node_nr; ++pos) {
		ordered_samples_t::iterator it = 
		    ordered_samples.find(node[pos].key);
		if (it != ordered_samples.end()) {
			it->second += node[pos].value;
		} else {
			ordered_samples_t::value_type
				val(node[pos].key, node[pos].value);
			ordered_samples.insert(val);
		}
	}

	odb_close(&samples_db);
}


void profile_t::set_offset(op_bfd const & abfd)
{
	// if no bfd file has been located for this samples file, we can't
	// shift sample because abfd.get_symbol_range() return the whole
	// address space and setting a non zero start_offset will overflow
	// in get_symbol_range() caller.
	if (abfd.valid()) {
		opd_header const & header = get_header();
		if (header.anon_start) {
			start_offset = header.anon_start;
		} else if (header.is_kernel) {
			start_offset = abfd.get_start_offset(0);
		}
	}
	cverb << (vdebug) << "start_offset is now " << start_offset << endl;
}


profile_t::iterator_pair
profile_t::samples_range(odb_key_t start, odb_key_t end) const
{
	// Check the start position isn't before start_offset:
	// this avoids wrapping/underflowing start/end.
	// This can happen on e.g. ARM kernels, where .init is
	// mapped before .text - we just have to skip any such
	// .init symbols.
	if (start < start_offset) {
		return make_pair(const_iterator(ordered_samples.end(), 0), 
			const_iterator(ordered_samples.end(), 0));
	}
	
	start -= start_offset;
	end -= start_offset;

	// sanity check if start > end caller will enter into an infinite loop
	if (start > end) {
		throw op_fatal_error("profile_t::samples_range(): start > end"
			" something wrong with kernel or module layout ?\n"
			"please report problem to "
			"oprofile-list@lists.sourceforge.net");
	}

	ordered_samples_t::const_iterator first = 
		ordered_samples.lower_bound(start);
	ordered_samples_t::const_iterator last =
		ordered_samples.lower_bound(end);

	return make_pair(const_iterator(first, start_offset),
		const_iterator(last, start_offset));
}


profile_t::iterator_pair profile_t::samples_range() const
{
	ordered_samples_t::const_iterator first = ordered_samples.begin();
	ordered_samples_t::const_iterator last = ordered_samples.end();

	return make_pair(const_iterator(first, start_offset),
		const_iterator(last, start_offset));
}