int test_procstat(struct tf_test *thiz) { struct procstat *ps; struct kinfo_vmentry *kv; struct kinfo_proc *kp; unsigned cnt, i; ps = procstat_open_sysctl(); thiz->t_assert(ps != NULL); cnt = 0; kp = procstat_getprocs(ps, KERN_PROC_PID, getpid(), &cnt); thiz->t_assert(kp != NULL); thiz->t_pf("getprocs retrieved %u procs\n", cnt); kv = procstat_getvmmap(ps, kp, &cnt); thiz->t_assert(kv != NULL); for (i = 0; i < cnt; i++) { thiz->t_pf("[%u] type: %d, " "start: 0x%"PRIx64", end: 0x%"PRIx64" " "(sz: %"PRIu64"%c), prot: 0x%x\n", i, kv[i].kve_type, kv[i].kve_start, kv[i].kve_end, SZFORMAT(kv[i].kve_end - kv[i].kve_start), kv[i].kve_protection); } procstat_freevmmap(ps, kv); procstat_freeprocs(ps, kp); procstat_close(ps); return (TF_SUCC); }
const char *proc_maps(pid_t pid, size_t *start, size_t *end, int *exe_self) { static struct kinfo_vmentry *freep = NULL; static unsigned int i, cnt; static char *exe_name; /* first, init */ if (freep == NULL) { struct kinfo_proc *ki = kinfo_getproc(pid); if (ki == NULL) { perror("Error in get process info"); exit(6); } freep = procstat_getvmmap(procstat_open_sysctl(), ki, &cnt); exe_name = ki->ki_comm; } while (i < cnt) { struct kinfo_vmentry *kve = &freep[i++]; if ((kve->kve_protection & KVME_PROT_EXEC) && kve->kve_path[0] == '/') { *start = kve->kve_start; *end = kve->kve_end; if (exe_self != NULL) { *exe_self = (strcmp(exe_name, strrchr(kve->kve_path, '/') + 1) == 0); } return kve->kve_path; } } i = 0; free(freep); freep = NULL; return NULL; }
int gc_vm_tbl_update(_gc_cap struct gc_vm_tbl *vt) { #ifdef GC_USE_LIBPROCSTAT struct procstat *ps; struct kinfo_vmentry *kv; struct kinfo_proc *kp; unsigned cnt, i; ps = procstat_open_sysctl(); if (ps == NULL) return (GC_ERROR); cnt = 0; kp = procstat_getprocs(ps, KERN_PROC_PID, getpid(), &cnt); if (kp == NULL) return (GC_ERROR); gc_debug("getprocs retrieved %u procs", cnt); kv = procstat_getvmmap(ps, kp, &cnt); if (kv == NULL) return (GC_ERROR); gc_debug("getvmmap retrieved %u entries", cnt); if (vt->vt_sz < cnt) return (GC_TOO_SMALL); vt->vt_nent = cnt; for (i = 0; i < vt->vt_nent; i++) { vt->vt_ent[i].ve_start = kv[i].kve_start; vt->vt_ent[i].ve_end = kv[i].kve_end; vt->vt_ent[i].ve_prot = kv[i].kve_protection; vt->vt_ent[i].ve_type = kv[i].kve_type; vt->vt_ent[i].ve_gctype = 0; gc_vm_tbl_track(vt, &vt->vt_ent[i]); } procstat_freevmmap(ps, kv); procstat_freeprocs(ps, kp); procstat_close(ps); return (GC_SUCC); #else /* !GC_USE_LIBPROCSTAT */ return (GC_ERROR); #endif /* GC_USE_LIBPROCSTAT */ }
void procstat_vm(struct procstat *procstat, struct kinfo_proc *kipp) { struct kinfo_vmentry *freep, *kve; int ptrwidth; unsigned int i, cnt; const char *str; #ifdef __x86_64__ ptrwidth = 14; #else ptrwidth = 2*sizeof(void *) + 2; #endif fprintf(stderr, "%*s %*s %3s %4s %4s %3s %3s %4s %-2s %-s\n", ptrwidth, "START", ptrwidth, "END", "PRT", "RES", "PRES", "REF", "SHD", "FL", "TP", "PATH"); #ifdef HAVE_PROCSTAT_GETVMMAP freep = procstat_getvmmap(procstat, kipp, &cnt); #else freep = kinfo_getvmmap(kipp->ki_pid, &cnt); #endif if (freep == NULL) return; for (i = 0; i < cnt; i++) { kve = &freep[i]; fprintf(stderr, "%#*jx ", ptrwidth, (uintmax_t)kve->kve_start); fprintf(stderr, "%#*jx ", ptrwidth, (uintmax_t)kve->kve_end); fprintf(stderr, "%s", kve->kve_protection & KVME_PROT_READ ? "r" : "-"); fprintf(stderr, "%s", kve->kve_protection & KVME_PROT_WRITE ? "w" : "-"); fprintf(stderr, "%s ", kve->kve_protection & KVME_PROT_EXEC ? "x" : "-"); fprintf(stderr, "%4d ", kve->kve_resident); fprintf(stderr, "%4d ", kve->kve_private_resident); fprintf(stderr, "%3d ", kve->kve_ref_count); fprintf(stderr, "%3d ", kve->kve_shadow_count); fprintf(stderr, "%-1s", kve->kve_flags & KVME_FLAG_COW ? "C" : "-"); fprintf(stderr, "%-1s", kve->kve_flags & KVME_FLAG_NEEDS_COPY ? "N" : "-"); fprintf(stderr, "%-1s", kve->kve_flags & KVME_FLAG_SUPER ? "S" : "-"); fprintf(stderr, "%-1s ", kve->kve_flags & KVME_FLAG_GROWS_UP ? "U" : kve->kve_flags & KVME_FLAG_GROWS_DOWN ? "D" : "-"); switch (kve->kve_type) { case KVME_TYPE_NONE: str = "--"; break; case KVME_TYPE_DEFAULT: str = "df"; break; case KVME_TYPE_VNODE: str = "vn"; break; case KVME_TYPE_SWAP: str = "sw"; break; case KVME_TYPE_DEVICE: str = "dv"; break; case KVME_TYPE_PHYS: str = "ph"; break; case KVME_TYPE_DEAD: str = "dd"; break; case KVME_TYPE_SG: str = "sg"; break; case KVME_TYPE_MGTDEVICE: str = "md"; break; case KVME_TYPE_UNKNOWN: default: str = "??"; break; } fprintf(stderr, "%-2s ", str); fprintf(stderr, "%-s\n", kve->kve_path); } free(freep); }
files = procstat_getfiles(pstat, proc, 0); if (files != nullptr) { STAILQ_FOREACH(file, files, next) { if (file->fs_uflags & PS_FST_UFLAG_CDIR) { r["cwd"] = TEXT(file->fs_path); } else if (file->fs_uflags & PS_FST_UFLAG_RDIR) { r["root"] = TEXT(file->fs_path); } } procstat_freefiles(pstat, files); } vmentry = procstat_getvmmap(pstat, proc, &cnt); if (vmentry != nullptr) { // Add up all the resident pages for each vmmap entry. for (i = 0; i < cnt; i++) { pages += vmentry[i].kve_resident; } // The column is in bytes. r["resident_size"] += INTEGER(pages * getpagesize()); procstat_freevmmap(pstat, vmentry); } // XXX: Not sure how to get these on FreeBSD yet. r["wired_size"] = INTEGER("0"); r["phys_footprint"] = INTEGER("0");
/** * \todo This function contains many cases that do not allow for a * recovery. Currently, xbt_abort() is called but we should * much rather die with the specific reason so that it's easier * to find out what's going on. */ XBT_PRIVATE std::vector<VmMap> get_memory_map(pid_t pid) { std::vector<VmMap> ret; #ifdef __linux__ /* Open the actual process's proc maps file and create the memory_map_t */ /* to be returned. */ char* path = bprintf("/proc/%i/maps", (int) pid); FILE *fp = std::fopen(path, "r"); if (fp == nullptr) { std::perror("fopen failed"); xbt_die("Cannot open %s to investigate the memory map of the process.", path); } free(path); setbuf(fp, nullptr); /* Read one line at the time, parse it and add it to the memory map to be returned */ ssize_t read; /* Number of bytes readed */ char* line = nullptr; std::size_t n = 0; /* Amount of bytes to read by xbt_getline */ while ((read = xbt_getline(&line, &n, fp)) != -1) { /** * The lines that we read have this format: (This is just an example) * 00602000-00603000 rw-p 00002000 00:28 1837264 <complete-path-to-file> */ //fprintf(stderr,"%s", line); /* Wipeout the new line character */ line[read - 1] = '\0'; /* Tokenize the line using spaces as delimiters and store each token in lfields array. We expect 5 tokens for 6 fields */ char* lfields[6]; lfields[0] = strtok(line, " "); int i; for (i = 1; i < 6 && lfields[i - 1] != nullptr; i++) { lfields[i] = std::strtok(nullptr, " "); } /* Check to see if we got the expected amount of columns */ if (i < 6) xbt_die("The memory map apparently only supplied less than 6 columns. Recovery impossible."); /* Ok we are good enough to try to get the info we need */ /* First get the start and the end address of the map */ char *tok = std::strtok(lfields[0], "-"); if (tok == nullptr) xbt_die("Start and end address of the map are not concatenated by a hyphen (-). Recovery impossible."); VmMap memreg; char *endptr; memreg.start_addr = std::strtoull(tok, &endptr, 16); /* Make sure that the entire string was an hex number */ if (*endptr != '\0') xbt_abort(); tok = std::strtok(nullptr, "-"); if (tok == nullptr) xbt_abort(); memreg.end_addr = std::strtoull(tok, &endptr, 16); /* Make sure that the entire string was an hex number */ if (*endptr != '\0') xbt_abort(); /* Get the permissions flags */ if (std::strlen(lfields[1]) < 4) xbt_abort(); memreg.prot = 0; for (i = 0; i < 3; i++){ switch(lfields[1][i]){ case 'r': memreg.prot |= PROT_READ; break; case 'w': memreg.prot |= PROT_WRITE; break; case 'x': memreg.prot |= PROT_EXEC; break; default: break; } } if (memreg.prot == 0) memreg.prot |= PROT_NONE; if (lfields[1][3] == 'p') { memreg.flags |= MAP_PRIVATE; } else { memreg.flags |= MAP_SHARED; if (lfields[1][3] != 's') XBT_WARN("The protection is neither 'p' (private) nor 's' (shared) but '%s'. Let's assume shared, as on b0rken win-ubuntu systems.\nFull line: %s\n", lfields[1], line); } /* Get the offset value */ memreg.offset = std::strtoull(lfields[2], &endptr, 16); /* Make sure that the entire string was an hex number */ if (*endptr != '\0') xbt_abort(); /* Get the device major:minor bytes */ tok = std::strtok(lfields[3], ":"); if (tok == nullptr) xbt_abort(); memreg.dev_major = (char) strtoul(tok, &endptr, 16); /* Make sure that the entire string was an hex number */ if (*endptr != '\0') xbt_abort(); tok = std::strtok(nullptr, ":"); if (tok == nullptr) xbt_abort(); memreg.dev_minor = (char) std::strtoul(tok, &endptr, 16); /* Make sure that the entire string was an hex number */ if (*endptr != '\0') xbt_abort(); /* Get the inode number and make sure that the entire string was a long int */ memreg.inode = strtoul(lfields[4], &endptr, 10); if (*endptr != '\0') xbt_abort(); /* And finally get the pathname */ if (lfields[5]) memreg.pathname = lfields[5]; /* Create space for a new map region in the region's array and copy the */ /* parsed stuff from the temporal memreg variable */ XBT_DEBUG("Found region for %s", !memreg.pathname.empty() ? memreg.pathname.c_str() : "(null)"); ret.push_back(std::move(memreg)); } std::free(line); std::fclose(fp); #elif defined __FreeBSD__ struct procstat *prstat; struct kinfo_proc *proc; struct kinfo_vmentry *vmentries; unsigned int cnt; if ((prstat = procstat_open_sysctl()) == NULL) { std::perror("procstat_open_sysctl failed"); xbt_die("Cannot access kernel state information"); } if ((proc = procstat_getprocs(prstat, KERN_PROC_PID, pid, &cnt)) == NULL) { std::perror("procstat_open_sysctl failed"); xbt_die("Cannot access process information"); } if ((vmentries = procstat_getvmmap(prstat, proc, &cnt)) == NULL) { std::perror("procstat_getvmmap failed"); xbt_die("Cannot access process memory mappings"); } for (unsigned int i = 0; i < cnt; i++) { VmMap memreg; /* Addresses */ memreg.start_addr = vmentries[i].kve_start; memreg.end_addr = vmentries[i].kve_end; /* Permissions */ memreg.prot = PROT_NONE; if (vmentries[i].kve_protection & KVME_PROT_READ) memreg.prot |= PROT_READ; if (vmentries[i].kve_protection & KVME_PROT_WRITE) memreg.prot |= PROT_WRITE; if (vmentries[i].kve_protection & KVME_PROT_EXEC) memreg.prot |= PROT_EXEC; /* Private (copy-on-write) or shared? */ if (vmentries[i].kve_flags & KVME_FLAG_COW) memreg.flags |= MAP_PRIVATE; else memreg.flags |= MAP_SHARED; /* Offset */ memreg.offset = vmentries[i].kve_offset; /* Device : not sure this can be mapped to something outside of Linux? */ memreg.dev_major = 0; memreg.dev_minor = 0; /* Inode */ memreg.inode = vmentries[i].kve_vn_fileid; /* * Path. Linuxize result by giving an anonymous mapping a path from * the previous mapping, provided previous is vnode and has a path, * and mark the stack. */ if (vmentries[i].kve_path[0] != '\0') memreg.pathname = vmentries[i].kve_path; else if (vmentries[i].kve_type == KVME_TYPE_DEFAULT && vmentries[i-1].kve_type == KVME_TYPE_VNODE && vmentries[i-1].kve_path[0] != '\0') memreg.pathname = vmentries[i-1].kve_path; else if (vmentries[i].kve_type == KVME_TYPE_DEFAULT && vmentries[i].kve_flags & KVME_FLAG_GROWS_DOWN) memreg.pathname = "[stack]"; /* * One last dirty modification: remove write permission from shared * libraries private clean pages. This is necessary because simgrid * later identifies mappings based on the permissions that are expected * when running the Linux kernel. */ if (vmentries[i].kve_type == KVME_TYPE_VNODE && ! (vmentries[i].kve_flags & KVME_FLAG_NEEDS_COPY)) memreg.prot &= ~PROT_WRITE; ret.push_back(std::move(memreg)); } procstat_freevmmap(prstat, vmentries); procstat_freeprocs(prstat, proc); procstat_close(prstat); #else xbt_die("Could not get memory map from process %lli", (long long int) pid); #endif return ret; }