mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:27:45 +00:00
Kernel: Steer away from heap allocations for ProcFS process data
Instead, use more static patterns to acquire that sort of data.
This commit is contained in:
parent
bf1adc2d5d
commit
7ba991dc37
11 changed files with 991 additions and 879 deletions
|
@ -9,601 +9,294 @@
|
|||
#include <AK/JsonValue.h>
|
||||
#include <Kernel/Arch/x86/InterruptDisabler.h>
|
||||
#include <Kernel/FileSystem/Custody.h>
|
||||
#include <Kernel/FileSystem/ProcFS.h>
|
||||
#include <Kernel/KBufferBuilder.h>
|
||||
#include <Kernel/Memory/AnonymousVMObject.h>
|
||||
#include <Kernel/Memory/MemoryManager.h>
|
||||
#include <Kernel/Process.h>
|
||||
#include <Kernel/ProcessExposed.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class ProcFSProcessStacks;
|
||||
class ProcFSThreadStack final : public ProcFSProcessInformation {
|
||||
public:
|
||||
// Note: We pass const ProcFSProcessStacks& to enforce creation with this type of directory
|
||||
static NonnullRefPtr<ProcFSThreadStack> create(const ProcFSProcessDirectory& process_directory, const ProcFSProcessStacks&, const Thread& thread)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSThreadStack(process_directory, thread));
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSThreadStack(const ProcFSProcessDirectory& process_directory, const Thread& thread)
|
||||
: ProcFSProcessInformation(String::formatted("{}", thread.tid()), process_directory)
|
||||
, m_associated_thread(thread)
|
||||
{
|
||||
}
|
||||
virtual bool output(KBufferBuilder& builder) override
|
||||
{
|
||||
JsonArraySerializer array { builder };
|
||||
bool show_kernel_addresses = Process::current()->is_superuser();
|
||||
bool kernel_address_added = false;
|
||||
for (auto address : Processor::capture_stack_trace(*m_associated_thread, 1024)) {
|
||||
if (!show_kernel_addresses && !Memory::is_user_address(VirtualAddress { address })) {
|
||||
if (kernel_address_added)
|
||||
continue;
|
||||
address = 0xdeadc0de;
|
||||
kernel_address_added = true;
|
||||
}
|
||||
array.add(address);
|
||||
}
|
||||
|
||||
array.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
NonnullRefPtr<Thread> m_associated_thread;
|
||||
};
|
||||
|
||||
class ProcFSProcessStacks final : public ProcFSExposedDirectory {
|
||||
// Note: This directory is special, because everything that is created here is dynamic!
|
||||
// This means we don't register anything in the m_components Vector, and every inode
|
||||
// is created in runtime when called to get it
|
||||
// Every ProcFSThreadStack (that represents a thread stack) is created only as a temporary object
|
||||
// therefore, we don't use m_components so when we are done with the ProcFSThreadStack object,
|
||||
// It should be deleted (as soon as possible)
|
||||
public:
|
||||
virtual KResult traverse_as_directory(unsigned, Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
|
||||
virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override;
|
||||
|
||||
static NonnullRefPtr<ProcFSProcessStacks> create(const ProcFSProcessDirectory& parent_directory)
|
||||
{
|
||||
auto directory = adopt_ref(*new (nothrow) ProcFSProcessStacks(parent_directory));
|
||||
return directory;
|
||||
}
|
||||
|
||||
virtual void prepare_for_deletion() override
|
||||
{
|
||||
ProcFSExposedDirectory::prepare_for_deletion();
|
||||
m_process_directory.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
ProcFSProcessStacks(const ProcFSProcessDirectory& parent_directory)
|
||||
: ProcFSExposedDirectory("stacks"sv, parent_directory)
|
||||
, m_process_directory(parent_directory)
|
||||
{
|
||||
}
|
||||
WeakPtr<ProcFSProcessDirectory> m_process_directory;
|
||||
mutable Mutex m_lock;
|
||||
};
|
||||
|
||||
KResult ProcFSProcessStacks::traverse_as_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
|
||||
KResultOr<size_t> Process::procfs_get_thread_stack(ThreadID thread_id, KBufferBuilder& builder) const
|
||||
{
|
||||
MutexLocker locker(m_lock);
|
||||
auto parent_directory = m_process_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return KResult(EINVAL);
|
||||
callback({ ".", { fsid, component_index() }, 0 });
|
||||
callback({ "..", { fsid, parent_directory->component_index() }, 0 });
|
||||
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
JsonArraySerializer array { builder };
|
||||
auto thread = Thread::from_tid(thread_id);
|
||||
if (!thread)
|
||||
return KResult(ESRCH);
|
||||
process->for_each_thread([&](const Thread& thread) {
|
||||
bool show_kernel_addresses = Process::current()->is_superuser();
|
||||
bool kernel_address_added = false;
|
||||
for (auto address : Processor::capture_stack_trace(*thread, 1024)) {
|
||||
if (!show_kernel_addresses && !Memory::is_user_address(VirtualAddress { address })) {
|
||||
if (kernel_address_added)
|
||||
continue;
|
||||
address = 0xdeadc0de;
|
||||
kernel_address_added = true;
|
||||
}
|
||||
array.add(address);
|
||||
}
|
||||
|
||||
array.finish();
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
InodeIndex Process::component_index() const
|
||||
{
|
||||
return SegmentedProcFSIndex::build_segmented_index_for_pid_directory(pid());
|
||||
}
|
||||
|
||||
NonnullRefPtr<Inode> Process::to_inode(const ProcFS& procfs_instance) const
|
||||
{
|
||||
return ProcFSProcessDirectoryInode::create(procfs_instance, m_protected_values.pid);
|
||||
}
|
||||
|
||||
KResult Process::traverse_as_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
|
||||
{
|
||||
callback({ ".", { fsid, SegmentedProcFSIndex::build_segmented_index_for_pid_directory(pid()) }, 0 });
|
||||
callback({ "..", { fsid, ProcFSComponentRegistry::the().root_directory().component_index() }, 0 });
|
||||
callback({ "fd", { fsid, SegmentedProcFSIndex::build_segmented_index_for_sub_directory(pid(), SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions) }, 0 });
|
||||
callback({ "stacks", { fsid, SegmentedProcFSIndex::build_segmented_index_for_sub_directory(pid(), SegmentedProcFSIndex::ProcessSubDirectory::Stacks) }, 0 });
|
||||
callback({ "unveil", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::Unveil) }, 0 });
|
||||
callback({ "pledge", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::Pledge) }, 0 });
|
||||
callback({ "fds", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::FileDescriptions) }, 0 });
|
||||
callback({ "exe", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::BinaryLink) }, 0 });
|
||||
callback({ "cwd", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::CurrentWorkDirectoryLink) }, 0 });
|
||||
callback({ "perf_events", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::PerformanceEvents) }, 0 });
|
||||
callback({ "vm", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::VirtualMemoryStats) }, 0 });
|
||||
callback({ "root", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::RootLink) }, 0 });
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
KResult Process::traverse_stacks_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
|
||||
{
|
||||
callback({ ".", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property(pid(), SegmentedProcFSIndex::ProcessSubDirectory::Stacks, SegmentedProcFSIndex::MainProcessProperty::Reserved) }, 0 });
|
||||
callback({ "..", { fsid, component_index() }, 0 });
|
||||
|
||||
for_each_thread([&](const Thread& thread) {
|
||||
int tid = thread.tid().value();
|
||||
InodeIdentifier identifier = { fsid, thread.global_procfs_inode_index() };
|
||||
InodeIdentifier identifier = { fsid, SegmentedProcFSIndex::build_segmented_index_for_thread_stack(pid(), thread.tid()) };
|
||||
callback({ String::number(tid), identifier, 0 });
|
||||
});
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
RefPtr<ProcFSExposedComponent> ProcFSProcessStacks::lookup(StringView name)
|
||||
RefPtr<Inode> Process::lookup_stacks_directory(const ProcFS& procfs, StringView name) const
|
||||
{
|
||||
MutexLocker locker(m_lock);
|
||||
auto parent_directory = m_process_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return nullptr;
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return nullptr;
|
||||
RefPtr<ProcFSThreadStack> procfd_stack;
|
||||
RefPtr<ProcFSProcessPropertyInode> thread_stack_inode;
|
||||
// FIXME: Try to exit the loop earlier
|
||||
process->for_each_thread([&](const Thread& thread) {
|
||||
for_each_thread([&](const Thread& thread) {
|
||||
int tid = thread.tid().value();
|
||||
VERIFY(!(tid < 0));
|
||||
if (name == String::number(tid)) {
|
||||
procfd_stack = ProcFSThreadStack::create(*parent_directory, *this, thread);
|
||||
thread_stack_inode = ProcFSProcessPropertyInode::create_for_thread_stack(procfs, thread.tid(), pid());
|
||||
}
|
||||
});
|
||||
return procfd_stack;
|
||||
return thread_stack_inode;
|
||||
}
|
||||
|
||||
class ProcFSProcessFileDescriptions;
|
||||
class ProcFSProcessFileDescription final : public ProcFSExposedLink {
|
||||
public:
|
||||
// Note: we pass const ProcFSProcessFileDescriptions& just to enforce creation of this in the correct directory.
|
||||
static NonnullRefPtr<ProcFSProcessFileDescription> create(unsigned fd_number, const FileDescription& fd, InodeIndex preallocated_index, const ProcFSProcessFileDescriptions&)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSProcessFileDescription(fd_number, fd, preallocated_index));
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSProcessFileDescription(unsigned fd_number, const FileDescription& fd, InodeIndex preallocated_index)
|
||||
: ProcFSExposedLink(String::formatted("{}", fd_number), preallocated_index)
|
||||
, m_associated_file_description(fd)
|
||||
{
|
||||
}
|
||||
virtual bool acquire_link(KBufferBuilder& builder) override
|
||||
{
|
||||
builder.append_bytes(m_associated_file_description->absolute_path().bytes());
|
||||
return true;
|
||||
}
|
||||
|
||||
NonnullRefPtr<FileDescription> m_associated_file_description;
|
||||
};
|
||||
|
||||
class ProcFSProcessFileDescriptions final : public ProcFSExposedDirectory {
|
||||
// Note: This directory is special, because everything that is created here is dynamic!
|
||||
// This means we don't register anything in the m_components Vector, and every inode
|
||||
// is created in runtime when called to get it
|
||||
// Every ProcFSProcessFileDescription (that represents a file descriptor) is created only as a temporary object
|
||||
// therefore, we don't use m_components so when we are done with the ProcFSProcessFileDescription object,
|
||||
// It should be deleted (as soon as possible)
|
||||
public:
|
||||
virtual KResult traverse_as_directory(unsigned, Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
|
||||
virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override;
|
||||
|
||||
static NonnullRefPtr<ProcFSProcessFileDescriptions> create(const ProcFSProcessDirectory& parent_directory)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSProcessFileDescriptions(parent_directory));
|
||||
}
|
||||
|
||||
virtual void prepare_for_deletion() override
|
||||
{
|
||||
ProcFSExposedDirectory::prepare_for_deletion();
|
||||
m_process_directory.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSProcessFileDescriptions(const ProcFSProcessDirectory& parent_directory)
|
||||
: ProcFSExposedDirectory("fd"sv, parent_directory)
|
||||
, m_process_directory(parent_directory)
|
||||
{
|
||||
}
|
||||
WeakPtr<ProcFSProcessDirectory> m_process_directory;
|
||||
mutable Mutex m_lock;
|
||||
};
|
||||
|
||||
KResult ProcFSProcessFileDescriptions::traverse_as_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
|
||||
KResultOr<size_t> Process::procfs_get_file_description_link(unsigned fd, KBufferBuilder& builder) const
|
||||
{
|
||||
MutexLocker locker(m_lock);
|
||||
auto parent_directory = m_process_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return KResult(EINVAL);
|
||||
callback({ ".", { fsid, component_index() }, 0 });
|
||||
callback({ "..", { fsid, parent_directory->component_index() }, 0 });
|
||||
auto file_description = m_fds.file_description(fd);
|
||||
if (!file_description)
|
||||
return EBADF;
|
||||
auto data = file_description->absolute_path();
|
||||
builder.append(data);
|
||||
return data.length();
|
||||
}
|
||||
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return KResult(ESRCH);
|
||||
KResult Process::traverse_file_descriptions_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
|
||||
{
|
||||
callback({ ".", { fsid, component_index() }, 0 });
|
||||
callback({ "..", { fsid, component_index() }, 0 });
|
||||
size_t count = 0;
|
||||
process->fds().enumerate([&](auto& file_description_metadata) {
|
||||
fds().enumerate([&](auto& file_description_metadata) {
|
||||
if (!file_description_metadata.is_valid()) {
|
||||
count++;
|
||||
return;
|
||||
}
|
||||
InodeIdentifier identifier = { fsid, file_description_metadata.global_procfs_inode_index() };
|
||||
InodeIdentifier identifier = { fsid, SegmentedProcFSIndex::build_segmented_index_for_file_description(pid(), count) };
|
||||
callback({ String::number(count), identifier, 0 });
|
||||
count++;
|
||||
});
|
||||
return KSuccess;
|
||||
}
|
||||
RefPtr<ProcFSExposedComponent> ProcFSProcessFileDescriptions::lookup(StringView name)
|
||||
|
||||
RefPtr<Inode> Process::lookup_file_descriptions_directory(const ProcFS& procfs, StringView name) const
|
||||
{
|
||||
MutexLocker locker(m_lock);
|
||||
auto parent_directory = m_process_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return nullptr;
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return nullptr;
|
||||
RefPtr<ProcFSProcessFileDescription> procfd_fd;
|
||||
RefPtr<ProcFSProcessPropertyInode> file_description_link;
|
||||
// FIXME: Try to exit the loop earlier
|
||||
size_t count = 0;
|
||||
process->fds().enumerate([&](auto& file_description_metadata) {
|
||||
fds().enumerate([&](auto& file_description_metadata) {
|
||||
if (!file_description_metadata.is_valid()) {
|
||||
count++;
|
||||
return;
|
||||
}
|
||||
if (name == String::number(count)) {
|
||||
procfd_fd = ProcFSProcessFileDescription::create(count, *file_description_metadata.description(), file_description_metadata.global_procfs_inode_index(), *this);
|
||||
file_description_link = ProcFSProcessPropertyInode::create_for_file_description_link(procfs, static_cast<unsigned>(count), pid());
|
||||
}
|
||||
count++;
|
||||
});
|
||||
return procfd_fd;
|
||||
return file_description_link;
|
||||
}
|
||||
|
||||
class ProcFSProcessPledge final : public ProcFSProcessInformation {
|
||||
public:
|
||||
static NonnullRefPtr<ProcFSProcessPledge> create(const ProcFSProcessDirectory& parent_directory)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSProcessPledge(parent_directory));
|
||||
KResult Process::procfs_get_pledge_stats(KBufferBuilder& builder) const
|
||||
{
|
||||
JsonObjectSerializer obj { builder };
|
||||
#define __ENUMERATE_PLEDGE_PROMISE(x) \
|
||||
if (has_promised(Pledge::x)) { \
|
||||
if (!builder.is_empty()) \
|
||||
builder.append(' '); \
|
||||
builder.append(#x); \
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSProcessPledge(const ProcFSProcessDirectory& parent_directory)
|
||||
: ProcFSProcessInformation("pledge"sv, parent_directory)
|
||||
{
|
||||
if (has_promises()) {
|
||||
StringBuilder builder;
|
||||
ENUMERATE_PLEDGE_PROMISES
|
||||
obj.add("promises", builder.build());
|
||||
}
|
||||
virtual bool output(KBufferBuilder& builder) override
|
||||
{
|
||||
auto parent_directory = m_parent_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return false;
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return false;
|
||||
JsonObjectSerializer obj { builder };
|
||||
#define __ENUMERATE_PLEDGE_PROMISE(x) \
|
||||
if (process->has_promised(Pledge::x)) { \
|
||||
if (!builder.is_empty()) \
|
||||
builder.append(' '); \
|
||||
builder.append(#x); \
|
||||
}
|
||||
if (process->has_promises()) {
|
||||
StringBuilder builder;
|
||||
ENUMERATE_PLEDGE_PROMISES
|
||||
obj.add("promises", builder.build());
|
||||
}
|
||||
#undef __ENUMERATE_PLEDGE_PROMISE
|
||||
obj.finish();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ProcFSProcessUnveil final : public ProcFSProcessInformation {
|
||||
public:
|
||||
static NonnullRefPtr<ProcFSProcessUnveil> create(const ProcFSProcessDirectory& parent_directory)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSProcessUnveil(parent_directory));
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSProcessUnveil(const ProcFSProcessDirectory& parent_directory)
|
||||
: ProcFSProcessInformation("unveil"sv, parent_directory)
|
||||
{
|
||||
}
|
||||
virtual bool output(KBufferBuilder& builder) override
|
||||
{
|
||||
auto parent_directory = m_parent_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return false;
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return false;
|
||||
JsonArraySerializer array { builder };
|
||||
for (auto& unveiled_path : process->unveiled_paths()) {
|
||||
if (!unveiled_path.was_explicitly_unveiled())
|
||||
continue;
|
||||
auto obj = array.add_object();
|
||||
obj.add("path", unveiled_path.path());
|
||||
StringBuilder permissions_builder;
|
||||
if (unveiled_path.permissions() & UnveilAccess::Read)
|
||||
permissions_builder.append('r');
|
||||
if (unveiled_path.permissions() & UnveilAccess::Write)
|
||||
permissions_builder.append('w');
|
||||
if (unveiled_path.permissions() & UnveilAccess::Execute)
|
||||
permissions_builder.append('x');
|
||||
if (unveiled_path.permissions() & UnveilAccess::CreateOrRemove)
|
||||
permissions_builder.append('c');
|
||||
if (unveiled_path.permissions() & UnveilAccess::Browse)
|
||||
permissions_builder.append('b');
|
||||
obj.add("permissions", permissions_builder.to_string());
|
||||
}
|
||||
array.finish();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ProcFSProcessPerformanceEvents final : public ProcFSProcessInformation {
|
||||
public:
|
||||
static NonnullRefPtr<ProcFSProcessPerformanceEvents> create(const ProcFSProcessDirectory& parent_directory)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSProcessPerformanceEvents(parent_directory));
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSProcessPerformanceEvents(const ProcFSProcessDirectory& parent_directory)
|
||||
: ProcFSProcessInformation("perf_events"sv, parent_directory)
|
||||
{
|
||||
}
|
||||
virtual bool output(KBufferBuilder& builder) override
|
||||
{
|
||||
InterruptDisabler disabler;
|
||||
auto parent_directory = m_parent_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return false;
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return false;
|
||||
if (!process->perf_events()) {
|
||||
dbgln("ProcFS: No perf events for {}", process->pid());
|
||||
return false;
|
||||
}
|
||||
return process->perf_events()->to_json(builder);
|
||||
}
|
||||
};
|
||||
|
||||
class ProcFSProcessOverallFileDescriptions final : public ProcFSProcessInformation {
|
||||
public:
|
||||
static NonnullRefPtr<ProcFSProcessOverallFileDescriptions> create(const ProcFSProcessDirectory& parent_directory)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSProcessOverallFileDescriptions(parent_directory));
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSProcessOverallFileDescriptions(const ProcFSProcessDirectory& parent_directory)
|
||||
: ProcFSProcessInformation("fds"sv, parent_directory)
|
||||
{
|
||||
}
|
||||
virtual bool output(KBufferBuilder& builder) override
|
||||
{
|
||||
auto parent_directory = m_parent_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return false;
|
||||
JsonArraySerializer array { builder };
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return false;
|
||||
if (process->fds().open_count() == 0) {
|
||||
array.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t count = 0;
|
||||
process->fds().enumerate([&](auto& file_description_metadata) {
|
||||
if (!file_description_metadata.is_valid()) {
|
||||
count++;
|
||||
return;
|
||||
}
|
||||
bool cloexec = file_description_metadata.flags() & FD_CLOEXEC;
|
||||
RefPtr<FileDescription> description = file_description_metadata.description();
|
||||
auto description_object = array.add_object();
|
||||
description_object.add("fd", count);
|
||||
description_object.add("absolute_path", description->absolute_path());
|
||||
description_object.add("seekable", description->file().is_seekable());
|
||||
description_object.add("class", description->file().class_name());
|
||||
description_object.add("offset", description->offset());
|
||||
description_object.add("cloexec", cloexec);
|
||||
description_object.add("blocking", description->is_blocking());
|
||||
description_object.add("can_read", description->can_read());
|
||||
description_object.add("can_write", description->can_write());
|
||||
count++;
|
||||
});
|
||||
|
||||
array.finish();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ProcFSProcessRoot final : public ProcFSExposedLink {
|
||||
public:
|
||||
static NonnullRefPtr<ProcFSProcessRoot> create(const ProcFSProcessDirectory& parent_directory)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSProcessRoot(parent_directory));
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSProcessRoot(const ProcFSProcessDirectory& parent_directory)
|
||||
: ProcFSExposedLink("root"sv)
|
||||
, m_parent_process_directory(parent_directory)
|
||||
{
|
||||
}
|
||||
virtual bool acquire_link(KBufferBuilder& builder) override
|
||||
{
|
||||
auto parent_directory = m_parent_process_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return false;
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return false;
|
||||
builder.append_bytes(process->root_directory_relative_to_global_root().absolute_path().to_byte_buffer());
|
||||
return true;
|
||||
}
|
||||
WeakPtr<ProcFSProcessDirectory> m_parent_process_directory;
|
||||
};
|
||||
|
||||
class ProcFSProcessVirtualMemory final : public ProcFSProcessInformation {
|
||||
public:
|
||||
static NonnullRefPtr<ProcFSProcessRoot> create(const ProcFSProcessDirectory& parent_directory)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSProcessVirtualMemory(parent_directory));
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSProcessVirtualMemory(const ProcFSProcessDirectory& parent_directory)
|
||||
: ProcFSProcessInformation("vm"sv, parent_directory)
|
||||
{
|
||||
}
|
||||
virtual bool output(KBufferBuilder& builder) override
|
||||
{
|
||||
auto parent_directory = m_parent_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return false;
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return false;
|
||||
JsonArraySerializer array { builder };
|
||||
{
|
||||
ScopedSpinLock lock(process->address_space().get_lock());
|
||||
for (auto& region : process->address_space().regions()) {
|
||||
if (!region->is_user() && !Process::current()->is_superuser())
|
||||
continue;
|
||||
auto region_object = array.add_object();
|
||||
region_object.add("readable", region->is_readable());
|
||||
region_object.add("writable", region->is_writable());
|
||||
region_object.add("executable", region->is_executable());
|
||||
region_object.add("stack", region->is_stack());
|
||||
region_object.add("shared", region->is_shared());
|
||||
region_object.add("syscall", region->is_syscall_region());
|
||||
region_object.add("purgeable", region->vmobject().is_anonymous());
|
||||
if (region->vmobject().is_anonymous()) {
|
||||
region_object.add("volatile", static_cast<Memory::AnonymousVMObject const&>(region->vmobject()).is_volatile());
|
||||
}
|
||||
region_object.add("cacheable", region->is_cacheable());
|
||||
region_object.add("address", region->vaddr().get());
|
||||
region_object.add("size", region->size());
|
||||
region_object.add("amount_resident", region->amount_resident());
|
||||
region_object.add("amount_dirty", region->amount_dirty());
|
||||
region_object.add("cow_pages", region->cow_pages());
|
||||
region_object.add("name", region->name());
|
||||
region_object.add("vmobject", region->vmobject().class_name());
|
||||
|
||||
StringBuilder pagemap_builder;
|
||||
for (size_t i = 0; i < region->page_count(); ++i) {
|
||||
auto* page = region->physical_page(i);
|
||||
if (!page)
|
||||
pagemap_builder.append('N');
|
||||
else if (page->is_shared_zero_page() || page->is_lazy_committed_page())
|
||||
pagemap_builder.append('Z');
|
||||
else
|
||||
pagemap_builder.append('P');
|
||||
}
|
||||
region_object.add("pagemap", pagemap_builder.to_string());
|
||||
}
|
||||
}
|
||||
array.finish();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ProcFSProcessCurrentWorkDirectory final : public ProcFSExposedLink {
|
||||
public:
|
||||
static NonnullRefPtr<ProcFSProcessCurrentWorkDirectory> create(const ProcFSProcessDirectory& parent_directory)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSProcessCurrentWorkDirectory(parent_directory));
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSProcessCurrentWorkDirectory(const ProcFSProcessDirectory& parent_directory)
|
||||
: ProcFSExposedLink("cwd"sv)
|
||||
, m_parent_process_directory(parent_directory)
|
||||
{
|
||||
}
|
||||
virtual bool acquire_link(KBufferBuilder& builder) override
|
||||
{
|
||||
auto parent_directory = m_parent_process_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return false;
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return false;
|
||||
builder.append_bytes(process->current_directory().absolute_path().bytes());
|
||||
return true;
|
||||
}
|
||||
|
||||
WeakPtr<ProcFSProcessDirectory> m_parent_process_directory;
|
||||
};
|
||||
|
||||
class ProcFSProcessBinary final : public ProcFSExposedLink {
|
||||
public:
|
||||
static NonnullRefPtr<ProcFSProcessBinary> create(const ProcFSProcessDirectory& parent_directory)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSProcessBinary(parent_directory));
|
||||
}
|
||||
|
||||
virtual mode_t required_mode() const override
|
||||
{
|
||||
auto parent_directory = m_parent_process_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return false;
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return false;
|
||||
if (!process->executable())
|
||||
return 0;
|
||||
return ProcFSExposedComponent::required_mode();
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSProcessBinary(const ProcFSProcessDirectory& parent_directory)
|
||||
: ProcFSExposedLink("exe"sv)
|
||||
, m_parent_process_directory(parent_directory)
|
||||
{
|
||||
}
|
||||
virtual bool acquire_link(KBufferBuilder& builder) override
|
||||
{
|
||||
auto parent_directory = m_parent_process_directory.strong_ref();
|
||||
if (parent_directory.is_null())
|
||||
return false;
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return false;
|
||||
auto* custody = process->executable();
|
||||
if (!custody)
|
||||
return false;
|
||||
builder.append(custody->absolute_path().bytes());
|
||||
return true;
|
||||
}
|
||||
|
||||
WeakPtr<ProcFSProcessDirectory> m_parent_process_directory;
|
||||
};
|
||||
|
||||
void ProcFSProcessDirectory::on_attach()
|
||||
{
|
||||
VERIFY(m_components.size() == 0);
|
||||
m_components.append(ProcFSProcessPledge::create(*this));
|
||||
m_components.append(ProcFSProcessUnveil::create(*this));
|
||||
m_components.append(ProcFSProcessPerformanceEvents::create(*this));
|
||||
m_components.append(ProcFSProcessFileDescriptions::create(*this));
|
||||
m_components.append(ProcFSProcessOverallFileDescriptions::create(*this));
|
||||
m_components.append(ProcFSProcessRoot::create(*this));
|
||||
m_components.append(ProcFSProcessVirtualMemory::create(*this));
|
||||
m_components.append(ProcFSProcessCurrentWorkDirectory::create(*this));
|
||||
m_components.append(ProcFSProcessBinary::create(*this));
|
||||
m_components.append(ProcFSProcessStacks::create(*this));
|
||||
}
|
||||
|
||||
RefPtr<ProcFSExposedComponent> ProcFSProcessDirectory::lookup(StringView name)
|
||||
{
|
||||
// Note: we need to allocate all sub components when doing a lookup, because
|
||||
// for some reason, the caller may not call ProcFSInode::attach method before calling this.
|
||||
if (m_components.size() == 0)
|
||||
on_attach();
|
||||
return ProcFSExposedDirectory::lookup(name);
|
||||
}
|
||||
|
||||
KResult ProcFSProcessDirectory::refresh_data(FileDescription&) const
|
||||
{
|
||||
if (m_components.size() != 0)
|
||||
return KSuccess;
|
||||
const_cast<ProcFSProcessDirectory&>(*this).on_attach();
|
||||
obj.finish();
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
NonnullRefPtr<ProcFSProcessDirectory> ProcFSProcessDirectory::create(const Process& process)
|
||||
KResult Process::procfs_get_unveil_stats(KBufferBuilder& builder) const
|
||||
{
|
||||
return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessDirectory(process)).release_nonnull();
|
||||
JsonArraySerializer array { builder };
|
||||
for (auto& unveiled_path : unveiled_paths()) {
|
||||
if (!unveiled_path.was_explicitly_unveiled())
|
||||
continue;
|
||||
auto obj = array.add_object();
|
||||
obj.add("path", unveiled_path.path());
|
||||
StringBuilder permissions_builder;
|
||||
if (unveiled_path.permissions() & UnveilAccess::Read)
|
||||
permissions_builder.append('r');
|
||||
if (unveiled_path.permissions() & UnveilAccess::Write)
|
||||
permissions_builder.append('w');
|
||||
if (unveiled_path.permissions() & UnveilAccess::Execute)
|
||||
permissions_builder.append('x');
|
||||
if (unveiled_path.permissions() & UnveilAccess::CreateOrRemove)
|
||||
permissions_builder.append('c');
|
||||
if (unveiled_path.permissions() & UnveilAccess::Browse)
|
||||
permissions_builder.append('b');
|
||||
obj.add("permissions", permissions_builder.to_string());
|
||||
}
|
||||
array.finish();
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
void ProcFSProcessDirectory::prepare_for_deletion()
|
||||
KResult Process::procfs_get_perf_events(KBufferBuilder& builder) const
|
||||
{
|
||||
ProcFSExposedDirectory::prepare_for_deletion();
|
||||
m_associated_process.clear();
|
||||
InterruptDisabler disabler;
|
||||
if (!const_cast<Process&>(*this).perf_events()) {
|
||||
dbgln("ProcFS: No perf events for {}", pid());
|
||||
return KResult(ENOBUFS);
|
||||
}
|
||||
return const_cast<Process&>(*this).perf_events()->to_json(builder) ? KSuccess : KResult(EINVAL);
|
||||
}
|
||||
|
||||
ProcFSProcessDirectory::ProcFSProcessDirectory(const Process& process)
|
||||
: ProcFSExposedDirectory(String::formatted("{:d}", process.pid().value()), ProcFSComponentRegistry::the().root_directory())
|
||||
, m_associated_process(process)
|
||||
KResult Process::procfs_get_fds_stats(KBufferBuilder& builder) const
|
||||
{
|
||||
JsonArraySerializer array { builder };
|
||||
if (fds().open_count() == 0) {
|
||||
array.finish();
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
size_t count = 0;
|
||||
fds().enumerate([&](auto& file_description_metadata) {
|
||||
if (!file_description_metadata.is_valid()) {
|
||||
count++;
|
||||
return;
|
||||
}
|
||||
bool cloexec = file_description_metadata.flags() & FD_CLOEXEC;
|
||||
RefPtr<FileDescription> description = file_description_metadata.description();
|
||||
auto description_object = array.add_object();
|
||||
description_object.add("fd", count);
|
||||
description_object.add("absolute_path", description->absolute_path());
|
||||
description_object.add("seekable", description->file().is_seekable());
|
||||
description_object.add("class", description->file().class_name());
|
||||
description_object.add("offset", description->offset());
|
||||
description_object.add("cloexec", cloexec);
|
||||
description_object.add("blocking", description->is_blocking());
|
||||
description_object.add("can_read", description->can_read());
|
||||
description_object.add("can_write", description->can_write());
|
||||
count++;
|
||||
});
|
||||
|
||||
array.finish();
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
KResult Process::procfs_get_root_link(KBufferBuilder& builder) const
|
||||
{
|
||||
builder.append_bytes(const_cast<Process&>(*this).root_directory_relative_to_global_root().absolute_path().to_byte_buffer());
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
KResult Process::procfs_get_virtual_memory_stats(KBufferBuilder& builder) const
|
||||
{
|
||||
JsonArraySerializer array { builder };
|
||||
{
|
||||
ScopedSpinLock lock(address_space().get_lock());
|
||||
for (auto& region : address_space().regions()) {
|
||||
if (!region->is_user() && !Process::current()->is_superuser())
|
||||
continue;
|
||||
auto region_object = array.add_object();
|
||||
region_object.add("readable", region->is_readable());
|
||||
region_object.add("writable", region->is_writable());
|
||||
region_object.add("executable", region->is_executable());
|
||||
region_object.add("stack", region->is_stack());
|
||||
region_object.add("shared", region->is_shared());
|
||||
region_object.add("syscall", region->is_syscall_region());
|
||||
region_object.add("purgeable", region->vmobject().is_anonymous());
|
||||
if (region->vmobject().is_anonymous()) {
|
||||
region_object.add("volatile", static_cast<Memory::AnonymousVMObject const&>(region->vmobject()).is_volatile());
|
||||
}
|
||||
region_object.add("cacheable", region->is_cacheable());
|
||||
region_object.add("address", region->vaddr().get());
|
||||
region_object.add("size", region->size());
|
||||
region_object.add("amount_resident", region->amount_resident());
|
||||
region_object.add("amount_dirty", region->amount_dirty());
|
||||
region_object.add("cow_pages", region->cow_pages());
|
||||
region_object.add("name", region->name());
|
||||
region_object.add("vmobject", region->vmobject().class_name());
|
||||
|
||||
StringBuilder pagemap_builder;
|
||||
for (size_t i = 0; i < region->page_count(); ++i) {
|
||||
auto* page = region->physical_page(i);
|
||||
if (!page)
|
||||
pagemap_builder.append('N');
|
||||
else if (page->is_shared_zero_page() || page->is_lazy_committed_page())
|
||||
pagemap_builder.append('Z');
|
||||
else
|
||||
pagemap_builder.append('P');
|
||||
}
|
||||
region_object.add("pagemap", pagemap_builder.to_string());
|
||||
}
|
||||
}
|
||||
array.finish();
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
KResult Process::procfs_get_current_work_directory_link(KBufferBuilder& builder) const
|
||||
{
|
||||
builder.append_bytes(const_cast<Process&>(*this).current_directory().absolute_path().bytes());
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
mode_t Process::binary_link_required_mode() const
|
||||
{
|
||||
if (!executable())
|
||||
return 0;
|
||||
return ProcFSExposedComponent::required_mode();
|
||||
}
|
||||
|
||||
KResult Process::procfs_get_binary_link(KBufferBuilder& builder) const
|
||||
{
|
||||
auto* custody = executable();
|
||||
if (!custody)
|
||||
return KResult(ENOEXEC);
|
||||
builder.append(custody->absolute_path().bytes());
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue