Skip to content

Commit

Permalink
Merge pull request #252 from P403n1x87/refactor/linux-proc-maps
Browse files Browse the repository at this point in the history
refactor(linux): proc maps parsing
  • Loading branch information
P403n1x87 authored Feb 18, 2025
2 parents c9cf2c6 + 2e78ef9 commit 5823eaf
Show file tree
Hide file tree
Showing 5 changed files with 356 additions and 153 deletions.
57 changes: 57 additions & 0 deletions src/linux/proc/exe.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// This file is part of "austin" which is released under GPL.
//
// See file LICENCE or go to http://www.gnu.org/licenses/ for full license
// details.
//
// Austin is a Python frame stack sampler for CPython.
//
// Copyright (c) 2018-2021 Gabriele N. Tornetta <[email protected]>.
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#pragma once


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "../../error.h"
#include "../../hints.h"


#define DELETED_SUFFIX " (deleted)"


// ----------------------------------------------------------------------------
static int
proc_exe_readlink(pid_t pid, char * dest, ssize_t size) {
char file_name[32];

sprintf(file_name, "/proc/%d/exe", pid);

if (readlink(file_name, dest, size) == -1) {
log_e("Cannot readlink %s", file_name);
FAIL; // cppcheck-suppress [resourceLeak]
}

// Handle deleted files
char * suffix = strstr(dest, DELETED_SUFFIX);
if (isvalid(suffix)) {
*suffix = '\0';
}

SUCCESS;
}
171 changes: 171 additions & 0 deletions src/linux/proc/maps.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// This file is part of "austin" which is released under GPL.
//
// See file LICENCE or go to http://www.gnu.org/licenses/ for full license
// details.
//
// Austin is a Python frame stack sampler for CPython.
//
// Copyright (c) 2018-2021 Gabriele N. Tornetta <[email protected]>.
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#pragma once


#include <stdio.h>
#include <stdint.h>
#include <string.h>

#include "../../error.h"
#include "../../resources.h"
#include "../common.h"

// ----------------------------------------------------------------------------

typedef struct _proc_map {
void * address;
size_t size;
uint8_t perms;
char * pathname;

struct _proc_map * next;
} proc_map_t;


#define PERMS_READ (1 << 0)
#define PERMS_WRITE (1 << 1)
#define PERMS_EXEC (1 << 2)


#define PROC_MAP_ITER(proc_maps, map) \
for (proc_map_t * map = proc_maps; isvalid(map); map = map->next)


// ----------------------------------------------------------------------------
static inline proc_map_t *
proc_map_new(pid_t pid) {
cu_char * line = NULL;
size_t len = 0;
char pathname[1024] = { 0 };
char perms[5] = { 0 };
proc_map_t * head = NULL;
proc_map_t * curr = NULL;
proc_map_t * next = NULL;

cu_FILE* fp = _procfs(pid, "maps");
if (fp == NULL) {
switch (errno) {
case EACCES: // Needs elevated privileges
set_error(EPROCPERM);
break;
case ENOENT: // Invalid pid
set_error(EPROCNPID);
break;
default:
set_error(EPROCVM);
}
return NULL;
}

while (getline(&line, &len, fp) != -1) {
ssize_t lower, upper;

int has_pathname = sscanf(line, ADDR_FMT "-" ADDR_FMT " %s %*x %*x:%*x %*x %s\n",
&lower, &upper, // Map bounds
perms, // Permissions
pathname // Binary path
) - 3; // We expect between 3 and 4 matches. We skip offset, dev and inode

if (has_pathname < 0) {
// Too few columns. This shouldn't happen but we skip this case anyway.
continue;
}
if (has_pathname && pathname[0] == '[') {
// Skip kernel memory maps
continue;
}

next = (proc_map_t*)calloc(1, sizeof(proc_map_t));
if (!isvalid(next)) {
log_ie("Cannot allocate memory for proc_map_t");
set_error(EPROC);
break;
}
if (!isvalid(head)) {
head = next;
}
else {
curr->next = next;
}
curr = next;

curr->address = (void*)lower;
curr->size = upper - lower;
curr->perms = 0;
if (perms[0] == 'r') curr->perms |= PERMS_READ;
if (perms[1] == 'w') curr->perms |= PERMS_WRITE;
if (perms[2] == 'x') curr->perms |= PERMS_EXEC;
curr->pathname = has_pathname ? strdup(pathname) : NULL;
}

return head;
}


// ----------------------------------------------------------------------------
static inline proc_map_t*
proc_map__first(proc_map_t* self, char * pathname) {
if (!isvalid(self) || !isvalid(pathname))
return NULL;

PROC_MAP_ITER(self, map) {
if (isvalid(map->pathname) && strcmp(map->pathname, pathname) == 0)
return map;
}

return NULL;
}


// ----------------------------------------------------------------------------
static inline proc_map_t*
proc_map__first_submatch(proc_map_t* self, char * needle) {
if (!isvalid(self) || !isvalid(needle))
return NULL;

PROC_MAP_ITER(self, map) {
if (isvalid(map->pathname) && isvalid(strstr(map->pathname, needle))) {
return map;
}
}

return NULL;
}


// ----------------------------------------------------------------------------
static inline void
proc_map__destroy(proc_map_t * self) {
if (!isvalid(self))
return;

proc_map__destroy(self->next);

sfree(self->pathname);

free(self);
}

CLEANUP_TYPE(proc_map_t, proc_map__destroy);
#define cu_proc_map_t __attribute__((cleanup(proc_map__destroyt))) proc_map_t
Loading

0 comments on commit 5823eaf

Please sign in to comment.