blob: ca99f33a162a0b37baa0f637a968caa5e7a69a5b [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 Klein98272562017-03-23 15:29:26 -040015#include <chrono>
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>
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
27#if __has_include(<execinfo.h>) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
28 #include <execinfo.h>
29 #include <fcntl.h>
30 #include <signal.h>
31
Mike Klein200f6da2017-03-28 09:30:11 -040032 static int log_fd = 2/*stderr*/;
33
34 static void log(const char* msg) {
35 write(log_fd, msg, strlen(msg));
36 }
Mike Klein61e99022017-03-23 18:36:39 -040037
38 static void setup_crash_handler() {
39 static void (*original_handlers[32])(int);
Mike Klein61e99022017-03-23 18:36:39 -040040 for (int sig : std::vector<int>{ SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }) {
41 original_handlers[sig] = signal(sig, [](int sig) {
Mike Klein200f6da2017-03-28 09:30:11 -040042 lockf(log_fd, F_LOCK, 0);
43 log("\ncaught signal ");
44 switch (sig) {
45 #define CASE(s) case s: log(#s); break
46 CASE(SIGABRT);
47 CASE(SIGBUS);
48 CASE(SIGFPE);
49 CASE(SIGILL);
50 CASE(SIGSEGV);
51 #undef CASE
52 }
53 log(" while running '");
Mike Kleine15a7b52017-03-29 12:41:13 -040054 log(tls_currently_running);
Mike Klein200f6da2017-03-28 09:30:11 -040055 log("'\n");
Mike Klein59eed0d2017-03-26 23:22:44 -040056
Mike Klein200f6da2017-03-28 09:30:11 -040057 void* stack[128];
58 int frames = backtrace(stack, sizeof(stack)/sizeof(*stack));
59 backtrace_symbols_fd(stack, frames, log_fd);
60 lockf(log_fd, F_ULOCK, 0);
Mike Klein61e99022017-03-23 18:36:39 -040061
Mike Klein61e99022017-03-23 18:36:39 -040062 signal(sig, original_handlers[sig]);
63 raise(sig);
64 });
65 }
66 }
67
Mike Klein200f6da2017-03-28 09:30:11 -040068 static void defer_logging() {
69 log_fd = fileno(tmpfile());
Mike Klein73714912017-07-26 18:30:57 +000070 atexit([] {
71 lseek(log_fd, 0, SEEK_SET);
72 char buf[1024];
73 while (size_t bytes = read(log_fd, buf, sizeof(buf))) {
74 write(2, buf, bytes);
75 }
76 });
Mike Klein61e99022017-03-23 18:36:39 -040077 }
Mike Klein200f6da2017-03-28 09:30:11 -040078
79 void ok_log(const char* msg) {
80 lockf(log_fd, F_LOCK, 0);
81 log("[");
Mike Kleine15a7b52017-03-29 12:41:13 -040082 log(tls_currently_running);
Mike Klein200f6da2017-03-28 09:30:11 -040083 log("]\t");
84 log(msg);
85 log("\n");
86 lockf(log_fd, F_ULOCK, 0);
87 }
88
Mike Klein61e99022017-03-23 18:36:39 -040089#else
90 static void setup_crash_handler() {}
Mike Klein200f6da2017-03-28 09:30:11 -040091 static void defer_logging() {}
92
93 void ok_log(const char* msg) {
Mike Klein84c54352017-08-29 19:59:57 -040094 fprintf(stderr, "[%s]\t%s\n", tls_currently_running, msg);
Mike Klein200f6da2017-03-28 09:30:11 -040095 }
Mike Klein61e99022017-03-23 18:36:39 -040096#endif
97
Mike Klein154e6da2017-07-26 15:13:47 -040098struct EngineType {
99 const char *name, *help;
100 std::unique_ptr<Engine> (*factory)(Options);
Mike Klein06432b22017-03-21 13:14:33 -0400101};
Mike Klein154e6da2017-07-26 15:13:47 -0400102static std::vector<EngineType> engine_types;
Mike Klein06432b22017-03-21 13:14:33 -0400103
Mike Klein7ac04832017-03-25 11:29:41 -0400104struct StreamType {
Mike Kleine15a7b52017-03-29 12:41:13 -0400105 const char *name, *help;
Mike Klein06432b22017-03-21 13:14:33 -0400106 std::unique_ptr<Stream> (*factory)(Options);
Mike Klein06432b22017-03-21 13:14:33 -0400107};
Mike Klein7ac04832017-03-25 11:29:41 -0400108static std::vector<StreamType> stream_types;
Mike Klein06432b22017-03-21 13:14:33 -0400109
Mike Klein7ac04832017-03-25 11:29:41 -0400110struct DstType {
Mike Kleine15a7b52017-03-29 12:41:13 -0400111 const char *name, *help;
Mike Klein0222e702017-03-25 15:53:14 -0400112 std::unique_ptr<Dst> (*factory)(Options);
Mike Klein51fe9712017-03-24 14:06:47 -0400113};
Mike Klein7ac04832017-03-25 11:29:41 -0400114static std::vector<DstType> dst_types;
Mike Klein51fe9712017-03-24 14:06:47 -0400115
Mike Kleinf5d1a552017-03-25 12:32:22 -0400116struct ViaType {
Mike Kleine15a7b52017-03-29 12:41:13 -0400117 const char *name, *help;
Mike Klein0222e702017-03-25 15:53:14 -0400118 std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>);
Mike Kleinf5d1a552017-03-25 12:32:22 -0400119};
120static std::vector<ViaType> via_types;
121
Mike Kleine15a7b52017-03-29 12:41:13 -0400122template <typename T>
123static std::string help_for(std::vector<T> registered) {
124 std::string help;
125 for (auto r : registered) {
126 help += "\n ";
127 help += r.name;
128 help += ": ";
129 help += r.help;
130 }
131 return help;
132}
133
Mike Klein06432b22017-03-21 13:14:33 -0400134int main(int argc, char** argv) {
135 SkGraphics::Init();
Mike Klein61e99022017-03-23 18:36:39 -0400136 setup_crash_handler();
Mike Klein06432b22017-03-21 13:14:33 -0400137
Mike Klein154e6da2017-07-26 15:13:47 -0400138 std::unique_ptr<Engine> engine;
Mike Klein0222e702017-03-25 15:53:14 -0400139 std::unique_ptr<Stream> stream;
Mike Kleine15a7b52017-03-29 12:41:13 -0400140 std::function<std::unique_ptr<Dst>(void)> dst_factory = []{
141 // A default Dst that's enough for unit tests and not much else.
142 struct : Dst {
143 Status draw(Src* src) override { return src->draw(nullptr); }
144 sk_sp<SkImage> image() override { return nullptr; }
145 } dst;
146 return move_unique(dst);
147 };
Mike Klein06432b22017-03-21 13:14:33 -0400148
149 auto help = [&] {
Mike Klein154e6da2017-07-26 15:13:47 -0400150 std::string engine_help = help_for(engine_types),
151 stream_help = help_for(stream_types),
Mike Kleine15a7b52017-03-29 12:41:13 -0400152 dst_help = help_for( dst_types),
153 via_help = help_for( via_types);
Mike Klein06432b22017-03-21 13:14:33 -0400154
Mike Klein154e6da2017-07-26 15:13:47 -0400155 printf("%s [engine] src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...] \n"
156 " engine: how to execute tasks%s \n"
Mike Kleine15a7b52017-03-29 12:41:13 -0400157 " src: content to draw%s \n"
158 " dst: how to draw that content%s \n"
159 " via: wrappers around dst%s \n"
160 " Most srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n",
Mike Klein154e6da2017-07-26 15:13:47 -0400161 argv[0],
162 engine_help.c_str(), stream_help.c_str(), dst_help.c_str(), via_help.c_str());
Mike Klein06432b22017-03-21 13:14:33 -0400163 return 1;
164 };
165
166 for (int i = 1; i < argc; i++) {
Mike Klein5a8da162017-04-12 11:02:44 -0400167 if (0 == strcmp("-h", argv[i])) { return help(); }
168 if (0 == strcmp("--help", argv[i])) { return help(); }
Mike Klein06432b22017-03-21 13:14:33 -0400169
Mike Klein154e6da2017-07-26 15:13:47 -0400170 for (auto e : engine_types) {
171 size_t len = strlen(e.name);
172 if (0 == strncmp(e.name, argv[i], len)) {
173 switch (argv[i][len]) {
174 case ':': len++;
175 case '\0': engine = e.factory(Options{argv[i]+len});
176 }
177 }
178 }
179
Mike Klein7ac04832017-03-25 11:29:41 -0400180 for (auto s : stream_types) {
Mike Klein51fe9712017-03-24 14:06:47 -0400181 size_t len = strlen(s.name);
182 if (0 == strncmp(s.name, argv[i], len)) {
Mike Klein06432b22017-03-21 13:14:33 -0400183 switch (argv[i][len]) {
184 case ':': len++;
Mike Klein51fe9712017-03-24 14:06:47 -0400185 case '\0': stream = s.factory(Options{argv[i]+len});
186 }
187 }
188 }
Mike Klein7ac04832017-03-25 11:29:41 -0400189 for (auto d : dst_types) {
Mike Klein51fe9712017-03-24 14:06:47 -0400190 size_t len = strlen(d.name);
191 if (0 == strncmp(d.name, argv[i], len)) {
192 switch (argv[i][len]) {
193 case ':': len++;
Mike Klein0222e702017-03-25 15:53:14 -0400194 case '\0': dst_factory = [=]{
195 return d.factory(Options{argv[i]+len});
Mike Kleinf5d1a552017-03-25 12:32:22 -0400196 };
197 }
198 }
199 }
200 for (auto v : via_types) {
201 size_t len = strlen(v.name);
202 if (0 == strncmp(v.name, argv[i], len)) {
Mike Klein0222e702017-03-25 15:53:14 -0400203 if (!dst_factory) { return help(); }
Mike Kleinf5d1a552017-03-25 12:32:22 -0400204 switch (argv[i][len]) {
205 case ':': len++;
Mike Klein0222e702017-03-25 15:53:14 -0400206 case '\0': dst_factory = [=]{
207 return v.factory(Options{argv[i]+len}, dst_factory());
Mike Klein7ac04832017-03-25 11:29:41 -0400208 };
Mike Klein06432b22017-03-21 13:14:33 -0400209 }
210 }
211 }
212 }
Mike Kleind63442d2017-03-27 14:16:04 -0400213 if (!stream) { return help(); }
Mike Klein06432b22017-03-21 13:14:33 -0400214
Mike Klein154e6da2017-07-26 15:13:47 -0400215 if (!engine) { engine = engine_types.back().factory(Options{}); }
Mike Klein06432b22017-03-21 13:14:33 -0400216
Mike Klein154e6da2017-07-26 15:13:47 -0400217 // If we know engine->spawn() will never crash, we can defer logging until we exit.
218 if (engine->crashproof()) {
219 defer_logging();
220 }
Mike Klein06432b22017-03-21 13:14:33 -0400221
Mike Klein06432b22017-03-21 13:14:33 -0400222 int ok = 0, failed = 0, crashed = 0, skipped = 0;
223
224 auto update_stats = [&](Status s) {
225 switch (s) {
226 case Status::OK: ok++; break;
227 case Status::Failed: failed++; break;
228 case Status::Crashed: crashed++; break;
229 case Status::Skipped: skipped++; break;
230 case Status::None: return;
231 }
232 const char* leader = "\r";
233 auto print = [&](int count, const char* label) {
234 if (count) {
235 printf("%s%d %s", leader, count, label);
236 leader = ", ";
237 }
238 };
239 print(ok, "ok");
240 print(failed, "failed");
241 print(crashed, "crashed");
242 print(skipped, "skipped");
243 fflush(stdout);
244 };
245
Mike Klein154e6da2017-07-26 15:13:47 -0400246 std::list<std::future<Status>> live;
247 const auto the_past = std::chrono::steady_clock::now();
248
249 auto wait_one = [&] {
250 if (live.empty()) {
251 return Status::None;
252 }
253
254 for (;;) {
255 for (auto it = live.begin(); it != live.end(); it++) {
256 if (it->wait_until(the_past) != std::future_status::timeout) {
257 Status s = it->get();
258 live.erase(it);
259 return s;
260 }
261 }
262 }
263 };
264
Mike Klein06432b22017-03-21 13:14:33 -0400265 auto spawn = [&](std::function<Status(void)> fn) {
Mike Klein154e6da2017-07-26 15:13:47 -0400266 std::future<Status> status;
267 for (;;) {
268 status = engine->spawn(fn);
269 if (status.valid()) {
270 break;
271 }
272 update_stats(wait_one());
Mike Klein06432b22017-03-21 13:14:33 -0400273 }
Mike Klein154e6da2017-07-26 15:13:47 -0400274 live.push_back(std::move(status));
Mike Klein06432b22017-03-21 13:14:33 -0400275 };
276
277 for (std::unique_ptr<Src> owned = stream->next(); owned; owned = stream->next()) {
278 Src* raw = owned.release(); // Can't move std::unique_ptr into a lambda in C++11. :(
279 spawn([=] {
280 std::unique_ptr<Src> src{raw};
281
Mike Kleine15a7b52017-03-29 12:41:13 -0400282 std::string name = src->name();
283 tls_currently_running = name.c_str();
Mike Klein06432b22017-03-21 13:14:33 -0400284
Mike Kleine15a7b52017-03-29 12:41:13 -0400285 return dst_factory()->draw(src.get());
Mike Klein06432b22017-03-21 13:14:33 -0400286 });
287 }
288
289 for (Status s = Status::OK; s != Status::None; ) {
Mike Klein154e6da2017-07-26 15:13:47 -0400290 s = wait_one();
Mike Klein06432b22017-03-21 13:14:33 -0400291 update_stats(s);
292 }
293 printf("\n");
294 return (failed || crashed) ? 1 : 0;
295}
Mike Klein7ac04832017-03-25 11:29:41 -0400296
297
Mike Kleine15a7b52017-03-29 12:41:13 -0400298Register::Register(const char* name, const char* help,
Mike Klein154e6da2017-07-26 15:13:47 -0400299 std::unique_ptr<Engine> (*factory)(Options)) {
300 engine_types.push_back(EngineType{name, help, factory});
301}
302Register::Register(const char* name, const char* help,
Mike Kleine15a7b52017-03-29 12:41:13 -0400303 std::unique_ptr<Stream> (*factory)(Options)) {
304 stream_types.push_back(StreamType{name, help, factory});
Mike Klein7ac04832017-03-25 11:29:41 -0400305}
Mike Kleine15a7b52017-03-29 12:41:13 -0400306Register::Register(const char* name, const char* help,
307 std::unique_ptr<Dst> (*factory)(Options)) {
308 dst_types.push_back(DstType{name, help, factory});
Mike Klein7ac04832017-03-25 11:29:41 -0400309}
Mike Kleine15a7b52017-03-29 12:41:13 -0400310Register::Register(const char* name, const char* help,
Mike Klein0222e702017-03-25 15:53:14 -0400311 std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>)) {
Mike Kleine15a7b52017-03-29 12:41:13 -0400312 via_types.push_back(ViaType{name, help, factory});
Mike Kleinf5d1a552017-03-25 12:32:22 -0400313}
Mike Klein7ac04832017-03-25 11:29:41 -0400314
315Options::Options(std::string str) {
316 std::string k,v, *curr = &k;
317 for (auto c : str) {
318 switch(c) {
Mike Klein88f9c1e2017-03-27 12:43:44 -0400319 case ',': (*this)[k] = v;
Mike Klein7ac04832017-03-25 11:29:41 -0400320 curr = &(k = "");
321 break;
322 case '=': curr = &(v = "");
323 break;
324 default: *curr += c;
325 }
326 }
Mike Klein88f9c1e2017-03-27 12:43:44 -0400327 (*this)[k] = v;
Mike Klein7ac04832017-03-25 11:29:41 -0400328}
329
Mike Klein88f9c1e2017-03-27 12:43:44 -0400330std::string& Options::operator[](std::string k) { return this->kv[k]; }
331
Mike Klein7ac04832017-03-25 11:29:41 -0400332std::string Options::operator()(std::string k, std::string fallback) const {
333 for (auto it = kv.find(k); it != kv.end(); ) {
334 return it->second;
335 }
336 return fallback;
337}