1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "PerfTest.h"
18 #include <fstream>
19 #include <iomanip>
20 #include <iostream>
21 #include <string>
22
23 #ifdef ASSERT
24 #undef ASSERT
25 #endif
26 #define ASSERT(cond) \
27 do { \
28 if (!(cond)) { \
29 cerr << __func__ << ":" << __LINE__ << " condition:" << #cond << " failed\n" << endl; \
30 exit(EXIT_FAILURE); \
31 } \
32 } while (0)
33
34 // the ratio that the service is synced on the same cpu beyond
35 // GOOD_SYNC_MIN is considered as good
36 #define GOOD_SYNC_MIN (0.6)
37
38 // the precision used for cout to dump float
39 #define DUMP_PRICISION 2
40
41 using std::cerr;
42 using std::cout;
43 using std::endl;
44 using std::left;
45 using std::ios;
46 using std::min;
47 using std::max;
48 using std::to_string;
49 using std::setprecision;
50 using std::setw;
51 using std::ofstream;
52 using std::make_tuple;
53
createPipePair()54 tuple<Pipe, Pipe> Pipe::createPipePair() {
55 int a[2];
56 int b[2];
57
58 int error1 = pipe(a);
59 int error2 = pipe(b);
60 ASSERT(error1 >= 0);
61 ASSERT(error2 >= 0);
62
63 return make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1]));
64 }
65
Pipe(Pipe && rval)66 Pipe::Pipe(Pipe&& rval) noexcept {
67 fd_read_ = rval.fd_read_;
68 fd_write_ = rval.fd_write_;
69 rval.fd_read_ = 0;
70 rval.fd_write_ = 0;
71 }
72
~Pipe()73 Pipe::~Pipe() {
74 if (fd_read_) {
75 close(fd_read_);
76 }
77 if (fd_write_) {
78 close(fd_write_);
79 }
80 }
81
combine(const Results & a,const Results & b)82 Results Results::combine(const Results& a, const Results& b) {
83 Results ret;
84 for (uint32_t i = 0; i < kNumBuckets; i++) {
85 ret.buckets_[i] = a.buckets_[i] + b.buckets_[i];
86 }
87 ret.worst_ = max(a.worst_, b.worst_);
88 ret.best_ = min(a.best_, b.best_);
89 ret.transactions_ = a.transactions_ + b.transactions_;
90 ret.miss_ = a.miss_ + b.miss_;
91 ret.total_time_ = a.total_time_ + b.total_time_;
92 return ret;
93 }
94
traceStop()95 static void traceStop() {
96 ofstream file;
97 file.open(TRACE_PATH "/tracing_on", ios::out | ios::trunc);
98 file << '0' << endl;
99 file.close();
100 }
101
addTime(uint64_t nano)102 void Results::addTime(uint64_t nano) {
103 buckets_[min(nano, kMaxTimeBucket - 1) / kTimePerBucket] += 1;
104 best_ = min(nano, best_);
105 worst_ = max(nano, worst_);
106 if (raw_dump_) {
107 raw_data_->push_back(nano);
108 }
109 transactions_ += 1;
110 total_time_ += nano;
111 if (missDeadline(nano)) {
112 miss_++;
113 if (tracing_) {
114 // There might be multiple process pair running the test concurrently
115 // each may execute following statements and only the first one actually
116 // stop the trace and any traceStop() after then has no effect.
117 traceStop();
118 cerr << endl;
119 cerr << "deadline triggered: halt & stop trace" << endl;
120 cerr << "log:" TRACE_PATH "/trace" << endl;
121 cerr << endl;
122 exit(EXIT_FAILURE);
123 }
124 }
125 }
126
setupRawData()127 void Results::setupRawData() {
128 raw_dump_ = true;
129 if (raw_data_ == nullptr) {
130 raw_data_ = new list<uint64_t>;
131 } else {
132 raw_data_->clear();
133 }
134 }
135
flushRawData()136 void Results::flushRawData() {
137 if (raw_dump_) {
138 bool first = true;
139 cout << "[";
140 for (auto nano : *raw_data_) {
141 cout << (first ? "" : ",") << to_string(nano);
142 first = false;
143 }
144 cout << "]," << endl;
145 delete raw_data_;
146 raw_data_ = nullptr;
147 }
148 }
149
dump() const150 void Results::dump() const {
151 double best = (double)best_ / 1.0E6;
152 double worst = (double)worst_ / 1.0E6;
153 double average = (double)total_time_ / transactions_ / 1.0E6;
154 int W = DUMP_PRICISION + 2;
155 cout << std::setprecision(DUMP_PRICISION) << "{ \"avg\":" << setw(W) << left << average
156 << ", \"wst\":" << setw(W) << left << worst << ", \"bst\":" << setw(W) << left << best
157 << ", \"miss\":" << left << miss_ << ", \"meetR\":" << setprecision(DUMP_PRICISION + 3)
158 << left << (1.0 - (double)miss_ / transactions_) << "}";
159 }
160
dumpDistribution() const161 void Results::dumpDistribution() const {
162 uint64_t cur_total = 0;
163 cout << "{ ";
164 cout << std::setprecision(DUMP_PRICISION + 3);
165 for (uint32_t i = 0; i < kNumBuckets; i++) {
166 float cur_time = kTimePerBucketMS * i + 0.5f * kTimePerBucketMS;
167 float accumulation = cur_total + buckets_[i];
168 if ((cur_total < 0.5f * transactions_) && (accumulation >= 0.5f * transactions_)) {
169 cout << "\"p50\":" << cur_time << ", ";
170 }
171 if ((cur_total < 0.9f * transactions_) && (accumulation >= 0.9f * transactions_)) {
172 cout << "\"p90\":" << cur_time << ", ";
173 }
174 if ((cur_total < 0.95f * transactions_) && (accumulation >= 0.95f * transactions_)) {
175 cout << "\"p95\":" << cur_time << ", ";
176 }
177 if ((cur_total < 0.99f * transactions_) && (accumulation >= 0.99f * transactions_)) {
178 cout << "\"p99\": " << cur_time;
179 }
180 cur_total += buckets_[i];
181 }
182 cout << "}";
183 }
184
combine(const PResults & a,const PResults & b)185 PResults PResults::combine(const PResults& a, const PResults& b) {
186 PResults ret;
187 ret.nNotInherent = a.nNotInherent + b.nNotInherent;
188 ret.nNotSync = a.nNotSync + b.nNotSync;
189 ret.other = Results::combine(a.other, b.other);
190 ret.fifo = Results::combine(a.fifo, b.fifo);
191 return ret;
192 }
193
dump() const194 void PResults::dump() const {
195 int no_trans = other.getTransactions() + fifo.getTransactions();
196 double sync_ratio = (1.0 - (double)nNotSync / no_trans);
197 cout << "{\"SYNC\":\"" << ((sync_ratio > GOOD_SYNC_MIN) ? "GOOD" : "POOR") << "\","
198 << "\"S\":" << (no_trans - nNotSync) << ",\"I\":" << no_trans << ","
199 << "\"R\":" << sync_ratio << "," << endl;
200 cout << " \"other_ms\":";
201 other.dump();
202 cout << "," << endl;
203 cout << " \"fifo_ms\": ";
204 fifo.dump();
205 cout << "," << endl;
206 cout << " \"otherdis\":";
207 other.dumpDistribution();
208 cout << "," << endl;
209 cout << " \"fifodis\": ";
210 fifo.dumpDistribution();
211 cout << endl;
212 cout << "}," << endl;
213 }
214