Add `/proc/[pid]/maps`

This commit is contained in:
vvsv 2025-12-10 14:14:31 +00:00 committed by Tate, Hongliang Tian
parent b1b909dd0f
commit 4e0ae560bf
5 changed files with 113 additions and 3 deletions

View File

@ -0,0 +1,53 @@
// SPDX-License-Identifier: MPL-2.0
use aster_util::printer::VmPrinter;
use super::TidDirOps;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
utils::{Inode, mkmod},
},
prelude::*,
process::Process,
vm::vmar::userspace_range,
};
/// Represents the inode at `/proc/[pid]/task/[tid]/maps` (and also `/proc/[pid]/maps`).
pub struct MapsFileOps(Arc<Process>);
impl MapsFileOps {
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.process_ref.clone();
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3343>
ProcFileBuilder::new(Self(process_ref), mkmod!(a+r))
.parent(parent)
.build()
.unwrap()
}
}
impl FileOps for MapsFileOps {
fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
let mut printer = VmPrinter::new_skip(writer, offset);
let vmar_guard = self.0.lock_vmar();
let Some(vmar) = vmar_guard.as_ref() else {
return_errno_with_message!(Errno::ESRCH, "the process has exited");
};
let user_stack_top = vmar.process_vm().init_stack().user_stack_top();
let guard = vmar.query(userspace_range());
for vm_mapping in guard.iter() {
if vm_mapping.map_to_addr() <= user_stack_top && vm_mapping.map_end() > user_stack_top {
vm_mapping.print_to_maps(&mut printer, "[stack]")?;
} else {
// TODO: Print the status of mappings other than the stack.
continue;
}
}
Ok(printer.bytes_written())
}
}

View File

@ -11,8 +11,9 @@ use crate::{
pid::task::{
cgroup::CgroupFileOps, cmdline::CmdlineFileOps, comm::CommFileOps,
environ::EnvironFileOps, exe::ExeSymOps, fd::FdDirOps, gid_map::GidMapFileOps,
mem::MemFileOps, mountinfo::MountInfoFileOps, oom_score_adj::OomScoreAdjFileOps,
stat::StatFileOps, status::StatusFileOps, uid_map::UidMapFileOps,
maps::MapsFileOps, mem::MemFileOps, mountinfo::MountInfoFileOps,
oom_score_adj::OomScoreAdjFileOps, stat::StatFileOps, status::StatusFileOps,
uid_map::UidMapFileOps,
},
template::{
DirOps, ProcDir, ProcDirBuilder, lookup_child_from_table,
@ -33,6 +34,7 @@ mod environ;
mod exe;
mod fd;
mod gid_map;
mod maps;
mod mem;
mod mountinfo;
mod oom_score_adj;
@ -112,6 +114,7 @@ impl TidDirOps {
("stat", StatFileOps::new_inode),
("status", StatusFileOps::new_inode),
("uid_map", UidMapFileOps::new_inode),
("maps", MapsFileOps::new_inode),
];
}

View File

@ -99,7 +99,7 @@ impl ProcessVm {
}
/// Returns the initial portion of the main stack of a process.
pub(super) fn init_stack(&self) -> &InitStack {
pub fn init_stack(&self) -> &InitStack {
&self.init_stack
}

View File

@ -932,6 +932,11 @@ pub fn is_userspace_vaddr(vaddr: Vaddr) -> bool {
(VMAR_LOWEST_ADDR..VMAR_CAP_ADDR).contains(&vaddr)
}
/// Returns the full user space virtual address range.
pub fn userspace_range() -> Range<Vaddr> {
VMAR_LOWEST_ADDR..VMAR_CAP_ADDR
}
fn check_userspace_page_range(vaddr: Vaddr, len: usize) -> Result<Range<Vaddr>> {
let Some(end) = vaddr.checked_add(len) else {
return_errno_with_message!(Errno::EINVAL, "address overflow");

View File

@ -7,6 +7,7 @@ use core::{
};
use align_ext::AlignExt;
use aster_util::printer::VmPrinter;
use ostd::{
io::IoMem,
mm::{
@ -200,6 +201,54 @@ impl VmMapping {
Ok(())
}
/// Prints the mapping information in the format of `/proc/[pid]/maps`.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/task_mmu.c#L304-L359>
pub fn print_to_maps(&self, printer: &mut VmPrinter, name: &str) -> Result<()> {
let start = self.map_to_addr;
let end = self.map_end();
let read_char = if self.perms.contains(VmPerms::READ) {
'r'
} else {
'-'
};
let write_char = if self.perms.contains(VmPerms::WRITE) {
'w'
} else {
'-'
};
let exec_char = if self.perms.contains(VmPerms::EXEC) {
'x'
} else {
'-'
};
let shared_char = if self.is_shared { 's' } else { 'p' };
let offset = self.vmo().map(|vmo| vmo.offset).unwrap_or(0);
let (dev_major, dev_minor) = self
.inode()
.map(|inode| device_id::decode_device_numbers(inode.metadata().dev))
.unwrap_or((0, 0));
let ino = self.inode().map(|inode| inode.ino()).unwrap_or(0);
writeln!(
printer,
"{:x}-{:x} {}{}{}{} {:08x} {:02x}:{:02x} {:<26} {}",
start,
end,
read_char,
write_char,
exec_char,
shared_char,
offset,
dev_major,
dev_minor,
ino,
name
)?;
Ok(())
}
/// Returns whether this mapping is a COW mapping.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/include/linux/mm.h#L1470-L1473>