17be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes/*
27be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * Copyright (C) 2012 The Android Open Source Project
37be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes *
47be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * Licensed under the Apache License, Version 2.0 (the "License");
57be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * you may not use this file except in compliance with the License.
67be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * You may obtain a copy of the License at
77be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes *
87be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes *      http://www.apache.org/licenses/LICENSE-2.0
97be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes *
107be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * Unless required by applicable law or agreed to in writing, software
117be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * distributed under the License is distributed on an "AS IS" BASIS,
127be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * See the License for the specific language governing permissions and
147be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * limitations under the License.
157be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes */
167be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
177be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes#include "benchmark.h"
187be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
197be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes#include <regex.h>
207be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes#include <stdio.h>
217be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes#include <stdlib.h>
227be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
237be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes#include <string>
247be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes#include <map>
257be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
26282e232e2a32cca8a288e81edddfd95f450cfc79Serban Constantinescu#include <inttypes.h>
27282e232e2a32cca8a288e81edddfd95f450cfc79Serban Constantinescu
281728b2396591853345507a063ed6075dfd251706Elliott Hughesstatic int64_t g_bytes_processed;
291728b2396591853345507a063ed6075dfd251706Elliott Hughesstatic int64_t g_benchmark_total_time_ns;
301728b2396591853345507a063ed6075dfd251706Elliott Hughesstatic int64_t g_benchmark_start_time_ns;
317be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
327be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughestypedef std::map<std::string, ::testing::Benchmark*> BenchmarkMap;
337be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughestypedef BenchmarkMap::iterator BenchmarkMapIt;
341728b2396591853345507a063ed6075dfd251706Elliott Hughesstatic BenchmarkMap g_benchmarks;
355ab51d0d1af455afb22621dc3b75079d2b274929Elliott Hughesstatic int g_name_column_width = 20;
367be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
377be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesstatic int Round(int n) {
387be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  int base = 1;
397be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  while (base*10 < n) {
407be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    base *= 10;
417be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
427be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  if (n < 2*base) {
437be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    return 2*base;
447be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
457be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  if (n < 5*base) {
467be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    return 5*base;
477be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
487be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  return 10*base;
497be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}
507be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
517be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesstatic int64_t NanoTime() {
527be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  struct timespec t;
537be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  t.tv_sec = t.tv_nsec = 0;
547be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  clock_gettime(CLOCK_MONOTONIC, &t);
557be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  return static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
567be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}
577be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
587be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesnamespace testing {
597be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
607be369d4c60e9df2316fdb6c73181a40020abef2Elliott HughesBenchmark* Benchmark::Arg(int arg) {
617be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  args_.push_back(arg);
627be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  return this;
637be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}
647be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
659edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughesconst char* Benchmark::Name() {
669edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes  return name_;
679edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes}
689edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes
697be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesbool Benchmark::ShouldRun(int argc, char* argv[]) {
707be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  if (argc == 1) {
717be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    return true;  // With no arguments, we run all benchmarks.
727be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
737be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  // Otherwise, we interpret each argument as a regular expression and
747be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  // see if any of our benchmarks match.
757be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  for (int i = 1; i < argc; i++) {
767be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    regex_t re;
777be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    if (regcomp(&re, argv[i], 0) != 0) {
787be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes      fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]);
797be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes      exit(EXIT_FAILURE);
807be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    }
817be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    int match = regexec(&re, name_, 0, NULL, 0);
827be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    regfree(&re);
837be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    if (match != REG_NOMATCH) {
847be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes      return true;
857be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    }
867be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
877be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  return false;
887be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}
897be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
907be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid Benchmark::Register(const char* name, void (*fn)(int), void (*fn_range)(int, int)) {
917be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  name_ = name;
927be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  fn_ = fn;
937be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  fn_range_ = fn_range;
947be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
957be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  if (fn_ == NULL && fn_range_ == NULL) {
967be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    fprintf(stderr, "%s: missing function\n", name_);
977be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    exit(EXIT_FAILURE);
987be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
997be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
1001728b2396591853345507a063ed6075dfd251706Elliott Hughes  g_benchmarks.insert(std::make_pair(name, this));
1017be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}
1027be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
1037be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid Benchmark::Run() {
1049edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes  if (fn_ != NULL) {
1059edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes    RunWithArg(0);
1069edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes  } else {
1079edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes    if (args_.empty()) {
1089edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes      fprintf(stderr, "%s: no args!\n", name_);
1099edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes      exit(EXIT_FAILURE);
1109edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes    }
1119edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes    for (size_t i = 0; i < args_.size(); ++i) {
1129edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes      RunWithArg(args_[i]);
1139edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes    }
1147be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
1157be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}
1167be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
1177be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid Benchmark::RunRepeatedlyWithArg(int iterations, int arg) {
1181728b2396591853345507a063ed6075dfd251706Elliott Hughes  g_bytes_processed = 0;
1191728b2396591853345507a063ed6075dfd251706Elliott Hughes  g_benchmark_total_time_ns = 0;
1201728b2396591853345507a063ed6075dfd251706Elliott Hughes  g_benchmark_start_time_ns = NanoTime();
1217be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  if (fn_ != NULL) {
1227be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    fn_(iterations);
1237be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  } else {
1247be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    fn_range_(iterations, arg);
1257be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
1261728b2396591853345507a063ed6075dfd251706Elliott Hughes  if (g_benchmark_start_time_ns != 0) {
1271728b2396591853345507a063ed6075dfd251706Elliott Hughes    g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns;
1287be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
1297be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}
1307be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
1317be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid Benchmark::RunWithArg(int arg) {
1327be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  // run once in case it's expensive
1337be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  int iterations = 1;
1347be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  RunRepeatedlyWithArg(iterations, arg);
1351728b2396591853345507a063ed6075dfd251706Elliott Hughes  while (g_benchmark_total_time_ns < 1e9 && iterations < 1e9) {
1367be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    int last = iterations;
1371728b2396591853345507a063ed6075dfd251706Elliott Hughes    if (g_benchmark_total_time_ns/iterations == 0) {
1387be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes      iterations = 1e9;
1397be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    } else {
1401728b2396591853345507a063ed6075dfd251706Elliott Hughes      iterations = 1e9 / (g_benchmark_total_time_ns/iterations);
1417be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    }
1427be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last));
1437be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    iterations = Round(iterations);
1447be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    RunRepeatedlyWithArg(iterations, arg);
1457be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
1467be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
1477be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  char throughput[100];
1487be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  throughput[0] = '\0';
1491728b2396591853345507a063ed6075dfd251706Elliott Hughes  if (g_benchmark_total_time_ns > 0 && g_bytes_processed > 0) {
1501728b2396591853345507a063ed6075dfd251706Elliott Hughes    double mib_processed = static_cast<double>(g_bytes_processed)/1e6;
1511728b2396591853345507a063ed6075dfd251706Elliott Hughes    double seconds = static_cast<double>(g_benchmark_total_time_ns)/1e9;
1527be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    snprintf(throughput, sizeof(throughput), " %8.2f MiB/s", mib_processed/seconds);
1537be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
1547be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
1557be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  char full_name[100];
1567be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  if (fn_range_ != NULL) {
1577be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    if (arg >= (1<<20)) {
1587be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes      snprintf(full_name, sizeof(full_name), "%s/%dM", name_, arg/(1<<20));
1597be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    } else if (arg >= (1<<10)) {
1607be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes      snprintf(full_name, sizeof(full_name), "%s/%dK", name_, arg/(1<<10));
1617be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    } else {
1627be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes      snprintf(full_name, sizeof(full_name), "%s/%d", name_, arg);
1637be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    }
1647be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  } else {
1657be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    snprintf(full_name, sizeof(full_name), "%s", name_);
1667be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
1677be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
168c0eed72cbfe29d7d5f7daea9d019982465c566f0Elliott Hughes  printf("%-*s %10d %10" PRId64 "%s\n", g_name_column_width, full_name,
1691728b2396591853345507a063ed6075dfd251706Elliott Hughes         iterations, g_benchmark_total_time_ns/iterations, throughput);
1707be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  fflush(stdout);
1717be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}
1727be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
1737be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}  // namespace testing
1747be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
1757be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid SetBenchmarkBytesProcessed(int64_t x) {
1761728b2396591853345507a063ed6075dfd251706Elliott Hughes  g_bytes_processed = x;
1777be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}
1787be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
1797be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid StopBenchmarkTiming() {
1801728b2396591853345507a063ed6075dfd251706Elliott Hughes  if (g_benchmark_start_time_ns != 0) {
1811728b2396591853345507a063ed6075dfd251706Elliott Hughes    g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns;
1827be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
1831728b2396591853345507a063ed6075dfd251706Elliott Hughes  g_benchmark_start_time_ns = 0;
1847be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}
1857be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
1867be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid StartBenchmarkTiming() {
1871728b2396591853345507a063ed6075dfd251706Elliott Hughes  if (g_benchmark_start_time_ns == 0) {
1881728b2396591853345507a063ed6075dfd251706Elliott Hughes    g_benchmark_start_time_ns = NanoTime();
1897be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
1907be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}
1917be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
1927be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesint main(int argc, char* argv[]) {
1931728b2396591853345507a063ed6075dfd251706Elliott Hughes  if (g_benchmarks.empty()) {
1949edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes    fprintf(stderr, "No benchmarks registered!\n");
1957be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    exit(EXIT_FAILURE);
1967be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
1977be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes
198c0eed72cbfe29d7d5f7daea9d019982465c566f0Elliott Hughes  for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) {
1995ab51d0d1af455afb22621dc3b75079d2b274929Elliott Hughes    int name_width = static_cast<int>(strlen(it->second->Name()));
2005ab51d0d1af455afb22621dc3b75079d2b274929Elliott Hughes    g_name_column_width = std::max(g_name_column_width, name_width);
201c0eed72cbfe29d7d5f7daea9d019982465c566f0Elliott Hughes  }
202c0eed72cbfe29d7d5f7daea9d019982465c566f0Elliott Hughes
2039edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes  bool need_header = true;
2041728b2396591853345507a063ed6075dfd251706Elliott Hughes  for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) {
2057be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    ::testing::Benchmark* b = it->second;
2067be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    if (b->ShouldRun(argc, argv)) {
2079edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes      if (need_header) {
208c0eed72cbfe29d7d5f7daea9d019982465c566f0Elliott Hughes        printf("%-*s %10s %10s\n", g_name_column_width, "", "iterations", "ns/op");
2099edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes        fflush(stdout);
2109edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes        need_header = false;
2119edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes      }
2127be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes      b->Run();
2137be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes    }
2147be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  }
2159edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes
2169edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes  if (need_header) {
2179edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes    fprintf(stderr, "No matching benchmarks!\n");
2189edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes    fprintf(stderr, "Available benchmarks:\n");
2191728b2396591853345507a063ed6075dfd251706Elliott Hughes    for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) {
2209edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes      fprintf(stderr, "  %s\n", it->second->Name());
2219edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes    }
2229edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes    exit(EXIT_FAILURE);
2239edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes  }
2249edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes
2257be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes  return 0;
2267be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes}
227