Skip to content

Commit

Permalink
#Centipede MacOS support 5/X: fix shared_memory_blob_sequence.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 674087419
  • Loading branch information
xinhaoyuan authored and copybara-github committed Sep 30, 2024
1 parent c6b9071 commit 62c308a
Show file tree
Hide file tree
Showing 16 changed files with 307 additions and 85 deletions.
23 changes: 18 additions & 5 deletions .github/workflows/bazel_test_centipede.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,28 @@ jobs:
if: ${{ !cancelled() }}
run: |
bazel test --local_test_jobs=1 --test_output=errors --no//fuzztest:use_riegeli \
centipede:callstack_test centipede:concurrent_bitset_test \
centipede:binary_info_test centipede:byte_array_mutator_test \
centipede:call_graph_test centipede:callstack_test
centipede:centipede_binary_test centipede:concurrent_bitset_test \
centipede:command_test \
centipede:concurrent_byteset_test centipede:config_file_test \
centipede:config_util_test centipede:environment_test \
centipede:feature_test centipede:foreach_nonzero_test \
centipede:config_util_test centipede:corpus_io_test \
centipede:corpus_test centipede:distill_test \
centipede:environment_test centipede:execution_metadata_test \
centipede:feature_set_test centipede:feature_test \
centipede:foreach_nonzero_test \
centipede:hashed_ring_buffer_test centipede:int_utils_test \
centipede:knobs_test centipede:pc_info_test \
centipede:knobs_test centipede:mutation_input_test \
centipede:pc_info_test centipede:resource_pool_test \
centipede:reverse_pc_table_test centipede:rolling_hash_test \
centipede:runner_cmp_trace_test centipede:runner_flags_test \
centipede:util_test centipede:workdir_test
centipede:runner_result_test centipede:rusage_stats_test \
centipede:seed_corpus_maker_lib_test \
centipede:seed_corpus_maker_proto_lib_test \
centipede:shared_memory_blob_sequence_test \
centipede:stats_test centipede:symbol_table_test \
centipede:util_test centipede:workdir_test && \
bazel test --local_test_jobs=1 --test_output=errors --no//fuzztest:use_riegeli --test_filter='-*ValidateTimelapseSnapshots' -- centipede:rusage_profiler_test
- name: Save new cache based on main
if: github.ref == 'refs/heads/main'
uses: actions/cache/save@v4
Expand Down
6 changes: 6 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ bazel_dep(
version = "1.7.1",
)

bazel_dep(
name = "platforms",
version = "0.0.10",
repo_name = "platforms",
)

# Dev dependencies.
bazel_dep(
name = "googletest",
Expand Down
9 changes: 9 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ http_archive(
url = "https://github.com/abseil/abseil-cpp/releases/download/20240116.0/abseil-cpp-20240116.0.tar.gz"
)

http_archive(
name = "platforms",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz",
"https://github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz",
],
sha256 = "218efe8ee736d26a3572663b374a253c012b716d8af0c07e842e82f238a0a7ee",
)

################################################################################
# Transitive dependencies for core FuzzTest
################################################################################
Expand Down
21 changes: 18 additions & 3 deletions centipede/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,12 @@ cc_library(
name = "stats",
srcs = ["stats.cc"],
hdrs = ["stats.h"],
linkopts = ["-latomic"], # for std::atomic::load()/store().
linkopts = select({
"@platforms//os:macos": [],
"//conditions:default": [
"-latomic", # for std::atomic::load()/store().
],
}),
deps = [
":environment",
":workdir",
Expand Down Expand Up @@ -420,7 +425,12 @@ cc_library(
name = "shared_memory_blob_sequence",
srcs = ["shared_memory_blob_sequence.cc"],
hdrs = ["shared_memory_blob_sequence.h"],
linkopts = ["-lrt"], # for shm_open.
linkopts = select({
"@platforms//os:macos": [],
"//conditions:default": [
"-lrt", # for shm_open
],
}),
visibility = PUBLIC_API_VISIBILITY,
deps = ["@com_google_absl//absl/base:nullability"],
# don't add any dependencies.
Expand Down Expand Up @@ -704,7 +714,12 @@ cc_library(
name = "early_exit",
srcs = ["early_exit.cc"],
hdrs = ["early_exit.h"],
linkopts = ["-latomic"], # for std::atomic::load()/store().
linkopts = select({
"@platforms//os:macos": [],
"//conditions:default": [
"-latomic", # for std::atomic::load()/store().
],
}),
visibility = PUBLIC_API_VISIBILITY,
)

Expand Down
21 changes: 16 additions & 5 deletions centipede/command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef __APPLE__
#include <inttypes.h>
#include <libproc.h>
#endif // __APPLE__

#include <algorithm>
#include <csignal>
Expand Down Expand Up @@ -59,6 +63,16 @@ constexpr std::string_view kCommandLineSeparator(" \\\n");
constexpr std::string_view kNoForkServerRequestPrefix("%f");

absl::StatusOr<std::string> GetProcessCreationStamp(pid_t pid) {
#ifdef __APPLE__
struct proc_bsdinfo info = {};
if (proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &info, PROC_PIDTBSDINFO_SIZE) !=
PROC_PIDTBSDINFO_SIZE) {
return absl::InternalError(
absl::StrCat("failed to get proc bsdinfo for ", pid));
}
return absl::StrFormat("%" PRIu64 ".%06" PRIu64, info.pbi_start_tvsec,
info.pbi_start_tvusec);
#else
constexpr int kFieldIndexOfStartTimeAfterComm = 19; // From `man procfs`
const std::string proc_stat_path = absl::StrFormat("/proc/%d/stat", pid);
std::string proc_stat_line;
Expand Down Expand Up @@ -86,6 +100,7 @@ absl::StatusOr<std::string> GetProcessCreationStamp(pid_t pid) {
": ", proc_stat_line));
}
return std::string(fields[kFieldIndexOfStartTimeAfterComm]);
#endif
}

} // namespace
Expand Down Expand Up @@ -211,7 +226,7 @@ bool Command::StartForkServer(std::string_view temp_dir_path,
CENTIPEDE_FORK_SERVER_FIFO1="%s" \
%s
} &
echo -n $! > "%s"
printf "%%s" $! > "%s"
)sh";
const std::string fork_server_command = absl::StrFormat(
kForkServerCommandStub, fork_server_->fifo_path_[0],
Expand Down Expand Up @@ -250,10 +265,6 @@ bool Command::StartForkServer(std::string_view temp_dir_path,
return false;
}

// The fork server has started and the comms pipes got opened successfully.
// Read the fork server's PID and the initial /proc/<PID>/exe symlink pointing
// at the fork server's binary, written to the provided files by `command`.
// `Execute()` uses these to monitor the fork server health.
std::string pid_str;
ReadFromLocalFile(pid_file_path, pid_str);
CHECK(absl::SimpleAtoi(pid_str, &fork_server_->pid_)) << VV(pid_str);
Expand Down
4 changes: 3 additions & 1 deletion centipede/command_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ TEST(CommandTest, ForkServer) {
const std::string log = std::filesystem::path{test_tmpdir} / input;
Command cmd(helper, {input}, {}, log, log);
EXPECT_TRUE(cmd.StartForkServer(test_tmpdir, "ForkServer"));
EXPECT_EQ(WTERMSIG(cmd.Execute()), SIGABRT);
// WTERMSIG() needs an lvalue on some platforms.
const int ret = cmd.Execute();
EXPECT_EQ(WTERMSIG(ret), SIGABRT);
std::string log_contents;
ReadFromLocalFile(log, log_contents);
EXPECT_EQ(log_contents, absl::Substitute("Got input: $0", input));
Expand Down
4 changes: 4 additions & 0 deletions centipede/environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ struct Environment {
std::string minimize_crash_file_path;
bool batch_triage_suspect_only = false;
size_t shmem_size_mb = 1024;
#ifdef __APPLE__
bool use_posix_shmem = true;
#else
bool use_posix_shmem = false;
#endif
bool dry_run = false;
bool save_binary_info = false;
bool populate_binary_info = true;
Expand Down
6 changes: 4 additions & 2 deletions centipede/execution_metadata_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "./centipede/execution_metadata.h"

#include <cstdint>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -96,8 +97,9 @@ TEST(ExecutionMetadata, AppendCmpEntryReturnsFalseAndSkipsOnBadArgs) {
TEST(ExecutionMetadata, ReadAndWriteKeepsCmpEntries) {
ExecutionMetadata metadata_in;
ASSERT_TRUE(metadata_in.AppendCmpEntry({1, 2}, {3, 4}));
SharedMemoryBlobSequence blobseq("test", /*size=*/1024,
/*use_posix_shmem=*/false);
std::vector<uint8_t> blob_storage;
blob_storage.resize(1024);
BlobSequence blobseq(blob_storage.data(), blob_storage.size());
EXPECT_TRUE(metadata_in.Write(/*tag=*/1, blobseq));
blobseq.Reset();
Blob blob = blobseq.Read();
Expand Down
8 changes: 6 additions & 2 deletions centipede/resource_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

#include "./centipede/resource_pool.h"

#include <sstream>
#include <string>
#include <thread> // NOLINT: For thread IDs.
#include <utility>

#include "absl/log/check.h"
Expand Down Expand Up @@ -63,11 +65,13 @@ const absl::Status& ResourcePool<ResourceT>::LeaseToken::status() const {

template <typename ResourceT>
std::string ResourcePool<ResourceT>::LeaseToken::id() const {
return absl::StrCat("lease_tid_", thread_id_, "_rid_", request_.id);
std::stringstream ss;
ss << thread_id_;
return absl::StrCat("lease_tid_", ss.str(), "_rid_", request_.id);
}

template <typename ResourceT>
pid_t ResourcePool<ResourceT>::LeaseToken::thread_id() const {
std::thread::id ResourcePool<ResourceT>::LeaseToken::thread_id() const {
return thread_id_;
}

Expand Down
5 changes: 3 additions & 2 deletions centipede/resource_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include <ostream>
#include <string>
#include <thread> // NOLINT: for thread IDs.

#include "absl/base/thread_annotations.h"
#include "absl/status/status.h"
Expand Down Expand Up @@ -144,7 +145,7 @@ class ResourcePool {
// A short description that can be used in logs.
std::string id() const;
// The thread ID that submitted the request.
pid_t thread_id() const;
std::thread::id thread_id() const;
// The creation time and the age of the lease.
absl::Time created_at() const;
absl::Duration age() const;
Expand All @@ -166,7 +167,7 @@ class ResourcePool {
LeaseRequest request_ = {};
absl::Status status_ = absl::OkStatus();
mutable bool status_checked_ = false;
pid_t thread_id_ = ::syscall(__NR_gettid);
std::thread::id thread_id_ = std::this_thread::get_id();
absl::Time created_at_ = absl::Now();
};

Expand Down
81 changes: 76 additions & 5 deletions centipede/runner_fork_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@
// works too early in the process. E.g. getenv() will not work yet.

#include <fcntl.h>
#ifdef __APPLE__
#include <sys/sysctl.h>
#else // __APPLE__
#include <linux/limits.h> // ARG_MAX
#endif // __APPLE__
#include <sys/wait.h>
#include <unistd.h>

Expand All @@ -64,14 +68,24 @@

namespace centipede {

namespace {

constexpr bool kForkServerDebug = false;
[[maybe_unused]] constexpr bool kForkServerDumpEnvAtStart = false;

} // namespace

// Writes a C string to stderr when debugging, no-op otherwise.
void Log(absl::Nonnull<const char *> str) {
// Uncomment these lines to debug.
// (void)write(STDERR_FILENO, str, strlen(str));
// fsync(STDERR_FILENO);
if constexpr (kForkServerDebug) {
(void)write(STDERR_FILENO, str, strlen(str));
fsync(STDERR_FILENO);
}
}

// Maybe writes the `reason` to stderr; then calls _exit.
// Maybe writes the `reason` to stderr; then calls _exit. We use this instead of
// CHECK/RunnerCheck since the fork server runs at the very early stage of the
// process, where the logging functions used there may not work.
void Exit(absl::Nonnull<const char *> reason) {
Log(reason);
_exit(0); // The exit code does not matter, it won't be checked anyway.
Expand All @@ -82,13 +96,63 @@ void Exit(absl::Nonnull<const char *> reason) {
static char env[ARG_MAX];
static ssize_t env_size;

// Reads /proc/self/environ into env.
void GetAllEnv() {
#ifdef __APPLE__
// Reference:
// https://chromium.googlesource.com/crashpad/crashpad/+/360e441c53ab4191a6fd2472cc57c3343a2f6944/util/posix/process_util_mac.cc
char args[ARG_MAX];
size_t args_size = sizeof(args);
int mib[] = {CTL_KERN, KERN_PROCARGS2, getpid()};
int rv =
sysctl(mib, sizeof(mib) / sizeof(mib[0]), args, &args_size, nullptr, 0);
if (rv != 0) {
Exit("GetEnv: sysctl({CTK_KERN, KERN_PROCARGS2, ...}) failed");
}
if (args_size < sizeof(int)) {
Exit("GetEnv: args_size too small");
}
int argc = 0;
memcpy(&argc, &args[0], sizeof(argc));
size_t start_pos = sizeof(argc);
// Find the end of the executable path.
while (start_pos < args_size && args[start_pos] != 0) ++start_pos;
if (start_pos == args_size) {
Exit("GetEnv: envp not found");
}
// Find the beginning of the string area.
while (start_pos < args_size && args[start_pos] == 0) ++start_pos;
if (start_pos == args_size) {
Exit("GetEnv: envp not found");
}
// Ignore the first argc strings, after which is the envp.
for (int i = 0; i < argc; ++i) {
while (start_pos < args_size && args[start_pos] != 0) ++start_pos;
if (start_pos == args_size) {
Exit("GetEnv: envp not found");
}
++start_pos;
}
const size_t end_pos = args_size;
memcpy(env, &args[start_pos], end_pos - start_pos);
env_size = end_pos - start_pos;
if constexpr (kForkServerDumpEnvAtStart) {
size_t pos = start_pos;
while (pos < args_size) {
const size_t len = strnlen(&args[pos], args_size - pos);
(void)write(STDERR_FILENO, &args[pos], len);
(void)write(STDERR_FILENO, "\n", 1);
pos += len + 1;
}
fsync(STDERR_FILENO);
}
#else // __APPLE__
// Reads /proc/self/environ into env.
int fd = open("/proc/self/environ", O_RDONLY);
if (fd < 0) Exit("GetEnv: can't open /proc/self/environ\n");
env_size = read(fd, env, sizeof(env));
if (env_size < 0) Exit("GetEnv: can't read to env\n");
if (close(fd) != 0) Exit("GetEnv: can't close /proc/self/environ\n");
#endif // __APPLE__
env[sizeof(env) - 1] = 0; // Just in case.
}

Expand Down Expand Up @@ -183,7 +247,14 @@ __attribute__((constructor(150))) void ForkServerCallMeVeryEarly() {
__builtin_unreachable();
}

// If supported, use .preinit_array to call `ForkServerCallMeVeryEarly` even
// earlier than the `constructor` attribute of the declaration. This helps to
// avoid potential conflicts with higher-priority constructors.
#ifdef __APPLE__
// .preinit_array is not supported in MacOS.
#else // __APPLE__
__attribute__((section(".preinit_array"))) auto call_very_early =
ForkServerCallMeVeryEarly;
#endif // __APPLE__

} // namespace centipede
Loading

0 comments on commit 62c308a

Please sign in to comment.