blob: ce296b9bb515c23e42c341295a6aa9c36640d8af [file] [log] [blame]
//===- not.cpp - The 'not' testing tool -----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Usage:
// not cmd
// Will return true if cmd doesn't crash and returns false.
// not --crash cmd
// Will return true if cmd crashes (e.g. for testing crash reporting).
// This file is a stripped down version of not.cpp from llvm/utils. This does
// not depend on any LLVM library.
#include <cstdlib>
#include <iostream>
#include <sstream>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#endif
#ifdef __APPLE__
#include <spawn.h>
#include <sys/wait.h>
#include <TargetConditionals.h>
#endif
int main(int argc, char* const* argv) {
bool expectCrash = false;
++argv;
--argc;
if (argc > 0 && std::string(argv[0]) == "--crash") {
++argv;
--argc;
expectCrash = true;
// Crash is expected, so disable crash report and symbolization to reduce
// output and avoid potentially slow symbolization.
#ifdef _WIN32
SetEnvironmentVariableA("LLVM_DISABLE_CRASH_REPORT", "1");
SetEnvironmentVariableA("LLVM_DISABLE_SYMBOLIZATION", "1");
#else
setenv("LLVM_DISABLE_CRASH_REPORT", "1", 0);
setenv("LLVM_DISABLE_SYMBOLIZATION", "1", 0);
#endif
}
if (argc == 0)
return 1;
int result;
#if !defined(TARGET_OS_IPHONE)
std::stringstream ss;
ss << argv[0];
for (int i = 1; i < argc; ++i)
ss << " " << argv[i];
std::string cmd = ss.str();
result = std::system(cmd.c_str());
#else
pid_t pid;
if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL))
return EXIT_FAILURE;
if (waitpid(pid, &result, WUNTRACED | WCONTINUED) == -1)
return EXIT_FAILURE;
#endif
int retcode = 0;
int signal = 0;
#ifdef _WIN32
// Handle abort() in msvcrt -- It has exit code as 3. abort(), aka
// unreachable, should be recognized as a crash. However, some binaries use
// exit code 3 on non-crash failure paths, so only do this if we expect a
// crash.
if (expectCrash && result == 3) {
retcode = 3;
signal = 1;
} else if (errno) {
// If the command interpreter was not found, errno will be set and 0 will
// be returned. It is unlikely that this will happen in our use case, but
// check anyway.
retcode = 1;
signal = 1;
} else {
// On Windows, result is the exit code, except for the special case above.
retcode = result;
signal = 0;
}
#elif defined(WEXITSTATUS) && defined(WTERMSIG)
// On POSIX systems and Solaris, result is a composite value of the exit code
// and, potentially, the signal that caused termination of the command.
retcode = WEXITSTATUS(result);
signal = WTERMSIG(result);
#else
#error "Unsupported system"
#endif
// If signal is non-zero, the command caused a crash, usually SIGABRT.
if (signal) {
if (expectCrash)
return EXIT_SUCCESS;
return EXIT_FAILURE;
}
// The command exited normally. If the command was expected to crash, return
// EXIT_FAILURE since EXIT_SUCCESS is returned in the event of an expected
// crash. Otherwise, invert the return code.
if (expectCrash)
return EXIT_FAILURE;
else if (retcode == EXIT_SUCCESS)
return EXIT_FAILURE;
else
return EXIT_SUCCESS;
}