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.
155 lines
5.4 KiB
155 lines
5.4 KiB
/* |
|
* Copyright (C) 2016 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 HWBINDER_PERF_TEST_H |
|
#define HWBINDER_PERF_TEST_H |
|
|
|
#include <unistd.h> |
|
#include <chrono> |
|
#include <list> |
|
#include <tuple> |
|
|
|
#define TRACE_PATH "/sys/kernel/debug/tracing" |
|
|
|
using std::list; |
|
using std::tuple; |
|
|
|
// Pipe is a object used for IPC between parent process and child process. |
|
// This IPC class is widely used in binder/hwbinder tests. |
|
// The common usage is the main process to create the Pipe and forks. |
|
// Both parent and child hold a object. Each recv() on one side requires |
|
// a send() on the other side to unblock. |
|
class Pipe { |
|
public: |
|
static tuple<Pipe, Pipe> createPipePair(); |
|
Pipe(Pipe&& rval); |
|
~Pipe(); |
|
inline void signal() { |
|
bool val = true; |
|
send(val); |
|
} |
|
inline void wait() { |
|
bool val = false; |
|
recv(val); |
|
} |
|
|
|
// write a data struct |
|
template <typename T> |
|
int send(const T& v) { |
|
return write(fd_write_, &v, sizeof(T)); |
|
} |
|
// read a data struct |
|
template <typename T> |
|
int recv(T& v) { |
|
return read(fd_read_, &v, sizeof(T)); |
|
} |
|
|
|
private: |
|
int fd_read_; // file descriptor to read |
|
int fd_write_; // file descriptor to write |
|
Pipe(int read_fd, int write_fd) : fd_read_{read_fd}, fd_write_{write_fd} {} |
|
Pipe(const Pipe&) = delete; |
|
Pipe& operator=(const Pipe&) = delete; |
|
Pipe& operator=(const Pipe&&) = delete; |
|
}; |
|
|
|
// statistics of latency |
|
// common usage: |
|
// |
|
// Results r; |
|
// Tick sta, end; |
|
// TICK_NOW(sta); |
|
// ... do something ... |
|
// TICK_NOW(end); |
|
// r.addTime(tickDiffNS(sta, end)); |
|
// |
|
// r.dump(); |
|
// r.dumpDistribution(); |
|
// |
|
class Results { |
|
public: |
|
// enable the deadline miss detection which stops the trace recording after |
|
// a transaction latency > deadline_us_ is detected. |
|
void setTracingMode(bool tracing, uint64_t deadline_us) { |
|
tracing_ = tracing; |
|
deadline_us_ = deadline_us; |
|
} |
|
inline uint64_t getTransactions() const { return transactions_; } |
|
inline bool missDeadline(uint64_t nano) const { return nano > deadline_us_ * 1000; } |
|
// Combine two sets of latency data points and update the aggregation info. |
|
static Results combine(const Results& a, const Results& b); |
|
// add a new transaction latency record |
|
void addTime(uint64_t nano); |
|
// prepare for raw data recording, it may allocate resources which requires |
|
// a flushRawData() to release |
|
void setupRawData(); |
|
// dump the raw data and release the resource |
|
void flushRawData(); |
|
// dump average, best, worst latency in json |
|
void dump() const; |
|
// dump latency distribution in json |
|
void dumpDistribution() const; |
|
|
|
private: |
|
static const uint32_t kNumBuckets = 128; |
|
static const uint64_t kMaxTimeBucket = 50ull * 1000000; |
|
static const uint64_t kTimePerBucket = kMaxTimeBucket / kNumBuckets; |
|
static constexpr float kTimePerBucketMS = kTimePerBucket / 1.0E6; |
|
uint64_t best_ = 0xffffffffffffffffULL; // best transaction latency in ns. |
|
uint64_t worst_ = 0; // worst transaction latency in ns. |
|
uint64_t transactions_ = 0; // number of transactions |
|
uint64_t total_time_ = 0; // total transaction time |
|
uint64_t miss_ = 0; // number of transactions whose latency > deadline |
|
uint32_t buckets_[kNumBuckets] = {0}; // statistics for the distribution |
|
list<uint64_t>* raw_data_ = nullptr; // list for raw-data |
|
bool tracing_ = false; // halt the trace log on a deadline miss |
|
bool raw_dump_ = false; // record the raw data for the dump after |
|
uint64_t deadline_us_ = 2500; // latency deadline in us. |
|
}; |
|
|
|
// statistics of a process pair |
|
class PResults { |
|
public: |
|
static PResults combine(const PResults& a, const PResults& b); |
|
int nNotInherent = 0; ///< #transactions that does not inherit priority |
|
int nNotSync = 0; ///< #transactions that are not synced |
|
Results other; ///< statistics of CFS-other transactions |
|
Results fifo; ///< statistics of RT-fifo transactions |
|
// dump and flush the raw data |
|
inline void flushRawData() { fifo.flushRawData(); } |
|
// dump in json |
|
void dump() const; |
|
}; |
|
|
|
// Tick keeps timestamp |
|
typedef std::chrono::time_point<std::chrono::high_resolution_clock> Tick; |
|
|
|
// get current timestamp as a Tick |
|
static inline Tick tickNow() { |
|
return std::chrono::high_resolution_clock::now(); |
|
} |
|
|
|
#define TICK_NOW(_tick) \ |
|
do { \ |
|
asm volatile("" ::: "memory"); \ |
|
_tick = tickNow(); \ |
|
asm volatile("" ::: "memory"); \ |
|
} while (0) |
|
|
|
// get nano seconds between sta & end |
|
static inline uint64_t tickDiffNS(Tick& sta, Tick& end) { |
|
return uint64_t(std::chrono::duration_cast<std::chrono::nanoseconds>(end - sta).count()); |
|
} |
|
#endif
|
|
|