You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
233 lines
6.9 KiB
233 lines
6.9 KiB
/*
|
|
* Copyright (C) 2011 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef ART_RUNTIME_BASE_TIMING_LOGGER_H_
|
|
#define ART_RUNTIME_BASE_TIMING_LOGGER_H_
|
|
|
|
#include "base/locks.h"
|
|
#include "base/macros.h"
|
|
#include "base/time_utils.h"
|
|
|
|
#include <memory>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace art {
|
|
class TimingLogger;
|
|
|
|
class CumulativeLogger {
|
|
public:
|
|
explicit CumulativeLogger(const std::string& name);
|
|
~CumulativeLogger();
|
|
void Start();
|
|
void End() REQUIRES(!GetLock());
|
|
void Reset() REQUIRES(!GetLock());
|
|
void Dump(std::ostream& os) const REQUIRES(!GetLock());
|
|
uint64_t GetTotalNs() const {
|
|
return GetTotalTime() * kAdjust;
|
|
}
|
|
// Allow the name to be modified, particularly when the cumulative logger is a field within a
|
|
// parent class that is unable to determine the "name" of a sub-class.
|
|
void SetName(const std::string& name) REQUIRES(!GetLock());
|
|
void AddLogger(const TimingLogger& logger) REQUIRES(!GetLock());
|
|
size_t GetIterations() const REQUIRES(!GetLock());
|
|
|
|
private:
|
|
class CumulativeTime {
|
|
public:
|
|
CumulativeTime(const char* name, uint64_t time) : name_(name), time_(time) {}
|
|
void Add(uint64_t time) { time_ += time; }
|
|
const char* Name() const { return name_; }
|
|
uint64_t Sum() const { return time_; }
|
|
// Compare addresses of names for sorting.
|
|
bool operator< (const CumulativeTime& ct) const {
|
|
return std::less<const char*>()(name_, ct.name_);
|
|
}
|
|
|
|
private:
|
|
const char* name_;
|
|
uint64_t time_;
|
|
};
|
|
|
|
void DumpAverages(std::ostream &os) const REQUIRES(GetLock());
|
|
void AddPair(const char* label, uint64_t delta_time) REQUIRES(GetLock());
|
|
uint64_t GetTotalTime() const {
|
|
return total_time_;
|
|
}
|
|
|
|
Mutex* GetLock() const {
|
|
return lock_.get();
|
|
}
|
|
|
|
static constexpr uint64_t kAdjust = 1000;
|
|
// Use a vector to keep dirty memory to minimal number of pages. Using a
|
|
// hashtable would be performant, but could lead to more dirty pages. Also, we
|
|
// don't expect this vector to be too big.
|
|
std::vector<CumulativeTime> cumulative_timers_ GUARDED_BY(GetLock());
|
|
std::string name_;
|
|
const std::string lock_name_;
|
|
mutable std::unique_ptr<Mutex> lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
|
|
size_t iterations_ GUARDED_BY(GetLock());
|
|
uint64_t total_time_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(CumulativeLogger);
|
|
};
|
|
|
|
// A timing logger that knows when a split starts for the purposes of logging tools, like systrace.
|
|
class TimingLogger {
|
|
public:
|
|
static constexpr size_t kIndexNotFound = static_cast<size_t>(-1);
|
|
|
|
// Kind of timing we are going to do. We collect time at the nano second.
|
|
enum class TimingKind {
|
|
kMonotonic,
|
|
kThreadCpu,
|
|
};
|
|
|
|
class Timing {
|
|
public:
|
|
Timing(TimingKind kind, const char* name) : name_(name) {
|
|
switch (kind) {
|
|
case TimingKind::kMonotonic:
|
|
time_ = NanoTime();
|
|
break;
|
|
case TimingKind::kThreadCpu:
|
|
time_ = ThreadCpuNanoTime();
|
|
break;
|
|
}
|
|
}
|
|
bool IsStartTiming() const {
|
|
return !IsEndTiming();
|
|
}
|
|
bool IsEndTiming() const {
|
|
return name_ == nullptr;
|
|
}
|
|
uint64_t GetTime() const {
|
|
return time_;
|
|
}
|
|
const char* GetName() const {
|
|
return name_;
|
|
}
|
|
|
|
private:
|
|
uint64_t time_;
|
|
const char* name_;
|
|
};
|
|
|
|
// Extra data that is only calculated when you call dump to prevent excess allocation.
|
|
class TimingData {
|
|
public:
|
|
TimingData() = default;
|
|
TimingData(TimingData&& other) {
|
|
std::swap(data_, other.data_);
|
|
}
|
|
TimingData& operator=(TimingData&& other) {
|
|
std::swap(data_, other.data_);
|
|
return *this;
|
|
}
|
|
uint64_t GetTotalTime(size_t idx) {
|
|
return data_[idx].total_time;
|
|
}
|
|
uint64_t GetExclusiveTime(size_t idx) {
|
|
return data_[idx].exclusive_time;
|
|
}
|
|
|
|
private:
|
|
// Each begin split has a total time and exclusive time. Exclusive time is total time - total
|
|
// time of children nodes.
|
|
struct CalculatedDataPoint {
|
|
CalculatedDataPoint() : total_time(0), exclusive_time(0) {}
|
|
uint64_t total_time;
|
|
uint64_t exclusive_time;
|
|
};
|
|
std::vector<CalculatedDataPoint> data_;
|
|
friend class TimingLogger;
|
|
};
|
|
|
|
TimingLogger(const char* name,
|
|
bool precise,
|
|
bool verbose,
|
|
TimingKind kind = TimingKind::kMonotonic);
|
|
~TimingLogger();
|
|
// Verify that all open timings have related closed timings.
|
|
void Verify();
|
|
// Clears current timings and labels.
|
|
void Reset();
|
|
// Starts a timing.
|
|
void StartTiming(const char* new_split_label);
|
|
// Ends the current timing.
|
|
void EndTiming();
|
|
// End the current timing and start a new timing. Usage not recommended.
|
|
void NewTiming(const char* new_split_label) {
|
|
EndTiming();
|
|
StartTiming(new_split_label);
|
|
}
|
|
// Returns the total duration of the timings (sum of total times).
|
|
uint64_t GetTotalNs() const;
|
|
// Find the index of a timing by name.
|
|
size_t FindTimingIndex(const char* name, size_t start_idx) const;
|
|
void Dump(std::ostream& os, const char* indent_string = " ") const;
|
|
|
|
// Scoped timing splits that can be nested and composed with the explicit split
|
|
// starts and ends.
|
|
class ScopedTiming {
|
|
public:
|
|
ScopedTiming(const char* label, TimingLogger* logger) : logger_(logger) {
|
|
logger_->StartTiming(label);
|
|
}
|
|
~ScopedTiming() {
|
|
logger_->EndTiming();
|
|
}
|
|
// Closes the current timing and opens a new timing.
|
|
void NewTiming(const char* label) {
|
|
logger_->NewTiming(label);
|
|
}
|
|
|
|
private:
|
|
TimingLogger* const logger_; // The timing logger which the scoped timing is associated with.
|
|
DISALLOW_COPY_AND_ASSIGN(ScopedTiming);
|
|
};
|
|
|
|
// Return the time points of when each start / end timings start and finish.
|
|
const std::vector<Timing>& GetTimings() const {
|
|
return timings_;
|
|
}
|
|
|
|
TimingData CalculateTimingData() const;
|
|
|
|
protected:
|
|
// The name of the timing logger.
|
|
const char* const name_;
|
|
// Do we want to print the exactly recorded split (true) or round down to the time unit being
|
|
// used (false).
|
|
const bool precise_;
|
|
// Verbose logging.
|
|
const bool verbose_;
|
|
// The kind of timing we want.
|
|
const TimingKind kind_;
|
|
// Timing points that are either start or end points. For each starting point ret[i] = location
|
|
// of end split associated with i. If it is and end split ret[i] = i.
|
|
std::vector<Timing> timings_;
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(TimingLogger);
|
|
};
|
|
|
|
} // namespace art
|
|
|
|
#endif // ART_RUNTIME_BASE_TIMING_LOGGER_H_
|