blob: edda9bede0451374b006dba99943b06a4f61ddee [file] [log] [blame]
Mike Klein06432b22017-03-21 13:14:33 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8// ok is an experimental test harness, maybe to replace DM. Key features:
9// * work is balanced across separate processes for stability and isolation;
10// * ok is entirely opt-in. No more maintaining huge --blacklists.
11
12#include "SkGraphics.h"
Florin Malitaab244f02017-05-03 19:16:58 +000013#include "SkImage.h"
Mike Klein7ac04832017-03-25 11:29:41 -040014#include "ok.h"
Mike Klein9827256c2017-03-23 15:29:26 -040015#include <chrono>
Mike Klein06432b22017-03-21 13:14:33 -040016#include <future>
Mike Klein9827256c2017-03-23 15:29:26 -040017#include <list>
Mike Klein06432b22017-03-21 13:14:33 -040018#include <stdio.h>
19#include <stdlib.h>
20#include <thread>
Mike Kleine15a7b52017-03-29 12:41:13 -040021#include <vector>
Mike Klein06432b22017-03-21 13:14:33 -040022
Mike Klein61e99022017-03-23 18:36:39 -040023#if !defined(__has_include)
24 #define __has_include(x) 0
25#endif
26
Mike Kleine15a7b52017-03-29 12:41:13 -040027static thread_local const char* tls_currently_running = "";
Mike Klein61e99022017-03-23 18:36:39 -040028
29#if __has_include(<execinfo.h>) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
30 #include <execinfo.h>
31 #include <fcntl.h>
32 #include <signal.h>
33
Mike Klein200f6da2017-03-28 09:30:11 -040034 static int log_fd = 2/*stderr*/;
35
36 static void log(const char* msg) {
37 write(log_fd, msg, strlen(msg));
38 }
Mike Klein61e99022017-03-23 18:36:39 -040039
40 static void setup_crash_handler() {
41 static void (*original_handlers[32])(int);
Mike Klein61e99022017-03-23 18:36:39 -040042 for (int sig : std::vector<int>{ SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }) {
43 original_handlers[sig] = signal(sig, [](int sig) {
Mike Klein200f6da2017-03-28 09:30:11 -040044 lockf(log_fd, F_LOCK, 0);
45 log("\ncaught signal ");
46 switch (sig) {
47 #define CASE(s) case s: log(#s); break
48 CASE(SIGABRT);
49 CASE(SIGBUS);
50 CASE(SIGFPE);
51 CASE(SIGILL);
52 CASE(SIGSEGV);
53 #undef CASE
54 }
55 log(" while running '");
Mike Kleine15a7b52017-03-29 12:41:13 -040056 log(tls_currently_running);
Mike Klein200f6da2017-03-28 09:30:11 -040057 log("'\n");
Mike Klein59eed0d2017-03-26 23:22:44 -040058
Mike Klein200f6da2017-03-28 09:30:11 -040059 void* stack[128];
60 int frames = backtrace(stack, sizeof(stack)/sizeof(*stack));
61 backtrace_symbols_fd(stack, frames, log_fd);
62 lockf(log_fd, F_ULOCK, 0);
Mike Klein61e99022017-03-23 18:36:39 -040063
Mike Klein61e99022017-03-23 18:36:39 -040064 signal(sig, original_handlers[sig]);
65 raise(sig);
66 });
67 }
68 }
69
Mike Klein200f6da2017-03-28 09:30:11 -040070 static void defer_logging() {
71 log_fd = fileno(tmpfile());
Mike Klein61e99022017-03-23 18:36:39 -040072 atexit([] {
Mike Klein200f6da2017-03-28 09:30:11 -040073 lseek(log_fd, 0, SEEK_SET);
Mike Klein61e99022017-03-23 18:36:39 -040074 char buf[1024];
Mike Klein200f6da2017-03-28 09:30:11 -040075 while (size_t bytes = read(log_fd, buf, sizeof(buf))) {
Mike Klein61e99022017-03-23 18:36:39 -040076 write(2, buf, bytes);
77 }
78 });
79 }
Mike Klein200f6da2017-03-28 09:30:11 -040080
81 void ok_log(const char* msg) {
82 lockf(log_fd, F_LOCK, 0);
83 log("[");
Mike Kleine15a7b52017-03-29 12:41:13 -040084 log(tls_currently_running);
Mike Klein200f6da2017-03-28 09:30:11 -040085 log("]\t");
86 log(msg);
87 log("\n");
88 lockf(log_fd, F_ULOCK, 0);
89 }
90
Mike Klein61e99022017-03-23 18:36:39 -040091#else
92 static void setup_crash_handler() {}
Mike Klein200f6da2017-03-28 09:30:11 -040093 static void defer_logging() {}
94
95 void ok_log(const char* msg) {
96 fprintf(stderr, "%s\n", msg);
97 }
Mike Klein61e99022017-03-23 18:36:39 -040098#endif
99
Mike Klein06432b22017-03-21 13:14:33 -0400100struct Engine {
101 virtual ~Engine() {}
102 virtual bool spawn(std::function<Status(void)>) = 0;
103 virtual Status wait_one() = 0;
104};
105
106struct SerialEngine : Engine {
107 Status last = Status::None;
108
109 bool spawn(std::function<Status(void)> fn) override {
110 last = fn();
111 return true;
112 }
113
114 Status wait_one() override {
115 Status s = last;
116 last = Status::None;
117 return s;
118 }
119};
120
121struct ThreadEngine : Engine {
Mike Klein9827256c2017-03-23 15:29:26 -0400122 std::list<std::future<Status>> live;
Mike Klein9fa99612017-04-27 09:50:34 -0400123 const std::chrono::steady_clock::time_point the_past = std::chrono::steady_clock::now();
Mike Klein06432b22017-03-21 13:14:33 -0400124
125 bool spawn(std::function<Status(void)> fn) override {
126 live.push_back(std::async(std::launch::async, fn));
127 return true;
128 }
129
130 Status wait_one() override {
131 if (live.empty()) {
132 return Status::None;
133 }
Mike Klein9827256c2017-03-23 15:29:26 -0400134
135 for (;;) {
136 for (auto it = live.begin(); it != live.end(); it++) {
Mike Klein9fa99612017-04-27 09:50:34 -0400137 if (it->wait_until(the_past) == std::future_status::ready) {
Mike Klein9827256c2017-03-23 15:29:26 -0400138 Status s = it->get();
139 live.erase(it);
140 return s;
141 }
142 }
143 }
Mike Klein06432b22017-03-21 13:14:33 -0400144 }
145};
146
147#if defined(_MSC_VER)
148 using ForkEngine = ThreadEngine;
149#else
150 #include <sys/wait.h>
151 #include <unistd.h>
152
153 struct ForkEngine : Engine {
154 bool spawn(std::function<Status(void)> fn) override {
155 switch (fork()) {
Mike Klein61e99022017-03-23 18:36:39 -0400156 case 0: _exit((int)fn());
Mike Klein06432b22017-03-21 13:14:33 -0400157 case -1: return false;
158 default: return true;
159 }
160 }
161
162 Status wait_one() override {
163 do {
164 int status;
165 if (wait(&status) > 0) {
166 return WIFEXITED(status) ? (Status)WEXITSTATUS(status)
167 : Status::Crashed;
168 }
169 } while (errno == EINTR);
170 return Status::None;
171 }
172 };
173#endif
174
Mike Klein7ac04832017-03-25 11:29:41 -0400175struct StreamType {
Mike Kleine15a7b52017-03-29 12:41:13 -0400176 const char *name, *help;
Mike Klein06432b22017-03-21 13:14:33 -0400177 std::unique_ptr<Stream> (*factory)(Options);
Mike Klein06432b22017-03-21 13:14:33 -0400178};
Mike Klein7ac04832017-03-25 11:29:41 -0400179static std::vector<StreamType> stream_types;
Mike Klein06432b22017-03-21 13:14:33 -0400180
Mike Klein7ac04832017-03-25 11:29:41 -0400181struct DstType {
Mike Kleine15a7b52017-03-29 12:41:13 -0400182 const char *name, *help;
Mike Klein0222e702017-03-25 15:53:14 -0400183 std::unique_ptr<Dst> (*factory)(Options);
Mike Klein51fe9712017-03-24 14:06:47 -0400184};
Mike Klein7ac04832017-03-25 11:29:41 -0400185static std::vector<DstType> dst_types;
Mike Klein51fe9712017-03-24 14:06:47 -0400186
Mike Kleinf5d1a552017-03-25 12:32:22 -0400187struct ViaType {
Mike Kleine15a7b52017-03-29 12:41:13 -0400188 const char *name, *help;
Mike Klein0222e702017-03-25 15:53:14 -0400189 std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>);
Mike Kleinf5d1a552017-03-25 12:32:22 -0400190};
191static std::vector<ViaType> via_types;
192
Mike Kleine15a7b52017-03-29 12:41:13 -0400193template <typename T>
194static std::string help_for(std::vector<T> registered) {
195 std::string help;
196 for (auto r : registered) {
197 help += "\n ";
198 help += r.name;
199 help += ": ";
200 help += r.help;
201 }
202 return help;
203}
204
Mike Klein06432b22017-03-21 13:14:33 -0400205int main(int argc, char** argv) {
206 SkGraphics::Init();
Mike Klein61e99022017-03-23 18:36:39 -0400207 setup_crash_handler();
Mike Klein06432b22017-03-21 13:14:33 -0400208
Mike Kleine15a7b52017-03-29 12:41:13 -0400209 int jobs{1};
Mike Klein0222e702017-03-25 15:53:14 -0400210 std::unique_ptr<Stream> stream;
Mike Kleine15a7b52017-03-29 12:41:13 -0400211 std::function<std::unique_ptr<Dst>(void)> dst_factory = []{
212 // A default Dst that's enough for unit tests and not much else.
213 struct : Dst {
214 Status draw(Src* src) override { return src->draw(nullptr); }
215 sk_sp<SkImage> image() override { return nullptr; }
216 } dst;
217 return move_unique(dst);
218 };
Mike Klein06432b22017-03-21 13:14:33 -0400219
220 auto help = [&] {
Mike Kleine15a7b52017-03-29 12:41:13 -0400221 std::string stream_help = help_for(stream_types),
222 dst_help = help_for( dst_types),
223 via_help = help_for( via_types);
Mike Klein06432b22017-03-21 13:14:33 -0400224
Mike Klein5a8da162017-04-12 11:02:44 -0400225 printf("%s [-j N] src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...] \n"
Mike Kleinf5d1a552017-03-25 12:32:22 -0400226 " -j: Run at most N processes at any time. \n"
227 " If <0, use -N threads instead. \n"
228 " If 0, use one thread in one process. \n"
229 " If 1 (default) or -1, auto-detect N. \n"
Mike Kleine15a7b52017-03-29 12:41:13 -0400230 " src: content to draw%s \n"
231 " dst: how to draw that content%s \n"
232 " via: wrappers around dst%s \n"
233 " Most srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n",
234 argv[0], stream_help.c_str(), dst_help.c_str(), via_help.c_str());
Mike Klein06432b22017-03-21 13:14:33 -0400235 return 1;
236 };
237
238 for (int i = 1; i < argc; i++) {
Mike Klein5a8da162017-04-12 11:02:44 -0400239 if (0 == strcmp("-j", argv[i])) { jobs = atoi(argv[++i]); }
240 if (0 == strcmp("-h", argv[i])) { return help(); }
241 if (0 == strcmp("--help", argv[i])) { return help(); }
Mike Klein06432b22017-03-21 13:14:33 -0400242
Mike Klein7ac04832017-03-25 11:29:41 -0400243 for (auto s : stream_types) {
Mike Klein51fe9712017-03-24 14:06:47 -0400244 size_t len = strlen(s.name);
245 if (0 == strncmp(s.name, argv[i], len)) {
Mike Klein06432b22017-03-21 13:14:33 -0400246 switch (argv[i][len]) {
247 case ':': len++;
Mike Klein51fe9712017-03-24 14:06:47 -0400248 case '\0': stream = s.factory(Options{argv[i]+len});
249 }
250 }
251 }
Mike Klein7ac04832017-03-25 11:29:41 -0400252 for (auto d : dst_types) {
Mike Klein51fe9712017-03-24 14:06:47 -0400253 size_t len = strlen(d.name);
254 if (0 == strncmp(d.name, argv[i], len)) {
255 switch (argv[i][len]) {
256 case ':': len++;
Mike Klein0222e702017-03-25 15:53:14 -0400257 case '\0': dst_factory = [=]{
258 return d.factory(Options{argv[i]+len});
Mike Kleinf5d1a552017-03-25 12:32:22 -0400259 };
260 }
261 }
262 }
263 for (auto v : via_types) {
264 size_t len = strlen(v.name);
265 if (0 == strncmp(v.name, argv[i], len)) {
Mike Klein0222e702017-03-25 15:53:14 -0400266 if (!dst_factory) { return help(); }
Mike Kleinf5d1a552017-03-25 12:32:22 -0400267 switch (argv[i][len]) {
268 case ':': len++;
Mike Klein0222e702017-03-25 15:53:14 -0400269 case '\0': dst_factory = [=]{
270 return v.factory(Options{argv[i]+len}, dst_factory());
Mike Klein7ac04832017-03-25 11:29:41 -0400271 };
Mike Klein06432b22017-03-21 13:14:33 -0400272 }
273 }
274 }
275 }
Mike Kleind63442d2017-03-27 14:16:04 -0400276 if (!stream) { return help(); }
Mike Klein06432b22017-03-21 13:14:33 -0400277
278 std::unique_ptr<Engine> engine;
Mike Klein200f6da2017-03-28 09:30:11 -0400279 if (jobs == 0) { engine.reset(new SerialEngine); }
280 if (jobs > 0) { engine.reset(new ForkEngine); defer_logging(); }
281 if (jobs < 0) { engine.reset(new ThreadEngine); jobs = -jobs; }
Mike Klein06432b22017-03-21 13:14:33 -0400282
283 if (jobs == 1) { jobs = std::thread::hardware_concurrency(); }
284
Mike Klein06432b22017-03-21 13:14:33 -0400285 int ok = 0, failed = 0, crashed = 0, skipped = 0;
286
287 auto update_stats = [&](Status s) {
288 switch (s) {
289 case Status::OK: ok++; break;
290 case Status::Failed: failed++; break;
291 case Status::Crashed: crashed++; break;
292 case Status::Skipped: skipped++; break;
293 case Status::None: return;
294 }
295 const char* leader = "\r";
296 auto print = [&](int count, const char* label) {
297 if (count) {
298 printf("%s%d %s", leader, count, label);
299 leader = ", ";
300 }
301 };
302 print(ok, "ok");
303 print(failed, "failed");
304 print(crashed, "crashed");
305 print(skipped, "skipped");
306 fflush(stdout);
307 };
308
309 auto spawn = [&](std::function<Status(void)> fn) {
310 if (--jobs < 0) {
311 update_stats(engine->wait_one());
312 }
313 while (!engine->spawn(fn)) {
314 update_stats(engine->wait_one());
315 }
316 };
317
318 for (std::unique_ptr<Src> owned = stream->next(); owned; owned = stream->next()) {
319 Src* raw = owned.release(); // Can't move std::unique_ptr into a lambda in C++11. :(
320 spawn([=] {
321 std::unique_ptr<Src> src{raw};
322
Mike Kleine15a7b52017-03-29 12:41:13 -0400323 std::string name = src->name();
324 tls_currently_running = name.c_str();
Mike Klein06432b22017-03-21 13:14:33 -0400325
Mike Kleine15a7b52017-03-29 12:41:13 -0400326 return dst_factory()->draw(src.get());
Mike Klein06432b22017-03-21 13:14:33 -0400327 });
328 }
329
330 for (Status s = Status::OK; s != Status::None; ) {
331 s = engine->wait_one();
332 update_stats(s);
333 }
334 printf("\n");
335 return (failed || crashed) ? 1 : 0;
336}
Mike Klein7ac04832017-03-25 11:29:41 -0400337
338
Mike Kleine15a7b52017-03-29 12:41:13 -0400339Register::Register(const char* name, const char* help,
340 std::unique_ptr<Stream> (*factory)(Options)) {
341 stream_types.push_back(StreamType{name, help, factory});
Mike Klein7ac04832017-03-25 11:29:41 -0400342}
Mike Kleine15a7b52017-03-29 12:41:13 -0400343Register::Register(const char* name, const char* help,
344 std::unique_ptr<Dst> (*factory)(Options)) {
345 dst_types.push_back(DstType{name, help, factory});
Mike Klein7ac04832017-03-25 11:29:41 -0400346}
Mike Kleine15a7b52017-03-29 12:41:13 -0400347Register::Register(const char* name, const char* help,
Mike Klein0222e702017-03-25 15:53:14 -0400348 std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>)) {
Mike Kleine15a7b52017-03-29 12:41:13 -0400349 via_types.push_back(ViaType{name, help, factory});
Mike Kleinf5d1a552017-03-25 12:32:22 -0400350}
Mike Klein7ac04832017-03-25 11:29:41 -0400351
352Options::Options(std::string str) {
353 std::string k,v, *curr = &k;
354 for (auto c : str) {
355 switch(c) {
Mike Klein88f9c1e2017-03-27 12:43:44 -0400356 case ',': (*this)[k] = v;
Mike Klein7ac04832017-03-25 11:29:41 -0400357 curr = &(k = "");
358 break;
359 case '=': curr = &(v = "");
360 break;
361 default: *curr += c;
362 }
363 }
Mike Klein88f9c1e2017-03-27 12:43:44 -0400364 (*this)[k] = v;
Mike Klein7ac04832017-03-25 11:29:41 -0400365}
366
Mike Klein88f9c1e2017-03-27 12:43:44 -0400367std::string& Options::operator[](std::string k) { return this->kv[k]; }
368
Mike Klein7ac04832017-03-25 11:29:41 -0400369std::string Options::operator()(std::string k, std::string fallback) const {
370 for (auto it = kv.find(k); it != kv.end(); ) {
371 return it->second;
372 }
373 return fallback;
374}