00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include <swri_profiler_tools/profile.h>
00031 #include <swri_profiler_tools/util.h>
00032 #include <algorithm>
00033 #include <set>
00034 #include <QStringList>
00035 #include <QDebug>
00036
00037 namespace swri_profiler_tools
00038 {
00039
00040 static const ProfileNode invalid_node_;
00041
00042 Profile::Profile()
00043 :
00044 profile_key_(-1),
00045 min_time_s_(0),
00046 max_time_s_(0)
00047 {
00048
00049 node_key_from_path_[""] = 0;
00050 ProfileNode &root_node = nodes_[0];
00051 root_node.node_key_ = 0;
00052 root_node.name_ = "";
00053 root_node.path_ = "";
00054 root_node.measured_ = false;
00055 root_node.depth_ = 0;
00056 root_node.parent_ = -1;
00057 }
00058
00059 Profile::~Profile()
00060 {
00061 }
00062
00063 void Profile::initialize(int profile_key, const QString &name)
00064 {
00065 if (isValid()) {
00066 qWarning("Re-initializing a valid profile (%d,%s) with (%d,%s). "
00067 "Something is probably horribly wrong.",
00068 profile_key_, qPrintable(name_),
00069 profile_key, qPrintable(name));
00070 }
00071 profile_key_ = profile_key;
00072 name_ = name;
00073 }
00074
00075 void Profile::addData(const NewProfileDataVector &data)
00076 {
00077 if (profile_key_ < 0) {
00078 qWarning("Attempt to add %zu elements to an invalid profile.", data.size());
00079 return;
00080 }
00081
00082 if (data.size() == 0) {
00083 return;
00084 }
00085
00086 std::set<uint64_t> modified_times;
00087
00088 bool nodes_added = false;
00089 for (auto const &item : data) {
00090 QString path = normalizeNodePath(item.label);
00091
00092
00093
00094 modified_times.insert(item.wall_stamp_sec);
00095
00096
00097
00098 expandTimeline(item.wall_stamp_sec);
00099
00100
00101
00102 nodes_added |= touchNode(path);
00103
00104 if (node_key_from_path_.count(path) == 0) {
00105 qWarning("Failed to touch node for %s. Data will be dropped. This should never happend.",
00106 qPrintable(path));
00107 continue;
00108 }
00109 int node_key = node_key_from_path_.at(path);
00110
00111
00112
00113
00114 storeItemData(modified_times, node_key, item);
00115 }
00116
00117
00118 if (nodes_added) {
00119 rebuildIndices();
00120 Q_EMIT nodesAdded(profile_key_);
00121 }
00122
00123
00124
00125 for (auto const &t : modified_times) {
00126 updateDerivedData(indexFromSec(t));
00127 }
00128
00129
00130 Q_EMIT dataAdded(profile_key_);
00131 }
00132
00133 void Profile::expandTimeline(const uint64_t sec)
00134 {
00135 if (sec >= min_time_s_ && sec < max_time_s_) {
00136
00137 } else if (min_time_s_ == max_time_s_) {
00138
00139 min_time_s_ = sec;
00140 max_time_s_ = sec+1;
00141 addDataToAllNodes(true, 1);
00142 } else if (sec >= max_time_s_) {
00143
00144 size_t new_elements = sec - max_time_s_ + 1;
00145 max_time_s_ = sec+1;
00146 addDataToAllNodes(true, new_elements);
00147 } else {
00148
00149
00150 size_t new_elements = min_time_s_ - sec;
00151 min_time_s_ = sec;
00152 addDataToAllNodes(false, new_elements);
00153 }
00154 }
00155
00156 void Profile::addDataToAllNodes(const bool back, const size_t count)
00157 {
00158 if (back) {
00159 for (auto &it : nodes_) {
00160 std::deque<ProfileEntry> &data = it.second.data_;
00161 ProfileEntry initial_value;
00162 if (!data.empty()) {
00163 initial_value = data.back();
00164 }
00165 initial_value.projected = true;
00166 data.insert(data.end(), count, initial_value);
00167 }
00168 } else {
00169 ProfileEntry initial_value;
00170 initial_value.projected = true;
00171 for (auto &it : nodes_) {
00172 std::deque<ProfileEntry> &data = it.second.data_;
00173 data.insert(data.begin(), count, initial_value);
00174 }
00175 }
00176 }
00177
00178 bool Profile::touchNode(const QString &path)
00179 {
00180
00181
00182
00183 if (node_key_from_path_.count(path)) {
00184 return false;
00185 }
00186
00187 QStringList all_parts = path.split('/');
00188 if (all_parts.isEmpty()) {
00189 qWarning("Node path (%s) does not have a root component. This should never happen.",
00190 qPrintable(path));
00191
00192
00193 return false;
00194 }
00195
00196 if (all_parts.front() != "") {
00197 qWarning("Split of node path '%s' did not yield root as first element. This should never happend.",
00198 qPrintable(path));
00199 } else {
00200 all_parts.removeFirst();
00201 }
00202
00203 QString this_path = "";
00204 int this_depth = 0;
00205 int parent_key = 0;
00206
00207 while (!all_parts.isEmpty()) {
00208 QString this_name = all_parts.takeFirst();
00209 this_path = this_path + "/" + this_name;
00210 this_depth += 1;
00211
00212 if (node_key_from_path_.count(this_path)) {
00213 parent_key = node_key_from_path_.at(this_path);
00214 continue;
00215 }
00216
00217
00218 int this_key = nodes_.size();
00219 while (nodes_.count(this_key)) { this_key++; }
00220
00221 node_key_from_path_[this_path] = this_key;
00222 ProfileNode &this_node = nodes_[this_key];
00223
00224 this_node.node_key_ = this_key;
00225 this_node.name_ = this_name;
00226 this_node.path_ = this_path;
00227 this_node.measured_ = false;
00228
00229 ProfileEntry initial_value;
00230 initial_value.projected = true;
00231 this_node.data_.resize(max_time_s_ - min_time_s_, initial_value);
00232
00233 this_node.depth_ = this_depth;
00234 this_node.parent_ = parent_key;
00235 parent_key = this_key;
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245 }
00246
00247 return true;
00248 }
00249
00250 void Profile::storeItemData(std::set<uint64_t> &modified_times,
00251 const int node_key,
00252 const NewProfileData &item)
00253 {
00254 size_t index = indexFromSec(item.wall_stamp_sec);
00255 ProfileNode &node = nodes_.at(node_key);
00256
00257 node.measured_ = true;
00258 node.data_[index].projected = false;
00259 node.data_[index].cumulative_call_count = item.cumulative_call_count;
00260 node.data_[index].cumulative_inclusive_duration_ns = item.cumulative_inclusive_duration_ns;
00261 node.data_[index].incremental_inclusive_duration_ns = item.incremental_inclusive_duration_ns;
00262 node.data_[index].incremental_max_duration_ns = item.incremental_max_duration_ns;
00263
00264
00265
00266
00267 for (size_t i = index+1; i < node.data_.size(); i++) {
00268 if (node.data_[i].projected == false) {
00269 break;
00270 }
00271 node.data_[i] = node.data_[index];
00272 node.data_[i].projected = true;
00273 modified_times.insert(secFromIndex(i));
00274 }
00275 }
00276
00277 void Profile::rebuildIndices()
00278 {
00279 rebuildFlatIndex();
00280 rebuildTreeIndex();
00281 }
00282
00283 void Profile::rebuildFlatIndex()
00284 {
00285 QStringList paths;
00286 for (auto const &it : nodes_) {
00287 paths.append(it.second.path());
00288 }
00289 paths.sort();
00290
00291 flat_index_.clear();
00292 flat_index_.reserve(paths.size());
00293 for (int i = 0; i < paths.size(); i++) {
00294 if (node_key_from_path_.count(paths[i]) == 0) {
00295 qWarning("Path is missing from node_key_from_path_. This should never happen.");
00296 continue;
00297 }
00298 flat_index_.push_back(node_key_from_path_.at(paths[i]));
00299 }
00300 }
00301
00302
00303 static bool compareInitialStringList(
00304 const QStringList &list1,
00305 const QStringList &list2)
00306 {
00307 int size = std::min(list1.size(), list2.size());
00308
00309 if (size == 0) {
00310 return true;
00311 }
00312
00313
00314
00315 for (int i = size; i > 0; i--) {
00316 if (list1[i-1] != list2[i-1]) {
00317 return false;
00318 }
00319 }
00320 return true;
00321 }
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338 void Profile::rebuildTreeIndex()
00339 {
00340
00341 for (auto &it : nodes_) {
00342 it.second.children_.clear();
00343 }
00344
00345 for (int key : flat_index_) {
00346 if (nodes_.count(key) == 0) {
00347 qWarning("Key (%d) in flat index was not found in nodes_ map. This should never happen.", key);
00348 continue;
00349 }
00350
00351 const ProfileNode &node = nodes_.at(key);
00352 if (node.parent_ < 0) {
00353 if (key != 0) {
00354 qWarning("Profile node %d does not have a valid parent. This should never happen.", key);
00355 }
00356 continue;
00357 }
00358
00359 if (nodes_.count(node.parentKey()) == 0) {
00360 qWarning("Profile node %d's parent (%d) was not found in nodes_. This should never happen.",
00361 key, node.parentKey());
00362 continue;
00363 }
00364
00365 ProfileNode &parent = nodes_.at(node.parentKey());
00366 parent.children_.push_back(key);
00367 }
00368 }
00369
00370 void Profile::updateDerivedData(size_t index)
00371 {
00372 if (nodes_.count(0) == 0) {
00373 qWarning("Profile is missing root node.");
00374 return;
00375 }
00376
00377 updateDerivedDataInternal(nodes_.at(0), index);
00378 }
00379
00380 void Profile::updateDerivedDataInternal(ProfileNode &node, size_t index)
00381 {
00382 uint64_t children_cum_call_count = 0;
00383 uint64_t children_cum_incl_duration = 0;
00384 uint64_t children_inc_incl_duration = 0;
00385 uint64_t children_inc_max_duration = 0;
00386
00387 for (auto &child_key : node.childKeys()) {
00388 if (nodes_.count(child_key) == 0) {
00389 qWarning("Invalid child key in updateDerivedDataInternal");
00390 continue;
00391 }
00392 ProfileNode &child = nodes_.at(child_key);
00393
00394 updateDerivedDataInternal(child, index);
00395 const ProfileEntry &data = child.data_[index];
00396 children_cum_call_count += data.cumulative_call_count;
00397 children_cum_incl_duration += data.cumulative_inclusive_duration_ns;
00398 children_inc_incl_duration += data.incremental_inclusive_duration_ns;
00399 children_inc_max_duration = std::max(children_inc_max_duration, data.incremental_max_duration_ns);
00400 }
00401
00402 ProfileEntry &data = node.data_[index];
00403 if (!node.measured_) {
00404 data.cumulative_call_count = children_cum_call_count;
00405 data.cumulative_inclusive_duration_ns = children_cum_incl_duration;
00406 data.incremental_inclusive_duration_ns = children_inc_incl_duration;
00407 data.incremental_max_duration_ns = children_inc_max_duration;
00408 }
00409
00410 if (children_cum_incl_duration > data.cumulative_inclusive_duration_ns) {
00411
00412 qWarning("Node's (%s) cumulative inclusive duration is less than it's combined"
00413 " children (%zu < %zu). I have not seen this before, so it may or may"
00414 " not be a big issue.",
00415 qPrintable(node.name()),
00416 data.cumulative_inclusive_duration_ns,
00417 children_cum_incl_duration);
00418 data.cumulative_inclusive_duration_ns = children_cum_incl_duration;
00419 data.cumulative_exclusive_duration_ns = 0;
00420 } else {
00421 data.cumulative_exclusive_duration_ns = data.cumulative_inclusive_duration_ns - children_cum_incl_duration;
00422 }
00423
00424 if (children_inc_incl_duration > data.incremental_inclusive_duration_ns) {
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436 if (children_inc_incl_duration - data.incremental_inclusive_duration_ns > 100000) {
00437 qWarning("Node's (%s) incremental inclusive timing is less than it's combined "
00438 "children (%zu < %zu). If this happens frequently, something is wrong.",
00439 qPrintable(node.name()),
00440 data.incremental_inclusive_duration_ns,
00441 children_inc_incl_duration);
00442 }
00443
00444 data.incremental_inclusive_duration_ns = children_inc_incl_duration;
00445 data.incremental_exclusive_duration_ns = 0;
00446 } else {
00447 data.incremental_exclusive_duration_ns = data.incremental_inclusive_duration_ns - children_inc_incl_duration;
00448 }
00449 }
00450
00451 void Profile::setName(const QString &name)
00452 {
00453 name_ = name;
00454 Q_EMIT profileModified(profile_key_);
00455 }
00456
00457 const ProfileNode& Profile::node(int node_key) const
00458 {
00459 if (nodes_.count(node_key) == 0) {
00460 qWarning("Someone requested an invalid node (%d) from profile (%d)",
00461 node_key, profile_key_);
00462 return invalid_node_;
00463 }
00464 return nodes_.at(node_key);
00465 }
00466
00467 const ProfileNode& Profile::rootNode() const
00468 {
00469 return node(0);
00470 }
00471
00472 const std::vector<int>& Profile::nodeKeys() const
00473 {
00474 return flat_index_;
00475 }
00476 }