crashpad/util/fuchsia/koid_utilities.cc
Scott Graham f5d5a41317 fuchsia: Add implementation of ScopedTaskSuspend
This implementation has some limitations as documented in the header,
however, threads must be suspended in order to use the register capture
debug API so this is somewhat useful for now in the context of
generate_dump.

Also, refactor some child-object retrieval helpers used in a few places.

Bug: crashpad:196
Change-Id: I1fdae5fc3d4b43841e535724eac10c1e58af04c5
Reviewed-on: https://chromium-review.googlesource.com/1007966
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
2018-04-13 16:46:40 +00:00

153 lines
4.8 KiB
C++

// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/fuchsia/koid_utilities.h"
#include <zircon/device/sysinfo.h>
#include <vector>
#include "base/files/file_path.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "util/file/file_io.h"
namespace crashpad {
namespace {
base::ScopedZxHandle GetRootJob() {
ScopedFileHandle sysinfo(
LoggingOpenFileForRead(base::FilePath("/dev/misc/sysinfo")));
if (!sysinfo.is_valid())
return base::ScopedZxHandle();
zx_handle_t root_job;
size_t n = ioctl_sysinfo_get_root_job(sysinfo.get(), &root_job);
if (n != sizeof(root_job)) {
LOG(ERROR) << "unexpected root job size";
return base::ScopedZxHandle();
}
return base::ScopedZxHandle(root_job);
}
bool FindProcess(const base::ScopedZxHandle& job,
zx_koid_t koid,
base::ScopedZxHandle* out) {
for (auto& proc : GetChildHandles(job.get(), ZX_INFO_JOB_PROCESSES)) {
if (GetKoidForHandle(proc.get()) == koid) {
*out = std::move(proc);
return true;
}
}
// TODO(scottmg): As this is recursing down the job tree all the handles are
// kept open, so this could be very expensive in terms of number of open
// handles. This function should be replaced by a syscall in the
// not-too-distant future, so hopefully OK for now.
for (const auto& child_job :
GetChildHandles(job.get(), ZX_INFO_JOB_CHILDREN)) {
if (FindProcess(child_job, koid, out))
return true;
}
return false;
}
} // namespace
std::vector<zx_koid_t> GetChildKoids(zx_handle_t parent,
zx_object_info_topic_t child_kind) {
size_t actual = 0;
size_t available = 0;
std::vector<zx_koid_t> result(100);
// This is inherently racy. Better if the process is suspended, but there's
// still no guarantee that a thread isn't externally created. As a result,
// must be in a retry loop.
for (;;) {
zx_status_t status = zx_object_get_info(parent,
child_kind,
result.data(),
result.size() * sizeof(zx_koid_t),
&actual,
&available);
// If the buffer is too small (even zero), the result is still ZX_OK, not
// ZX_ERR_BUFFER_TOO_SMALL.
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_object_get_info";
break;
}
if (actual == available) {
break;
}
// Resize to the expected number next time, with a bit of slop to handle the
// race between here and the next request.
result.resize(available + 10);
}
result.resize(actual);
return result;
}
std::vector<base::ScopedZxHandle> GetChildHandles(zx_handle_t parent,
zx_object_info_topic_t type) {
auto koids = GetChildKoids(parent, type);
return GetHandlesForChildKoids(parent, koids);
}
std::vector<base::ScopedZxHandle> GetHandlesForChildKoids(
zx_handle_t parent,
const std::vector<zx_koid_t>& koids) {
std::vector<base::ScopedZxHandle> result;
result.reserve(koids.size());
for (zx_koid_t koid : koids) {
zx_handle_t handle;
if (zx_object_get_child(parent, koid, ZX_RIGHT_SAME_RIGHTS, &handle) ==
ZX_OK) {
result.emplace_back(base::ScopedZxHandle(handle));
} else {
result.push_back(base::ScopedZxHandle());
}
}
return result;
}
zx_koid_t GetKoidForHandle(zx_handle_t object) {
zx_info_handle_basic_t info;
zx_status_t status = zx_object_get_info(
object, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_object_get_info";
return ZX_HANDLE_INVALID;
}
return info.koid;
}
// TODO(scottmg): This implementation uses some debug/temporary/hacky APIs and
// ioctls that are currently the only way to go from pid to handle. This should
// hopefully eventually be replaced by more or less a single
// zx_debug_something() syscall.
base::ScopedZxHandle GetProcessFromKoid(zx_koid_t koid) {
base::ScopedZxHandle result;
if (!FindProcess(GetRootJob(), koid, &result)) {
LOG(ERROR) << "process " << koid << " not found";
}
return result;
}
} // namespace crashpad