1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <fbl/unique_ptr.h>
6#include <fbl/vector.h>
7#include <lib/zx/resource.h>
8#include <zircon/device/sysinfo.h>
9#include <zircon/status.h>
10#include <zircon/syscalls.h>
11
12#include <assert.h>
13#include <errno.h>
14#include <fcntl.h>
15#include <getopt.h>
16#include <inttypes.h>
17#include <limits.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <threads.h>
22#include <unistd.h>
23
24#include "stress_test.h"
25
26namespace {
27
28zx_status_t get_root_resource(zx::resource* root_resource) {
29    int fd = open("/dev/misc/sysinfo", O_RDWR);
30    if (fd < 0) {
31        fprintf(stderr, "ERROR: Cannot open sysinfo: %s (%d)\n",
32                strerror(errno), errno);
33        return ZX_ERR_NOT_FOUND;
34    }
35
36    zx_handle_t h;
37    ssize_t n = ioctl_sysinfo_get_root_resource(fd, &h);
38    close(fd);
39
40    if (n != sizeof(*root_resource)) {
41        if (n < 0) {
42            fprintf(stderr, "ERROR: Cannot obtain root resource: %s (%zd)\n",
43                    zx_status_get_string((zx_status_t)n), n);
44            return (zx_status_t)n;
45        } else {
46            fprintf(stderr, "ERROR: Cannot obtain root resource (%zd != %zd)\n",
47                    n, sizeof(root_resource));
48            return ZX_ERR_NOT_FOUND;
49        }
50    }
51
52    root_resource->reset(h);
53
54    return ZX_OK;
55}
56
57zx_status_t get_kmem_stats(zx_info_kmem_stats_t* kmem_stats) {
58    zx::resource root_resource;
59    zx_status_t ret = get_root_resource(&root_resource);
60    if (ret != ZX_OK) {
61        return ret;
62    }
63
64    zx_status_t err = zx_object_get_info(
65        root_resource.get(), ZX_INFO_KMEM_STATS, kmem_stats, sizeof(*kmem_stats), nullptr, nullptr);
66    if (err != ZX_OK) {
67        fprintf(stderr, "ZX_INFO_KMEM_STATS returns %d (%s)\n",
68                err, zx_status_get_string(err));
69        return err;
70    }
71
72    return ZX_OK;
73}
74
75void print_help(char** argv, FILE* f) {
76    fprintf(f, "Usage: %s [options]\n", argv[0]);
77    fprintf(f, "options:\n");
78    fprintf(f, "\t-h:                   This help\n");
79    fprintf(f, "\t-t [time in seconds]: stop all tests after the time has elapsed\n");
80    fprintf(f, "\t-v:                   verbose, status output\n");
81}
82
83} // namespace
84
85int main(int argc, char** argv) {
86    zx_status_t status;
87
88    bool verbose = false;
89    zx::duration run_duration = zx::duration::infinite();
90
91    int c;
92    while ((c = getopt(argc, argv, "ht:v")) > 0) {
93        switch (c) {
94        case 'h':
95            print_help(argv, stdout);
96            return 0;
97        case 't': {
98            long t = atol(optarg);
99            if (t <= 0) {
100                fprintf(stderr, "bad time argument\n");
101                print_help(argv, stderr);
102                return 1;
103            }
104            run_duration = zx::sec(t);
105            break;
106        }
107        case 'v':
108            verbose = true;
109            break;
110        default:
111            fprintf(stderr, "Unknown option\n");
112            print_help(argv, stderr);
113            return 1;
114        }
115    }
116
117    // read some system stats for each test to use
118    zx_info_kmem_stats_t kmem_stats;
119    status = get_kmem_stats(&kmem_stats);
120    if (status != ZX_OK) {
121        fprintf(stderr, "error reading kmem stats\n");
122        return 1;
123    }
124
125    if (run_duration != zx::duration::infinite()) {
126        printf("Running stress tests for %" PRIu64 " seconds\n", run_duration.to_secs());
127    } else {
128        printf("Running stress tests continually\n");
129    }
130
131    // initialize all the tests
132    for (auto& test : StressTest::tests()) {
133        printf("Initializing %s test\n", test->name());
134        status = test->Init(verbose, kmem_stats);
135        if (status != ZX_OK) {
136            fprintf(stderr, "error initializing test\n");
137            return 1;
138        }
139    }
140
141    // start all of them
142    for (auto& test : StressTest::tests()) {
143        printf("Starting %s test\n", test->name());
144        status = test->Start();
145        if (status != ZX_OK) {
146            fprintf(stderr, "error initializing test\n");
147            return 1;
148        }
149    }
150
151    // set stdin to non blocking
152    fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
153
154    zx::time start_time = zx::clock::get_monotonic();
155    bool stop = false;
156    for (;;) {
157        // look for ctrl-c for terminals that do not support it
158        char c;
159        while (read(STDIN_FILENO, &c, 1) > 0) {
160            if (c == 0x3) {
161                stop = true;
162                break;
163            }
164        }
165        if (stop) {
166            break;
167        }
168
169        // wait for a second to try again
170        zx::nanosleep(zx::deadline_after(zx::sec(1)));
171
172        if (run_duration != zx::duration::infinite()) {
173            zx::time now = zx::clock::get_monotonic();
174            if (now - start_time >= run_duration) {
175                break;
176            }
177        }
178    }
179
180    // shut them down
181    for (auto& test : StressTest::tests()) {
182        printf("Stopping %s test\n", test->name());
183        status = test->Stop();
184        if (status != ZX_OK) {
185            fprintf(stderr, "error stopping test\n");
186            return 1;
187        }
188    }
189
190    return 0;
191}
192