blob: 29c2a224f1e2eefba83f65a5c353db219abc4a04 [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 Klein9827256c2017-03-23 15:29:26 -040016#include <list>
Mike Klein06432b22017-03-21 13:14:33 -040017#include <stdio.h>
18#include <stdlib.h>
Mike Kleine15a7b52017-03-29 12:41:13 -040019#include <vector>
Mike Klein06432b22017-03-21 13:14:33 -040020
Mike Klein61e99022017-03-23 18:36:39 -040021#if !defined(__has_include)
22 #define __has_include(x) 0
23#endif
24
Mike Kleine15a7b52017-03-29 12:41:13 -040025static thread_local const char* tls_currently_running = "";
Mike Klein61e99022017-03-23 18:36:39 -040026
Mike Klein1b4602b2017-08-30 10:23:01 -040027#if __has_include(<execinfo.h>)
Mike Klein61e99022017-03-23 18:36:39 -040028 #include <execinfo.h>
Mike Klein1b4602b2017-08-30 10:23:01 -040029
30 #define CAN_BACKTRACE
31 static void backtrace(int fd) {
32 void* stack[128];
33 int frames = backtrace(stack, sizeof(stack)/sizeof(*stack));
34 backtrace_symbols_fd(stack, frames, fd);
35 }
36
37#elif __has_include(<dlfcn.h>) && __has_include(<unwind.h>)
38 #include <cxxabi.h>
39 #include <dlfcn.h>
40 #include <unwind.h>
41
42 #define CAN_BACKTRACE
43 static void backtrace(int fd) {
44 FILE* file = fdopen(fd, "a");
45 _Unwind_Backtrace([](_Unwind_Context* ctx, void* arg) {
46 auto file = (FILE*)arg;
47 if (auto ip = (void*)_Unwind_GetIP(ctx)) {
48 const char* name = "[unknown]";
49 void* addr = nullptr;
50 Dl_info info;
51 if (dladdr(ip, &info) && info.dli_sname && info.dli_saddr) {
52 name = info.dli_sname;
53 addr = info.dli_saddr;
54 }
55
56 int ok;
57 char* demangled = abi::__cxa_demangle(name, nullptr,0, &ok);
58 if (ok == 0 && demangled) {
59 name = demangled;
60 }
61
62 fprintf(file, "\t%p %s+%zu\n", ip, name, (size_t)ip - (size_t)addr);
63 free(demangled);
64 }
65 return _URC_NO_REASON;
66 }, file);
67 fflush(file);
68 }
69#endif
70
71#if defined(CAN_BACKTRACE) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
Mike Klein61e99022017-03-23 18:36:39 -040072 #include <fcntl.h>
73 #include <signal.h>
74
Mike Klein94fef592017-08-30 14:02:47 -040075 // We'd ordinarily just use lockf(), but fcntl() is more portable to older Android NDK APIs.
76 static void lock_or_unlock_fd(int fd, short type) {
77 struct flock fl{};
78 fl.l_type = type;
79 fl.l_whence = SEEK_CUR;
80 fl.l_start = 0;
81 fl.l_len = 0; // 0 == the entire file
82 fcntl(fd, F_SETLKW, &fl);
83 }
84 static void lock_fd(int fd) { lock_or_unlock_fd(fd, F_WRLCK); }
85 static void unlock_fd(int fd) { lock_or_unlock_fd(fd, F_UNLCK); }
Mike Klein1b4602b2017-08-30 10:23:01 -040086
Mike Klein200f6da2017-03-28 09:30:11 -040087 static int log_fd = 2/*stderr*/;
88
89 static void log(const char* msg) {
90 write(log_fd, msg, strlen(msg));
91 }
Mike Klein61e99022017-03-23 18:36:39 -040092
93 static void setup_crash_handler() {
94 static void (*original_handlers[32])(int);
Mike Klein61e99022017-03-23 18:36:39 -040095 for (int sig : std::vector<int>{ SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }) {
96 original_handlers[sig] = signal(sig, [](int sig) {
Mike Klein1b4602b2017-08-30 10:23:01 -040097 lock_fd(log_fd);
Mike Klein200f6da2017-03-28 09:30:11 -040098 log("\ncaught signal ");
99 switch (sig) {
100 #define CASE(s) case s: log(#s); break
101 CASE(SIGABRT);
102 CASE(SIGBUS);
103 CASE(SIGFPE);
104 CASE(SIGILL);
105 CASE(SIGSEGV);
106 #undef CASE
107 }
108 log(" while running '");
Mike Kleine15a7b52017-03-29 12:41:13 -0400109 log(tls_currently_running);
Mike Klein200f6da2017-03-28 09:30:11 -0400110 log("'\n");
Mike Klein1b4602b2017-08-30 10:23:01 -0400111 backtrace(log_fd);
112 unlock_fd(log_fd);
Mike Klein61e99022017-03-23 18:36:39 -0400113
Mike Klein61e99022017-03-23 18:36:39 -0400114 signal(sig, original_handlers[sig]);
115 raise(sig);
116 });
117 }
118 }
119
Mike Klein200f6da2017-03-28 09:30:11 -0400120 static void defer_logging() {
121 log_fd = fileno(tmpfile());
Mike Klein73714912017-07-26 18:30:57 +0000122 atexit([] {
123 lseek(log_fd, 0, SEEK_SET);
124 char buf[1024];
125 while (size_t bytes = read(log_fd, buf, sizeof(buf))) {
126 write(2, buf, bytes);
127 }
128 });
Mike Klein61e99022017-03-23 18:36:39 -0400129 }
Mike Klein200f6da2017-03-28 09:30:11 -0400130
131 void ok_log(const char* msg) {
Mike Klein1b4602b2017-08-30 10:23:01 -0400132 lock_fd(log_fd);
Mike Klein200f6da2017-03-28 09:30:11 -0400133 log("[");
Mike Kleine15a7b52017-03-29 12:41:13 -0400134 log(tls_currently_running);
Mike Klein200f6da2017-03-28 09:30:11 -0400135 log("]\t");
136 log(msg);
137 log("\n");
Mike Klein1b4602b2017-08-30 10:23:01 -0400138 unlock_fd(log_fd);
Mike Klein200f6da2017-03-28 09:30:11 -0400139 }
140
Mike Klein61e99022017-03-23 18:36:39 -0400141#else
142 static void setup_crash_handler() {}
Mike Klein200f6da2017-03-28 09:30:11 -0400143 static void defer_logging() {}
144
145 void ok_log(const char* msg) {
Mike Klein84c54352017-08-29 19:59:57 -0400146 fprintf(stderr, "[%s]\t%s\n", tls_currently_running, msg);
Mike Klein200f6da2017-03-28 09:30:11 -0400147 }
Mike Klein61e99022017-03-23 18:36:39 -0400148#endif
149
Mike Klein154e6da2017-07-26 15:13:47 -0400150struct EngineType {
151 const char *name, *help;
152 std::unique_ptr<Engine> (*factory)(Options);
Mike Klein06432b22017-03-21 13:14:33 -0400153};
Mike Klein154e6da2017-07-26 15:13:47 -0400154static std::vector<EngineType> engine_types;
Mike Klein06432b22017-03-21 13:14:33 -0400155
Mike Klein7ac04832017-03-25 11:29:41 -0400156struct StreamType {
Mike Kleine15a7b52017-03-29 12:41:13 -0400157 const char *name, *help;
Mike Klein06432b22017-03-21 13:14:33 -0400158 std::unique_ptr<Stream> (*factory)(Options);
Mike Klein06432b22017-03-21 13:14:33 -0400159};
Mike Klein7ac04832017-03-25 11:29:41 -0400160static std::vector<StreamType> stream_types;
Mike Klein06432b22017-03-21 13:14:33 -0400161
Mike Klein7ac04832017-03-25 11:29:41 -0400162struct DstType {
Mike Kleine15a7b52017-03-29 12:41:13 -0400163 const char *name, *help;
Mike Klein0222e702017-03-25 15:53:14 -0400164 std::unique_ptr<Dst> (*factory)(Options);
Mike Klein51fe9712017-03-24 14:06:47 -0400165};
Mike Klein7ac04832017-03-25 11:29:41 -0400166static std::vector<DstType> dst_types;
Mike Klein51fe9712017-03-24 14:06:47 -0400167
Mike Kleinf5d1a552017-03-25 12:32:22 -0400168struct ViaType {
Mike Kleine15a7b52017-03-29 12:41:13 -0400169 const char *name, *help;
Mike Klein0222e702017-03-25 15:53:14 -0400170 std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>);
Mike Kleinf5d1a552017-03-25 12:32:22 -0400171};
172static std::vector<ViaType> via_types;
173
Mike Kleine15a7b52017-03-29 12:41:13 -0400174template <typename T>
175static std::string help_for(std::vector<T> registered) {
176 std::string help;
177 for (auto r : registered) {
178 help += "\n ";
179 help += r.name;
180 help += ": ";
181 help += r.help;
182 }
183 return help;
184}
185
Mike Klein06432b22017-03-21 13:14:33 -0400186int main(int argc, char** argv) {
187 SkGraphics::Init();
Mike Klein61e99022017-03-23 18:36:39 -0400188 setup_crash_handler();
Mike Klein06432b22017-03-21 13:14:33 -0400189
Mike Klein154e6da2017-07-26 15:13:47 -0400190 std::unique_ptr<Engine> engine;
Mike Klein0222e702017-03-25 15:53:14 -0400191 std::unique_ptr<Stream> stream;
Mike Kleine15a7b52017-03-29 12:41:13 -0400192 std::function<std::unique_ptr<Dst>(void)> dst_factory = []{
193 // A default Dst that's enough for unit tests and not much else.
194 struct : Dst {
195 Status draw(Src* src) override { return src->draw(nullptr); }
196 sk_sp<SkImage> image() override { return nullptr; }
197 } dst;
198 return move_unique(dst);
199 };
Mike Klein06432b22017-03-21 13:14:33 -0400200
201 auto help = [&] {
Mike Klein154e6da2017-07-26 15:13:47 -0400202 std::string engine_help = help_for(engine_types),
203 stream_help = help_for(stream_types),
Mike Kleine15a7b52017-03-29 12:41:13 -0400204 dst_help = help_for( dst_types),
205 via_help = help_for( via_types);
Mike Klein06432b22017-03-21 13:14:33 -0400206
Mike Klein154e6da2017-07-26 15:13:47 -0400207 printf("%s [engine] src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...] \n"
208 " engine: how to execute tasks%s \n"
Mike Kleine15a7b52017-03-29 12:41:13 -0400209 " src: content to draw%s \n"
210 " dst: how to draw that content%s \n"
211 " via: wrappers around dst%s \n"
212 " Most srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n",
Mike Klein154e6da2017-07-26 15:13:47 -0400213 argv[0],
214 engine_help.c_str(), stream_help.c_str(), dst_help.c_str(), via_help.c_str());
Mike Klein06432b22017-03-21 13:14:33 -0400215 return 1;
216 };
217
218 for (int i = 1; i < argc; i++) {
Mike Klein5a8da162017-04-12 11:02:44 -0400219 if (0 == strcmp("-h", argv[i])) { return help(); }
220 if (0 == strcmp("--help", argv[i])) { return help(); }
Mike Klein06432b22017-03-21 13:14:33 -0400221
Mike Klein154e6da2017-07-26 15:13:47 -0400222 for (auto e : engine_types) {
223 size_t len = strlen(e.name);
224 if (0 == strncmp(e.name, argv[i], len)) {
225 switch (argv[i][len]) {
226 case ':': len++;
227 case '\0': engine = e.factory(Options{argv[i]+len});
228 }
229 }
230 }
231
Mike Klein7ac04832017-03-25 11:29:41 -0400232 for (auto s : stream_types) {
Mike Klein51fe9712017-03-24 14:06:47 -0400233 size_t len = strlen(s.name);
234 if (0 == strncmp(s.name, argv[i], len)) {
Mike Klein06432b22017-03-21 13:14:33 -0400235 switch (argv[i][len]) {
236 case ':': len++;
Mike Klein51fe9712017-03-24 14:06:47 -0400237 case '\0': stream = s.factory(Options{argv[i]+len});
238 }
239 }
240 }
Mike Klein7ac04832017-03-25 11:29:41 -0400241 for (auto d : dst_types) {
Mike Klein51fe9712017-03-24 14:06:47 -0400242 size_t len = strlen(d.name);
243 if (0 == strncmp(d.name, argv[i], len)) {
244 switch (argv[i][len]) {
245 case ':': len++;
Mike Klein0222e702017-03-25 15:53:14 -0400246 case '\0': dst_factory = [=]{
247 return d.factory(Options{argv[i]+len});
Mike Kleinf5d1a552017-03-25 12:32:22 -0400248 };
249 }
250 }
251 }
252 for (auto v : via_types) {
253 size_t len = strlen(v.name);
254 if (0 == strncmp(v.name, argv[i], len)) {
Mike Klein0222e702017-03-25 15:53:14 -0400255 if (!dst_factory) { return help(); }
Mike Kleinf5d1a552017-03-25 12:32:22 -0400256 switch (argv[i][len]) {
257 case ':': len++;
Mike Klein0222e702017-03-25 15:53:14 -0400258 case '\0': dst_factory = [=]{
259 return v.factory(Options{argv[i]+len}, dst_factory());
Mike Klein7ac04832017-03-25 11:29:41 -0400260 };
Mike Klein06432b22017-03-21 13:14:33 -0400261 }
262 }
263 }
264 }
Mike Kleind63442d2017-03-27 14:16:04 -0400265 if (!stream) { return help(); }
Mike Klein06432b22017-03-21 13:14:33 -0400266
Mike Klein154e6da2017-07-26 15:13:47 -0400267 if (!engine) { engine = engine_types.back().factory(Options{}); }
Mike Klein06432b22017-03-21 13:14:33 -0400268
Mike Klein154e6da2017-07-26 15:13:47 -0400269 // If we know engine->spawn() will never crash, we can defer logging until we exit.
270 if (engine->crashproof()) {
271 defer_logging();
272 }
Mike Klein06432b22017-03-21 13:14:33 -0400273
Mike Klein06432b22017-03-21 13:14:33 -0400274 int ok = 0, failed = 0, crashed = 0, skipped = 0;
275
276 auto update_stats = [&](Status s) {
277 switch (s) {
278 case Status::OK: ok++; break;
279 case Status::Failed: failed++; break;
280 case Status::Crashed: crashed++; break;
281 case Status::Skipped: skipped++; break;
282 case Status::None: return;
283 }
284 const char* leader = "\r";
285 auto print = [&](int count, const char* label) {
286 if (count) {
287 printf("%s%d %s", leader, count, label);
288 leader = ", ";
289 }
290 };
291 print(ok, "ok");
292 print(failed, "failed");
293 print(crashed, "crashed");
294 print(skipped, "skipped");
295 fflush(stdout);
296 };
297
Mike Klein154e6da2017-07-26 15:13:47 -0400298 std::list<std::future<Status>> live;
299 const auto the_past = std::chrono::steady_clock::now();
300
301 auto wait_one = [&] {
302 if (live.empty()) {
303 return Status::None;
304 }
305
306 for (;;) {
307 for (auto it = live.begin(); it != live.end(); it++) {
308 if (it->wait_until(the_past) != std::future_status::timeout) {
309 Status s = it->get();
310 live.erase(it);
311 return s;
312 }
313 }
314 }
315 };
316
Mike Klein06432b22017-03-21 13:14:33 -0400317 auto spawn = [&](std::function<Status(void)> fn) {
Mike Klein154e6da2017-07-26 15:13:47 -0400318 std::future<Status> status;
319 for (;;) {
320 status = engine->spawn(fn);
321 if (status.valid()) {
322 break;
323 }
324 update_stats(wait_one());
Mike Klein06432b22017-03-21 13:14:33 -0400325 }
Mike Klein154e6da2017-07-26 15:13:47 -0400326 live.push_back(std::move(status));
Mike Klein06432b22017-03-21 13:14:33 -0400327 };
328
329 for (std::unique_ptr<Src> owned = stream->next(); owned; owned = stream->next()) {
330 Src* raw = owned.release(); // Can't move std::unique_ptr into a lambda in C++11. :(
331 spawn([=] {
332 std::unique_ptr<Src> src{raw};
333
Mike Kleine15a7b52017-03-29 12:41:13 -0400334 std::string name = src->name();
335 tls_currently_running = name.c_str();
Mike Klein06432b22017-03-21 13:14:33 -0400336
Mike Kleine15a7b52017-03-29 12:41:13 -0400337 return dst_factory()->draw(src.get());
Mike Klein06432b22017-03-21 13:14:33 -0400338 });
339 }
340
341 for (Status s = Status::OK; s != Status::None; ) {
Mike Klein154e6da2017-07-26 15:13:47 -0400342 s = wait_one();
Mike Klein06432b22017-03-21 13:14:33 -0400343 update_stats(s);
344 }
345 printf("\n");
346 return (failed || crashed) ? 1 : 0;
347}
Mike Klein7ac04832017-03-25 11:29:41 -0400348
349
Mike Kleine15a7b52017-03-29 12:41:13 -0400350Register::Register(const char* name, const char* help,
Mike Klein154e6da2017-07-26 15:13:47 -0400351 std::unique_ptr<Engine> (*factory)(Options)) {
352 engine_types.push_back(EngineType{name, help, factory});
353}
354Register::Register(const char* name, const char* help,
Mike Kleine15a7b52017-03-29 12:41:13 -0400355 std::unique_ptr<Stream> (*factory)(Options)) {
356 stream_types.push_back(StreamType{name, help, factory});
Mike Klein7ac04832017-03-25 11:29:41 -0400357}
Mike Kleine15a7b52017-03-29 12:41:13 -0400358Register::Register(const char* name, const char* help,
359 std::unique_ptr<Dst> (*factory)(Options)) {
360 dst_types.push_back(DstType{name, help, factory});
Mike Klein7ac04832017-03-25 11:29:41 -0400361}
Mike Kleine15a7b52017-03-29 12:41:13 -0400362Register::Register(const char* name, const char* help,
Mike Klein0222e702017-03-25 15:53:14 -0400363 std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>)) {
Mike Kleine15a7b52017-03-29 12:41:13 -0400364 via_types.push_back(ViaType{name, help, factory});
Mike Kleinf5d1a552017-03-25 12:32:22 -0400365}
Mike Klein7ac04832017-03-25 11:29:41 -0400366
367Options::Options(std::string str) {
368 std::string k,v, *curr = &k;
369 for (auto c : str) {
370 switch(c) {
Mike Klein88f9c1e2017-03-27 12:43:44 -0400371 case ',': (*this)[k] = v;
Mike Klein7ac04832017-03-25 11:29:41 -0400372 curr = &(k = "");
373 break;
374 case '=': curr = &(v = "");
375 break;
376 default: *curr += c;
377 }
378 }
Mike Klein88f9c1e2017-03-27 12:43:44 -0400379 (*this)[k] = v;
Mike Klein7ac04832017-03-25 11:29:41 -0400380}
381
Mike Klein88f9c1e2017-03-27 12:43:44 -0400382std::string& Options::operator[](std::string k) { return this->kv[k]; }
383
Mike Klein7ac04832017-03-25 11:29:41 -0400384std::string Options::operator()(std::string k, std::string fallback) const {
385 for (auto it = kv.find(k); it != kv.end(); ) {
386 return it->second;
387 }
388 return fallback;
389}