aboutsummaryrefslogtreecommitdiff
path: root/libpp/diff_container.cpp
blob: 7762916bbffd80567a729223b4c59e2cbec2cede (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
/**
 * @file diff_container.cpp
 * Container for diffed symbols
 *
 * @remark Copyright 2005 OProfile authors
 * @remark Read the file COPYING
 *
 * @author Philippe Elie
 * @author John Levon
 */

/* older glibc has C99 INFINITY in _GNU_SOURCE */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include "diff_container.h"

#include <cmath>

using namespace std;


namespace {


/// a comparator suitable for diffing symbols
bool rough_less(symbol_entry const & lhs, symbol_entry const & rhs)
{
	if (lhs.image_name != rhs.image_name)
		return lhs.image_name < rhs.image_name;

	if (lhs.app_name != rhs.app_name)
		return lhs.app_name < rhs.app_name;

	if (lhs.name != rhs.name)
		return lhs.name < rhs.name;

	return false;
}


/// possibly add a diff sym
void
add_sym(diff_collection & syms, diff_symbol const & sym,
        profile_container::symbol_choice & choice)
{
	if (choice.match_image
	    && (image_names.name(sym.image_name) != choice.image_name))
		return;

	if (fabs(sym.diffs[0]) < choice.threshold)
		return;

	choice.hints = sym.output_hint(choice.hints);
	syms.push_back(sym);
}


/// add a symbol not present in the new profile
void
symbol_old(diff_collection & syms, symbol_entry const & sym,
           profile_container::symbol_choice & choice)
{
	diff_symbol symbol(sym);
	symbol.diffs.fill(sym.sample.counts.size(), -INFINITY);
	add_sym(syms, symbol, choice);
}


/// add a symbol not present in the old profile
void
symbol_new(diff_collection & syms, symbol_entry const & sym,
           profile_container::symbol_choice & choice)
{
	diff_symbol symbol(sym);
	symbol.diffs.fill(sym.sample.counts.size(), INFINITY);
	add_sym(syms, symbol, choice);
}


/// add a diffed symbol
void symbol_diff(diff_collection & syms,
                 symbol_entry const & sym1, count_array_t const & total1,
                 symbol_entry const & sym2, count_array_t const & total2,
                 profile_container::symbol_choice & choice)
{
	diff_symbol symbol(sym2);

	size_t size = sym2.sample.counts.size();
	for (size_t i = 0; i != size; ++i) {
		double percent1;
		double percent2;
		percent1 = op_ratio(sym1.sample.counts[i], total1[i]);
		percent2 = op_ratio(sym2.sample.counts[i], total2[i]);
		symbol.diffs[i] = op_ratio(percent2 - percent1, percent1);
		symbol.diffs[i] *= 100.0;
	}

	add_sym(syms, symbol, choice);
}


}; // namespace anon


diff_container::diff_container(profile_container const & c1,
                               profile_container const & c2)
	: pc1(c1), pc2(c2),
	  total1(pc1.samples_count()), total2(pc2.samples_count())
{
}


diff_collection const
diff_container::get_symbols(profile_container::symbol_choice & choice) const
{
	diff_collection syms;

	/*
	 * Do a pairwise comparison of the two symbol sets. We're
	 * relying here on the symbol container being sorted such
	 * that rough_less() is suitable for iterating through the
	 * two lists (see less_symbol).
	 */

	symbol_container::symbols_t::iterator it1 = pc1.begin_symbol();
	symbol_container::symbols_t::iterator end1 = pc1.end_symbol();
	symbol_container::symbols_t::iterator it2 = pc2.begin_symbol();
	symbol_container::symbols_t::iterator end2 = pc2.end_symbol();

	while (it1 != end1 && it2 != end2) {
		if (rough_less(*it1, *it2)) {
			symbol_old(syms, *it1, choice);
			++it1;
		} else if (rough_less(*it2, *it1)) {
			symbol_new(syms, *it2, choice);
			++it2;
		} else {
			symbol_diff(syms, *it1, total1, *it2, total2, choice);
			++it1;
			++it2;
		}
	}

	for (; it1 != end1; ++it1)
		symbol_old(syms, *it1, choice);

	for (; it2 != end2; ++it2)
		symbol_new(syms, *it2, choice);

	return syms;
}


count_array_t const diff_container::samples_count() const
{
	return total2;
}