1 : // Copyright 2013 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 : //
15 : // Declares the sample grinder, which processes trace files containing
16 : // TraceSampleData records. It can aggregate to a variety of targets
17 : // (basic blocks, functions, compilands, lines), and output to a variety of
18 : // formats (CSV, KCacheGrind).
19 :
20 : #ifndef SYZYGY_GRINDER_GRINDERS_SAMPLE_GRINDER_H_
21 : #define SYZYGY_GRINDER_GRINDERS_SAMPLE_GRINDER_H_
22 :
23 : #include "syzygy/core/address_space.h"
24 : #include "syzygy/core/string_table.h"
25 : #include "syzygy/grinder/basic_block_util.h"
26 : #include "syzygy/grinder/grinder.h"
27 : #include "syzygy/pe/pe_file.h"
28 :
29 : namespace grinder {
30 : namespace grinders {
31 :
32 : // This class processes trace files containing TraceSampleData records and
33 : // produces estimates of block/function/compiland heat.
34 : class SampleGrinder : public GrinderInterface {
35 : public:
36 : // The level of aggregation to be used in processing samples.
37 : enum AggregationLevel {
38 : kBasicBlock,
39 : kFunction,
40 : kCompiland,
41 : kLine,
42 : kAggregationLevelMax, // Must be last.
43 : };
44 :
45 : // The names of the aggregation levels. These must be kept in sync with
46 : // AggregationLevel.
47 : static const char* kAggregationLevelNames[];
48 :
49 : SampleGrinder();
50 : ~SampleGrinder();
51 :
52 : // @name SampleInterface implementation.
53 : // @{
54 : virtual bool ParseCommandLine(const base::CommandLine* command_line) override;
55 : virtual void SetParser(Parser* parser) override;
56 : virtual bool Grind() override;
57 : virtual bool OutputData(FILE* file) override;
58 : // @}
59 :
60 : // @name ParseEventHandler implementation.
61 : // @{
62 : // Override of the OnProcessStarted callback. This is required to get
63 : // the system clock rate for scaling frequency data.
64 : virtual void OnProcessStarted(base::Time time,
65 : DWORD process_id,
66 : const TraceSystemInfo* data) override;
67 : // Override of the OnSampleData callback.
68 : virtual void OnSampleData(base::Time Time,
69 : DWORD process_id,
70 : const TraceSampleData* data) override;
71 : // @}
72 :
73 : // @name Parameter names.
74 : // @{
75 : static const char kAggregationLevel[];
76 : static const char kImage[];
77 : // @}
78 :
79 : // Forward declarations. These are public so that they are accessible by
80 : // anonymous helper functions.
81 : struct ModuleKey;
82 : struct ModuleData;
83 :
84 : // Some type definitions. There are public so that they are accessible by
85 : // anonymous helper functions.
86 :
87 : // We store some metadata for each basic-block in an image, allowing us to
88 : // roll up the heat based on different categories.
89 : struct BasicBlockData {
90 : const std::string* compiland;
91 : const std::string* function;
92 : double heat;
93 : };
94 : // This is the type of address-space that is used for representing estimates
95 : // of heat calculated from aggregate sample data.
96 : typedef core::AddressSpace<core::RelativeAddress, size_t, BasicBlockData>
97 : HeatMap;
98 :
99 : // This is the final aggregate type used to represent heat as rolled up to
100 : // named objects (compilands or functions).
101 : typedef std::map<const std::string*, double> NameHeatMap;
102 :
103 : protected:
104 : // Finds or creates the sample data associated with the given module.
105 : ModuleData* GetModuleData(
106 : const base::FilePath& module_path,
107 : const TraceSampleData* sample_data);
108 :
109 : // Upsamples the provided @p module_data so that it has at least as many
110 : // buckets as the @p sample_data. If the resolution is already sufficient
111 : // this does nothing.
112 : // @param sample_data The sample data to be used as a reference.
113 : // @param module_data The module data to be potentially upsampled.
114 : // @note This is exposed for unit testing.
115 : static void UpsampleModuleData(
116 : const TraceSampleData* sample_data,
117 : SampleGrinder::ModuleData* module_data);
118 :
119 : // Updates the @p module_data with the samples from @p sample_data. The
120 : // @p module_data must already be at sufficient resolution to accept the
121 : // data in @p sample_data. This can fail if the @p sample_data and the
122 : // @p module_data do not have consistent metadata.
123 : // @param clock_rate The clock rate to be used in scaling the sample data.
124 : // @param sample_data The sample data to be added.
125 : // @param module_data The module data to be incremented.
126 : // @returns True on success, false otherwise.
127 : static bool IncrementModuleData(
128 : double clock_rate,
129 : const TraceSampleData* sample_data,
130 : SampleGrinder::ModuleData* module_data);
131 :
132 : // Given a populated @p heat_map and aggregate @p module_data, estimates heat
133 : // for each range in the @p heat_map. The values represent an estimate of
134 : // amount of time spent in the range, in seconds.
135 : // @param module_data Aggregate module data.
136 : // @param heat A pre-populated address space representing the basic blocks of
137 : // the module in question.
138 : // @param total_samples The total number of samples processed will be returned
139 : // here. This is optional and may be NULL.
140 : // @returns the total weight of orphaned samples that were unable to be mapped
141 : // to any range in the heat map.
142 : static double IncrementHeatMapFromModuleData(
143 : const SampleGrinder::ModuleData& module_data,
144 : HeatMap* heat_map,
145 : double* total_samples);
146 :
147 : // Given a populated @p heat_map performs an aggregation of the heat based
148 : // on function or compiland names.
149 : // @param aggregation_level The aggregation level. Must be one of
150 : // kFunction or kCompiland.
151 : // @param heat_map The BB heat map to be aggregated.
152 : // @param name_heat_map The named heat map to be populated.
153 : static void RollUpByName(AggregationLevel aggregation_level,
154 : const HeatMap& heat_map,
155 : NameHeatMap* name_heat_map);
156 :
157 : // The aggregation level to be used in processing samples.
158 : AggregationLevel aggregation_level_;
159 :
160 : // If image_path_ is not empty, then this data is used as a filter for
161 : // processing.
162 : base::FilePath image_path_;
163 : pe::PEFile image_;
164 : pe::PEFile::Signature image_signature_;
165 :
166 : // Points to the parser that is feeding us events. Used to get module
167 : // information.
168 : Parser* parser_;
169 :
170 : // Set to true if any call to OnIndexedFrequency fails. Processing will
171 : // continue with a warning that results may be partial.
172 : bool event_handler_errored_;
173 :
174 : // The clock rate that is currently in force. This is used for scaling
175 : // sample values.
176 : double clock_rate_;
177 :
178 : // As we parse sample data we update a running tally at the finest bucket
179 : // size observed in trace files.
180 : typedef std::map<ModuleKey, ModuleData> ModuleDataMap;
181 : ModuleDataMap module_data_;
182 :
183 : // These are used for holding final results. They are populated by Grind()
184 : // when the aggregation mode is anything except 'line'.
185 : core::StringTable string_table_;
186 : HeatMap heat_map_;
187 : NameHeatMap name_heat_map_;
188 :
189 : // Used only in 'line' aggregation mode. Populated by Grind().
190 : LineInfo line_info_;
191 :
192 : private:
193 : DISALLOW_COPY_AND_ASSIGN(SampleGrinder);
194 : };
195 :
196 : struct SampleGrinder::ModuleKey {
197 : size_t module_size;
198 : uint32 module_checksum;
199 : uint32 module_time_date_stamp;
200 :
201 : bool operator<(const ModuleKey& rhs) const;
202 : };
203 :
204 : struct SampleGrinder::ModuleData {
205 E : ModuleData::ModuleData() : bucket_size(0) {}
206 :
207 : base::FilePath module_path;
208 : uint32 bucket_size;
209 : core::RelativeAddress bucket_start;
210 : std::vector<double> buckets;
211 : };
212 :
213 : } // namespace grinders
214 : } // namespace grinder
215 :
216 : #endif // SYZYGY_GRINDER_GRINDERS_SAMPLE_GRINDER_H_
|