blob: de5727b8152b7fb2766db18edd31b3657d79cf98 [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"
Mike Klein7ac04832017-03-25 11:29:41 -040013#include "ok.h"
Mike Klein98272562017-03-23 15:29:26 -040014#include <chrono>
Mike Klein06432b22017-03-21 13:14:33 -040015#include <future>
Mike Klein98272562017-03-23 15:29:26 -040016#include <list>
Mike Klein06432b22017-03-21 13:14:33 -040017#include <stdio.h>
18#include <stdlib.h>
19#include <thread>
Mike Kleine15a7b52017-03-29 12:41:13 -040020#include <vector>
Mike Klein06432b22017-03-21 13:14:33 -040021
Mike Klein61e99022017-03-23 18:36:39 -040022#if !defined(__has_include)
23 #define __has_include(x) 0
24#endif
25
Mike Kleine15a7b52017-03-29 12:41:13 -040026static thread_local const char* tls_currently_running = "";
Mike Klein61e99022017-03-23 18:36:39 -040027
28#if __has_include(<execinfo.h>) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
29 #include <execinfo.h>
30 #include <fcntl.h>
31 #include <signal.h>
32
Mike Klein200f6da2017-03-28 09:30:11 -040033 static int log_fd = 2/*stderr*/;
34
35 static void log(const char* msg) {
36 write(log_fd, msg, strlen(msg));
37 }
Mike Klein61e99022017-03-23 18:36:39 -040038
39 static void setup_crash_handler() {
40 static void (*original_handlers[32])(int);
Mike Klein61e99022017-03-23 18:36:39 -040041 for (int sig : std::vector<int>{ SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }) {
42 original_handlers[sig] = signal(sig, [](int sig) {
Mike Klein200f6da2017-03-28 09:30:11 -040043 lockf(log_fd, F_LOCK, 0);
44 log("\ncaught signal ");
45 switch (sig) {
46 #define CASE(s) case s: log(#s); break
47 CASE(SIGABRT);
48 CASE(SIGBUS);
49 CASE(SIGFPE);
50 CASE(SIGILL);
51 CASE(SIGSEGV);
52 #undef CASE
53 }
54 log(" while running '");
Mike Kleine15a7b52017-03-29 12:41:13 -040055 log(tls_currently_running);
Mike Klein200f6da2017-03-28 09:30:11 -040056 log("'\n");
Mike Klein59eed0d2017-03-26 23:22:44 -040057
Mike Klein200f6da2017-03-28 09:30:11 -040058 void* stack[128];
59 int frames = backtrace(stack, sizeof(stack)/sizeof(*stack));
60 backtrace_symbols_fd(stack, frames, log_fd);
61 lockf(log_fd, F_ULOCK, 0);
Mike Klein61e99022017-03-23 18:36:39 -040062
Mike Klein61e99022017-03-23 18:36:39 -040063 signal(sig, original_handlers[sig]);
64 raise(sig);
65 });
66 }
67 }
68
Mike Klein200f6da2017-03-28 09:30:11 -040069 static void defer_logging() {
70 log_fd = fileno(tmpfile());
Mike Klein61e99022017-03-23 18:36:39 -040071 atexit([] {
Mike Klein200f6da2017-03-28 09:30:11 -040072 lseek(log_fd, 0, SEEK_SET);
Mike Klein61e99022017-03-23 18:36:39 -040073 char buf[1024];
Mike Klein200f6da2017-03-28 09:30:11 -040074 while (size_t bytes = read(log_fd, buf, sizeof(buf))) {
Mike Klein61e99022017-03-23 18:36:39 -040075 write(2, buf, bytes);
76 }
77 });
78 }
Mike Klein200f6da2017-03-28 09:30:11 -040079
80 void ok_log(const char* msg) {
81 lockf(log_fd, F_LOCK, 0);
82 log("[");
Mike Kleine15a7b52017-03-29 12:41:13 -040083 log(tls_currently_running);
Mike Klein200f6da2017-03-28 09:30:11 -040084 log("]\t");
85 log(msg);
86 log("\n");
87 lockf(log_fd, F_ULOCK, 0);
88 }
89
Mike Klein61e99022017-03-23 18:36:39 -040090#else
91 static void setup_crash_handler() {}
Mike Klein200f6da2017-03-28 09:30:11 -040092 static void defer_logging() {}
93
94 void ok_log(const char* msg) {
95 fprintf(stderr, "%s\n", msg);
96 }
Mike Klein61e99022017-03-23 18:36:39 -040097#endif
98
Mike Klein06432b22017-03-21 13:14:33 -040099struct Engine {
100 virtual ~Engine() {}
101 virtual bool spawn(std::function<Status(void)>) = 0;
102 virtual Status wait_one() = 0;
103};
104
105struct SerialEngine : Engine {
106 Status last = Status::None;
107
108 bool spawn(std::function<Status(void)> fn) override {
109 last = fn();
110 return true;
111 }
112
113 Status wait_one() override {
114 Status s = last;
115 last = Status::None;
116 return s;
117 }
118};
119
120struct ThreadEngine : Engine {
Mike Klein98272562017-03-23 15:29:26 -0400121 std::list<std::future<Status>> live;
Mike Klein06432b22017-03-21 13:14:33 -0400122
123 bool spawn(std::function<Status(void)> fn) override {
124 live.push_back(std::async(std::launch::async, fn));
125 return true;
126 }
127
128 Status wait_one() override {
129 if (live.empty()) {
130 return Status::None;
131 }
Mike Klein98272562017-03-23 15:29:26 -0400132
133 for (;;) {
134 for (auto it = live.begin(); it != live.end(); it++) {
135 if (it->wait_for(std::chrono::seconds::zero()) == std::future_status::ready) {
136 Status s = it->get();
137 live.erase(it);
138 return s;
139 }
140 }
141 }
Mike Klein06432b22017-03-21 13:14:33 -0400142 }
143};
144
145#if defined(_MSC_VER)
146 using ForkEngine = ThreadEngine;
147#else
148 #include <sys/wait.h>
149 #include <unistd.h>
150
151 struct ForkEngine : Engine {
152 bool spawn(std::function<Status(void)> fn) override {
153 switch (fork()) {
Mike Klein61e99022017-03-23 18:36:39 -0400154 case 0: _exit((int)fn());
Mike Klein06432b22017-03-21 13:14:33 -0400155 case -1: return false;
156 default: return true;
157 }
158 }
159
160 Status wait_one() override {
161 do {
162 int status;
163 if (wait(&status) > 0) {
164 return WIFEXITED(status) ? (Status)WEXITSTATUS(status)
165 : Status::Crashed;
166 }
167 } while (errno == EINTR);
168 return Status::None;
169 }
170 };
171#endif
172
Mike Klein7ac04832017-03-25 11:29:41 -0400173struct StreamType {
Mike Kleine15a7b52017-03-29 12:41:13 -0400174 const char *name, *help;
Mike Klein06432b22017-03-21 13:14:33 -0400175 std::unique_ptr<Stream> (*factory)(Options);
Mike Klein06432b22017-03-21 13:14:33 -0400176};
Mike Klein7ac04832017-03-25 11:29:41 -0400177static std::vector<StreamType> stream_types;
Mike Klein06432b22017-03-21 13:14:33 -0400178
Mike Klein7ac04832017-03-25 11:29:41 -0400179struct DstType {
Mike Kleine15a7b52017-03-29 12:41:13 -0400180 const char *name, *help;
Mike Klein0222e702017-03-25 15:53:14 -0400181 std::unique_ptr<Dst> (*factory)(Options);
Mike Klein51fe9712017-03-24 14:06:47 -0400182};
Mike Klein7ac04832017-03-25 11:29:41 -0400183static std::vector<DstType> dst_types;
Mike Klein51fe9712017-03-24 14:06:47 -0400184
Mike Kleinf5d1a552017-03-25 12:32:22 -0400185struct ViaType {
Mike Kleine15a7b52017-03-29 12:41:13 -0400186 const char *name, *help;
Mike Klein0222e702017-03-25 15:53:14 -0400187 std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>);
Mike Kleinf5d1a552017-03-25 12:32:22 -0400188};
189static std::vector<ViaType> via_types;
190
Mike Kleine15a7b52017-03-29 12:41:13 -0400191template <typename T>
192static std::string help_for(std::vector<T> registered) {
193 std::string help;
194 for (auto r : registered) {
195 help += "\n ";
196 help += r.name;
197 help += ": ";
198 help += r.help;
199 }
200 return help;
201}
202
Mike Klein06432b22017-03-21 13:14:33 -0400203int main(int argc, char** argv) {
204 SkGraphics::Init();
Mike Klein61e99022017-03-23 18:36:39 -0400205 setup_crash_handler();
Mike Klein06432b22017-03-21 13:14:33 -0400206
Mike Kleine15a7b52017-03-29 12:41:13 -0400207 int jobs{1};
Mike Klein0222e702017-03-25 15:53:14 -0400208 std::unique_ptr<Stream> stream;
Mike Kleine15a7b52017-03-29 12:41:13 -0400209 std::function<std::unique_ptr<Dst>(void)> dst_factory = []{
210 // A default Dst that's enough for unit tests and not much else.
211 struct : Dst {
212 Status draw(Src* src) override { return src->draw(nullptr); }
213 sk_sp<SkImage> image() override { return nullptr; }
214 } dst;
215 return move_unique(dst);
216 };
Mike Klein06432b22017-03-21 13:14:33 -0400217
218 auto help = [&] {
Mike Kleine15a7b52017-03-29 12:41:13 -0400219 std::string stream_help = help_for(stream_types),
220 dst_help = help_for( dst_types),
221 via_help = help_for( via_types);
Mike Klein06432b22017-03-21 13:14:33 -0400222
Mike Kleinf5d1a552017-03-25 12:32:22 -0400223 printf("%s [-j N] [-m regex] [-s regex] [-w dir] [-h] \n"
Mike Klein0222e702017-03-25 15:53:14 -0400224 " src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...] \n"
Mike Kleinf5d1a552017-03-25 12:32:22 -0400225 " -j: Run at most N processes at any time. \n"
226 " If <0, use -N threads instead. \n"
227 " If 0, use one thread in one process. \n"
228 " If 1 (default) or -1, auto-detect N. \n"
Mike Kleinf5d1a552017-03-25 12:32:22 -0400229 " -h: Print this message and exit. \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 Kleine15a7b52017-03-29 12:41:13 -0400239 if (0 == strcmp("-j", argv[i])) { jobs = atoi(argv[++i]); }
Mike Klein06432b22017-03-21 13:14:33 -0400240 if (0 == strcmp("-h", argv[i])) { return help(); }
241
Mike Klein7ac04832017-03-25 11:29:41 -0400242 for (auto s : stream_types) {
Mike Klein51fe9712017-03-24 14:06:47 -0400243 size_t len = strlen(s.name);
244 if (0 == strncmp(s.name, argv[i], len)) {
Mike Klein06432b22017-03-21 13:14:33 -0400245 switch (argv[i][len]) {
246 case ':': len++;
Mike Klein51fe9712017-03-24 14:06:47 -0400247 case '\0': stream = s.factory(Options{argv[i]+len});
248 }
249 }
250 }
Mike Klein7ac04832017-03-25 11:29:41 -0400251 for (auto d : dst_types) {
Mike Klein51fe9712017-03-24 14:06:47 -0400252 size_t len = strlen(d.name);
253 if (0 == strncmp(d.name, argv[i], len)) {
254 switch (argv[i][len]) {
255 case ':': len++;
Mike Klein0222e702017-03-25 15:53:14 -0400256 case '\0': dst_factory = [=]{
257 return d.factory(Options{argv[i]+len});
Mike Kleinf5d1a552017-03-25 12:32:22 -0400258 };
259 }
260 }
261 }
262 for (auto v : via_types) {
263 size_t len = strlen(v.name);
264 if (0 == strncmp(v.name, argv[i], len)) {
Mike Klein0222e702017-03-25 15:53:14 -0400265 if (!dst_factory) { return help(); }
Mike Kleinf5d1a552017-03-25 12:32:22 -0400266 switch (argv[i][len]) {
267 case ':': len++;
Mike Klein0222e702017-03-25 15:53:14 -0400268 case '\0': dst_factory = [=]{
269 return v.factory(Options{argv[i]+len}, dst_factory());
Mike Klein7ac04832017-03-25 11:29:41 -0400270 };
Mike Klein06432b22017-03-21 13:14:33 -0400271 }
272 }
273 }
274 }
Mike Kleind63442d2017-03-27 14:16:04 -0400275 if (!stream) { return help(); }
Mike Klein06432b22017-03-21 13:14:33 -0400276
277 std::unique_ptr<Engine> engine;
Mike Klein200f6da2017-03-28 09:30:11 -0400278 if (jobs == 0) { engine.reset(new SerialEngine); }
279 if (jobs > 0) { engine.reset(new ForkEngine); defer_logging(); }
280 if (jobs < 0) { engine.reset(new ThreadEngine); jobs = -jobs; }
Mike Klein06432b22017-03-21 13:14:33 -0400281
282 if (jobs == 1) { jobs = std::thread::hardware_concurrency(); }
283
Mike Klein06432b22017-03-21 13:14:33 -0400284 int ok = 0, failed = 0, crashed = 0, skipped = 0;
285
286 auto update_stats = [&](Status s) {
287 switch (s) {
288 case Status::OK: ok++; break;
289 case Status::Failed: failed++; break;
290 case Status::Crashed: crashed++; break;
291 case Status::Skipped: skipped++; break;
292 case Status::None: return;
293 }
294 const char* leader = "\r";
295 auto print = [&](int count, const char* label) {
296 if (count) {
297 printf("%s%d %s", leader, count, label);
298 leader = ", ";
299 }
300 };
301 print(ok, "ok");
302 print(failed, "failed");
303 print(crashed, "crashed");
304 print(skipped, "skipped");
305 fflush(stdout);
306 };
307
308 auto spawn = [&](std::function<Status(void)> fn) {
309 if (--jobs < 0) {
310 update_stats(engine->wait_one());
311 }
312 while (!engine->spawn(fn)) {
313 update_stats(engine->wait_one());
314 }
315 };
316
317 for (std::unique_ptr<Src> owned = stream->next(); owned; owned = stream->next()) {
318 Src* raw = owned.release(); // Can't move std::unique_ptr into a lambda in C++11. :(
319 spawn([=] {
320 std::unique_ptr<Src> src{raw};
321
Mike Kleine15a7b52017-03-29 12:41:13 -0400322 std::string name = src->name();
323 tls_currently_running = name.c_str();
Mike Klein06432b22017-03-21 13:14:33 -0400324
Mike Kleine15a7b52017-03-29 12:41:13 -0400325 return dst_factory()->draw(src.get());
Mike Klein06432b22017-03-21 13:14:33 -0400326 });
327 }
328
329 for (Status s = Status::OK; s != Status::None; ) {
330 s = engine->wait_one();
331 update_stats(s);
332 }
333 printf("\n");
334 return (failed || crashed) ? 1 : 0;
335}
Mike Klein7ac04832017-03-25 11:29:41 -0400336
337
Mike Kleine15a7b52017-03-29 12:41:13 -0400338Register::Register(const char* name, const char* help,
339 std::unique_ptr<Stream> (*factory)(Options)) {
340 stream_types.push_back(StreamType{name, help, factory});
Mike Klein7ac04832017-03-25 11:29:41 -0400341}
Mike Kleine15a7b52017-03-29 12:41:13 -0400342Register::Register(const char* name, const char* help,
343 std::unique_ptr<Dst> (*factory)(Options)) {
344 dst_types.push_back(DstType{name, help, factory});
Mike Klein7ac04832017-03-25 11:29:41 -0400345}
Mike Kleine15a7b52017-03-29 12:41:13 -0400346Register::Register(const char* name, const char* help,
Mike Klein0222e702017-03-25 15:53:14 -0400347 std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>)) {
Mike Kleine15a7b52017-03-29 12:41:13 -0400348 via_types.push_back(ViaType{name, help, factory});
Mike Kleinf5d1a552017-03-25 12:32:22 -0400349}
Mike Klein7ac04832017-03-25 11:29:41 -0400350
351Options::Options(std::string str) {
352 std::string k,v, *curr = &k;
353 for (auto c : str) {
354 switch(c) {
Mike Klein88f9c1e2017-03-27 12:43:44 -0400355 case ',': (*this)[k] = v;
Mike Klein7ac04832017-03-25 11:29:41 -0400356 curr = &(k = "");
357 break;
358 case '=': curr = &(v = "");
359 break;
360 default: *curr += c;
361 }
362 }
Mike Klein88f9c1e2017-03-27 12:43:44 -0400363 (*this)[k] = v;
Mike Klein7ac04832017-03-25 11:29:41 -0400364}
365
Mike Klein88f9c1e2017-03-27 12:43:44 -0400366std::string& Options::operator[](std::string k) { return this->kv[k]; }
367
Mike Klein7ac04832017-03-25 11:29:41 -0400368std::string Options::operator()(std::string k, std::string fallback) const {
369 for (auto it = kv.find(k); it != kv.end(); ) {
370 return it->second;
371 }
372 return fallback;
373}