aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/ok.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ok.cpp')
-rw-r--r--tools/ok.cpp389
1 files changed, 0 insertions, 389 deletions
diff --git a/tools/ok.cpp b/tools/ok.cpp
deleted file mode 100644
index 29c2a224f1..0000000000
--- a/tools/ok.cpp
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-// ok is an experimental test harness, maybe to replace DM. Key features:
-// * work is balanced across separate processes for stability and isolation;
-// * ok is entirely opt-in. No more maintaining huge --blacklists.
-
-#include "SkGraphics.h"
-#include "SkImage.h"
-#include "ok.h"
-#include <chrono>
-#include <list>
-#include <stdio.h>
-#include <stdlib.h>
-#include <vector>
-
-#if !defined(__has_include)
- #define __has_include(x) 0
-#endif
-
-static thread_local const char* tls_currently_running = "";
-
-#if __has_include(<execinfo.h>)
- #include <execinfo.h>
-
- #define CAN_BACKTRACE
- static void backtrace(int fd) {
- void* stack[128];
- int frames = backtrace(stack, sizeof(stack)/sizeof(*stack));
- backtrace_symbols_fd(stack, frames, fd);
- }
-
-#elif __has_include(<dlfcn.h>) && __has_include(<unwind.h>)
- #include <cxxabi.h>
- #include <dlfcn.h>
- #include <unwind.h>
-
- #define CAN_BACKTRACE
- static void backtrace(int fd) {
- FILE* file = fdopen(fd, "a");
- _Unwind_Backtrace([](_Unwind_Context* ctx, void* arg) {
- auto file = (FILE*)arg;
- if (auto ip = (void*)_Unwind_GetIP(ctx)) {
- const char* name = "[unknown]";
- void* addr = nullptr;
- Dl_info info;
- if (dladdr(ip, &info) && info.dli_sname && info.dli_saddr) {
- name = info.dli_sname;
- addr = info.dli_saddr;
- }
-
- int ok;
- char* demangled = abi::__cxa_demangle(name, nullptr,0, &ok);
- if (ok == 0 && demangled) {
- name = demangled;
- }
-
- fprintf(file, "\t%p %s+%zu\n", ip, name, (size_t)ip - (size_t)addr);
- free(demangled);
- }
- return _URC_NO_REASON;
- }, file);
- fflush(file);
- }
-#endif
-
-#if defined(CAN_BACKTRACE) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
- #include <fcntl.h>
- #include <signal.h>
-
- // We'd ordinarily just use lockf(), but fcntl() is more portable to older Android NDK APIs.
- static void lock_or_unlock_fd(int fd, short type) {
- struct flock fl{};
- fl.l_type = type;
- fl.l_whence = SEEK_CUR;
- fl.l_start = 0;
- fl.l_len = 0; // 0 == the entire file
- fcntl(fd, F_SETLKW, &fl);
- }
- static void lock_fd(int fd) { lock_or_unlock_fd(fd, F_WRLCK); }
- static void unlock_fd(int fd) { lock_or_unlock_fd(fd, F_UNLCK); }
-
- static int log_fd = 2/*stderr*/;
-
- static void log(const char* msg) {
- write(log_fd, msg, strlen(msg));
- }
-
- static void setup_crash_handler() {
- static void (*original_handlers[32])(int);
- for (int sig : std::vector<int>{ SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }) {
- original_handlers[sig] = signal(sig, [](int sig) {
- lock_fd(log_fd);
- log("\ncaught signal ");
- switch (sig) {
- #define CASE(s) case s: log(#s); break
- CASE(SIGABRT);
- CASE(SIGBUS);
- CASE(SIGFPE);
- CASE(SIGILL);
- CASE(SIGSEGV);
- #undef CASE
- }
- log(" while running '");
- log(tls_currently_running);
- log("'\n");
- backtrace(log_fd);
- unlock_fd(log_fd);
-
- signal(sig, original_handlers[sig]);
- raise(sig);
- });
- }
- }
-
- static void defer_logging() {
- log_fd = fileno(tmpfile());
- atexit([] {
- lseek(log_fd, 0, SEEK_SET);
- char buf[1024];
- while (size_t bytes = read(log_fd, buf, sizeof(buf))) {
- write(2, buf, bytes);
- }
- });
- }
-
- void ok_log(const char* msg) {
- lock_fd(log_fd);
- log("[");
- log(tls_currently_running);
- log("]\t");
- log(msg);
- log("\n");
- unlock_fd(log_fd);
- }
-
-#else
- static void setup_crash_handler() {}
- static void defer_logging() {}
-
- void ok_log(const char* msg) {
- fprintf(stderr, "[%s]\t%s\n", tls_currently_running, msg);
- }
-#endif
-
-struct EngineType {
- const char *name, *help;
- std::unique_ptr<Engine> (*factory)(Options);
-};
-static std::vector<EngineType> engine_types;
-
-struct StreamType {
- const char *name, *help;
- std::unique_ptr<Stream> (*factory)(Options);
-};
-static std::vector<StreamType> stream_types;
-
-struct DstType {
- const char *name, *help;
- std::unique_ptr<Dst> (*factory)(Options);
-};
-static std::vector<DstType> dst_types;
-
-struct ViaType {
- const char *name, *help;
- std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>);
-};
-static std::vector<ViaType> via_types;
-
-template <typename T>
-static std::string help_for(std::vector<T> registered) {
- std::string help;
- for (auto r : registered) {
- help += "\n ";
- help += r.name;
- help += ": ";
- help += r.help;
- }
- return help;
-}
-
-int main(int argc, char** argv) {
- SkGraphics::Init();
- setup_crash_handler();
-
- std::unique_ptr<Engine> engine;
- std::unique_ptr<Stream> stream;
- std::function<std::unique_ptr<Dst>(void)> dst_factory = []{
- // A default Dst that's enough for unit tests and not much else.
- struct : Dst {
- Status draw(Src* src) override { return src->draw(nullptr); }
- sk_sp<SkImage> image() override { return nullptr; }
- } dst;
- return move_unique(dst);
- };
-
- auto help = [&] {
- std::string engine_help = help_for(engine_types),
- stream_help = help_for(stream_types),
- dst_help = help_for( dst_types),
- via_help = help_for( via_types);
-
- printf("%s [engine] src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...] \n"
- " engine: how to execute tasks%s \n"
- " src: content to draw%s \n"
- " dst: how to draw that content%s \n"
- " via: wrappers around dst%s \n"
- " Most srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n",
- argv[0],
- engine_help.c_str(), stream_help.c_str(), dst_help.c_str(), via_help.c_str());
- return 1;
- };
-
- for (int i = 1; i < argc; i++) {
- if (0 == strcmp("-h", argv[i])) { return help(); }
- if (0 == strcmp("--help", argv[i])) { return help(); }
-
- for (auto e : engine_types) {
- size_t len = strlen(e.name);
- if (0 == strncmp(e.name, argv[i], len)) {
- switch (argv[i][len]) {
- case ':': len++;
- case '\0': engine = e.factory(Options{argv[i]+len});
- }
- }
- }
-
- for (auto s : stream_types) {
- size_t len = strlen(s.name);
- if (0 == strncmp(s.name, argv[i], len)) {
- switch (argv[i][len]) {
- case ':': len++;
- case '\0': stream = s.factory(Options{argv[i]+len});
- }
- }
- }
- for (auto d : dst_types) {
- size_t len = strlen(d.name);
- if (0 == strncmp(d.name, argv[i], len)) {
- switch (argv[i][len]) {
- case ':': len++;
- case '\0': dst_factory = [=]{
- return d.factory(Options{argv[i]+len});
- };
- }
- }
- }
- for (auto v : via_types) {
- size_t len = strlen(v.name);
- if (0 == strncmp(v.name, argv[i], len)) {
- if (!dst_factory) { return help(); }
- switch (argv[i][len]) {
- case ':': len++;
- case '\0': dst_factory = [=]{
- return v.factory(Options{argv[i]+len}, dst_factory());
- };
- }
- }
- }
- }
- if (!stream) { return help(); }
-
- if (!engine) { engine = engine_types.back().factory(Options{}); }
-
- // If we know engine->spawn() will never crash, we can defer logging until we exit.
- if (engine->crashproof()) {
- defer_logging();
- }
-
- int ok = 0, failed = 0, crashed = 0, skipped = 0;
-
- auto update_stats = [&](Status s) {
- switch (s) {
- case Status::OK: ok++; break;
- case Status::Failed: failed++; break;
- case Status::Crashed: crashed++; break;
- case Status::Skipped: skipped++; break;
- case Status::None: return;
- }
- const char* leader = "\r";
- auto print = [&](int count, const char* label) {
- if (count) {
- printf("%s%d %s", leader, count, label);
- leader = ", ";
- }
- };
- print(ok, "ok");
- print(failed, "failed");
- print(crashed, "crashed");
- print(skipped, "skipped");
- fflush(stdout);
- };
-
- std::list<std::future<Status>> live;
- const auto the_past = std::chrono::steady_clock::now();
-
- auto wait_one = [&] {
- if (live.empty()) {
- return Status::None;
- }
-
- for (;;) {
- for (auto it = live.begin(); it != live.end(); it++) {
- if (it->wait_until(the_past) != std::future_status::timeout) {
- Status s = it->get();
- live.erase(it);
- return s;
- }
- }
- }
- };
-
- auto spawn = [&](std::function<Status(void)> fn) {
- std::future<Status> status;
- for (;;) {
- status = engine->spawn(fn);
- if (status.valid()) {
- break;
- }
- update_stats(wait_one());
- }
- live.push_back(std::move(status));
- };
-
- for (std::unique_ptr<Src> owned = stream->next(); owned; owned = stream->next()) {
- Src* raw = owned.release(); // Can't move std::unique_ptr into a lambda in C++11. :(
- spawn([=] {
- std::unique_ptr<Src> src{raw};
-
- std::string name = src->name();
- tls_currently_running = name.c_str();
-
- return dst_factory()->draw(src.get());
- });
- }
-
- for (Status s = Status::OK; s != Status::None; ) {
- s = wait_one();
- update_stats(s);
- }
- printf("\n");
- return (failed || crashed) ? 1 : 0;
-}
-
-
-Register::Register(const char* name, const char* help,
- std::unique_ptr<Engine> (*factory)(Options)) {
- engine_types.push_back(EngineType{name, help, factory});
-}
-Register::Register(const char* name, const char* help,
- std::unique_ptr<Stream> (*factory)(Options)) {
- stream_types.push_back(StreamType{name, help, factory});
-}
-Register::Register(const char* name, const char* help,
- std::unique_ptr<Dst> (*factory)(Options)) {
- dst_types.push_back(DstType{name, help, factory});
-}
-Register::Register(const char* name, const char* help,
- std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>)) {
- via_types.push_back(ViaType{name, help, factory});
-}
-
-Options::Options(std::string str) {
- std::string k,v, *curr = &k;
- for (auto c : str) {
- switch(c) {
- case ',': (*this)[k] = v;
- curr = &(k = "");
- break;
- case '=': curr = &(v = "");
- break;
- default: *curr += c;
- }
- }
- (*this)[k] = v;
-}
-
-std::string& Options::operator[](std::string k) { return this->kv[k]; }
-
-std::string Options::operator()(std::string k, std::string fallback) const {
- for (auto it = kv.find(k); it != kv.end(); ) {
- return it->second;
- }
- return fallback;
-}