From 2cd736129480e3cadc9164c82c7529c4d91d0deb Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Wed, 8 Mar 2023 22:20:59 +0100 Subject: [PATCH] Add memusage(2). Switch xz to memusage(2) and fix native self-cross issue. This is a compatible ABI change. --- Makefile | 2 +- kernel/fs/kram.cpp | 4 +- .../include/sortix/kernel/memorymanagement.h | 7 +- kernel/include/sortix/kernel/syscall.h | 3 +- kernel/include/sortix/memusage.h | 37 ++++ kernel/include/sortix/syscall.h | 5 +- kernel/memorymanagement.cpp | 48 +++- kernel/net/packet.cpp | 4 +- kernel/process.cpp | 6 +- kernel/syscall.cpp | 3 +- kernel/x86-family/memorymanagement.cpp | 18 +- libc/Makefile | 1 + libc/include/memusage.h | 39 ++++ libc/memusage/memusage.c | 30 +++ ports/xz/xz.patch | 62 +++++- share/man/man7/following-development.7 | 5 + utils/memstat.1 | 85 +++++++- utils/memstat.c | 205 ++++++++++++++---- 18 files changed, 492 insertions(+), 72 deletions(-) create mode 100644 kernel/include/sortix/memusage.h create mode 100644 libc/include/memusage.h create mode 100644 libc/memusage/memusage.c diff --git a/Makefile b/Makefile index 7eeb6ecd..6da60cd5 100644 --- a/Makefile +++ b/Makefile @@ -225,7 +225,7 @@ sysroot-system: sysroot-fsh sysroot-base-headers echo 'ID=sortix' && \ echo 'VERSION_ID="$(VERSION)"' && \ echo 'PRETTY_NAME="Sortix $(VERSION)"' && \ - echo 'SORTIX_ABI=1.3' && \ + echo 'SORTIX_ABI=1.4' && \ true) > "$(SYSROOT)/etc/sortix-release" echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system" ln -sf sortix-release "$(SYSROOT)/etc/os-release" diff --git a/kernel/fs/kram.cpp b/kernel/fs/kram.cpp index bbf57a00..e328df9e 100644 --- a/kernel/fs/kram.cpp +++ b/kernel/fs/kram.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2012, 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -90,7 +90,7 @@ int common_statvfs(ioctx_t* ctx, struct statvfs* stvfs, dev_t dev) { size_t memory_used; size_t memory_total; - Memory::Statistics(&memory_used, &memory_total); + Memory::Statistics(&memory_used, &memory_total, NULL); struct statvfs retstvfs; memset(&retstvfs, 0, sizeof(retstvfs)); retstvfs.f_bsize = Page::Size(); diff --git a/kernel/include/sortix/kernel/memorymanagement.h b/kernel/include/sortix/kernel/memorymanagement.h index ef2791de..088f3e2f 100644 --- a/kernel/include/sortix/kernel/memorymanagement.h +++ b/kernel/include/sortix/kernel/memorymanagement.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2014, 2015, 2017 Jonas 'Sortie' Termansen. + * Copyright (c) 2011, 2012, 2014, 2015, 2017, 2022 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -33,13 +33,12 @@ class Process; enum page_usage { - PAGE_USAGE_OTHER, PAGE_USAGE_PHYSICAL, PAGE_USAGE_PAGING_OVERHEAD, PAGE_USAGE_KERNEL_HEAP, PAGE_USAGE_FILESYSTEM_CACHE, PAGE_USAGE_USER_SPACE, - PAGE_USAGE_EXECUTE, + PAGE_USAGE_EXECVE, PAGE_USAGE_DRIVER, PAGE_USAGE_NETWORK_PACKET, PAGE_USAGE_NUM_KINDS, @@ -110,7 +109,7 @@ void PageProtectAdd(addr_t mapto, int protection); void PageProtectSub(addr_t mapto, int protection); bool MapRange(addr_t where, size_t bytes, int protection, enum page_usage usage); bool UnmapRange(addr_t where, size_t bytes, enum page_usage usage); -void Statistics(size_t* amountused, size_t* totalmem); +void Statistics(size_t* used, size_t* total, size_t* purposes); addr_t GetKernelStack(); size_t GetKernelStackSize(); void GetKernelVirtualArea(addr_t* from, size_t* size); diff --git a/kernel/include/sortix/kernel/syscall.h b/kernel/include/sortix/kernel/syscall.h index d84261b1..17bc22f8 100644 --- a/kernel/include/sortix/kernel/syscall.h +++ b/kernel/include/sortix/kernel/syscall.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2016, 2021 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2021, 2022, 2023 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -117,6 +117,7 @@ int sys_linkat(int, const char*, int, const char*, int); int sys_listen(int, int); off_t sys_lseek(int, off_t, int); int sys_memstat(size_t*, size_t*); +int sys_memusage(const size_t*, size_t*, size_t); int sys_mkdirat(int, const char*, mode_t); int sys_mkpartition(int, off_t, off_t, int); int sys_mkpty(int*, int*, int); diff --git a/kernel/include/sortix/memusage.h b/kernel/include/sortix/memusage.h new file mode 100644 index 00000000..51417334 --- /dev/null +++ b/kernel/include/sortix/memusage.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * sortix/memusage.h + * Memory usage statistics. + */ + +#ifndef _INCLUDE_SORTIX_MEMUSAGE_H +#define _INCLUDE_SORTIX_MEMUSAGE_H + +#define MEMUSAGE_TOTAL 0 +#define MEMUSAGE_USED 1 + +#define MEMUSAGE_PURPOSE_FIRST 16 +#define MEMUSAGE_PURPOSE_PHYSICAL (MEMUSAGE_PURPOSE_FIRST + 0) +#define MEMUSAGE_PURPOSE_PAGING (MEMUSAGE_PURPOSE_FIRST + 1) +#define MEMUSAGE_PURPOSE_KERNEL (MEMUSAGE_PURPOSE_FIRST + 2) +#define MEMUSAGE_PURPOSE_FILESYSTEM (MEMUSAGE_PURPOSE_FIRST + 3) +#define MEMUSAGE_PURPOSE_USERSPACE (MEMUSAGE_PURPOSE_FIRST + 4) +#define MEMUSAGE_PURPOSE_EXECVE (MEMUSAGE_PURPOSE_FIRST + 5) +#define MEMUSAGE_PURPOSE_DRIVER (MEMUSAGE_PURPOSE_FIRST + 6) +#define MEMUSAGE_PURPOSE_NETWORK (MEMUSAGE_PURPOSE_FIRST + 7) +#define MEMUSAGE_PURPOSE_LAST MEMUSAGE_PURPOSE_NETWORK + +#endif diff --git a/kernel/include/sortix/syscall.h b/kernel/include/sortix/syscall.h index 842a576b..900397e9 100644 --- a/kernel/include/sortix/syscall.h +++ b/kernel/include/sortix/syscall.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2016, 2021 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2021, 2022 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -189,6 +189,7 @@ #define SYSCALL_GETDNSCONFIG 166 #define SYSCALL_SETDNSCONFIG 167 #define SYSCALL_FUTEX 168 -#define SYSCALL_MAX_NUM 169 /* index of highest constant + 1 */ +#define SYSCALL_MEMUSAGE 169 +#define SYSCALL_MAX_NUM 170 /* index of highest constant + 1 */ #endif diff --git a/kernel/memorymanagement.cpp b/kernel/memorymanagement.cpp index 6fe51192..827c52da 100644 --- a/kernel/memorymanagement.cpp +++ b/kernel/memorymanagement.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2011, 2012, 2013, 2015, 2022, 2023 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -39,11 +40,13 @@ namespace Sortix { +// TODO: After releasing Sortix 1.1, remove this deprecated system call retained +// for backwards ABI compatibility with Sortix 1.0's xz port. int sys_memstat(size_t* memused, size_t* memtotal) { size_t used; size_t total; - Memory::Statistics(&used, &total); + Memory::Statistics(&used, &total, NULL); if ( memused && !CopyToUser(memused, &used, sizeof(used)) ) return -1; if ( memtotal && !CopyToUser(memtotal, &total, sizeof(total)) ) @@ -51,6 +54,47 @@ int sys_memstat(size_t* memused, size_t* memtotal) return 0; } +int sys_memusage(const size_t* user_statistics, + size_t* user_values, + size_t length) +{ + size_t used; + size_t total; + size_t purposes[PAGE_USAGE_NUM_KINDS]; + Memory::Statistics(&used, &total, purposes); + for ( size_t start = 0; start < length; ) + { + const size_t BLOCK = 32; + size_t statistics[BLOCK]; + size_t amount = length - start; + if ( BLOCK < amount ) + amount = BLOCK; + if ( !CopyFromUser(statistics, user_statistics + start, + amount * sizeof(size_t)) ) + return -1; + for ( size_t i = 0; i < amount; i++ ) + { + size_t statistic = statistics[i]; + size_t value; + if ( statistic == MEMUSAGE_TOTAL ) + value = total; + else if ( statistic == MEMUSAGE_USED ) + value = used; + else if ( MEMUSAGE_PURPOSE_FIRST <= statistic && + statistic <= MEMUSAGE_PURPOSE_LAST ) + value = purposes[statistic - MEMUSAGE_PURPOSE_FIRST]; + else + return errno = EINVAL, -1; + statistics[i] = value; + } + if ( !CopyToUser(user_values + start, statistics, + amount * sizeof(size_t)) ) + return -1; + start += amount; + } + return 0; +} + } // namespace Sortix namespace Sortix { diff --git a/kernel/net/packet.cpp b/kernel/net/packet.cpp index a1697def..b968d8fe 100644 --- a/kernel/net/packet.cpp +++ b/kernel/net/packet.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2015 Meisaka Yukara. - * Copyright (c) 2016, 2017 Jonas 'Sortie' Termansen. + * Copyright (c) 2016, 2017, 2022 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -84,7 +84,7 @@ Ref GetPacket() else { size_t total_memory; - Memory::Statistics(NULL, &total_memory); + Memory::Statistics(NULL, &total_memory, NULL); size_t total_pages = total_memory / Page::Size(); size_t max_packets = total_pages / MAX_PACKET_FRACTION; if ( max_packets <= packet_count ) diff --git a/kernel/process.cpp b/kernel/process.cpp index 601745a3..99e641c9 100644 --- a/kernel/process.cpp +++ b/kernel/process.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2016, 2021 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2021-2022 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -1165,7 +1165,7 @@ static bool sys_execve_alloc(addralloc_t* alloc, size_t size) { if ( !AllocateKernelAddress(alloc, size) ) return false; - if ( !Memory::MapRange(alloc->from, alloc->size, PROT_KREAD | PROT_KWRITE, PAGE_USAGE_EXECUTE) ) + if ( !Memory::MapRange(alloc->from, alloc->size, PROT_KREAD | PROT_KWRITE, PAGE_USAGE_EXECVE) ) return FreeKernelAddress(alloc), false; Memory::Flush(); return true; @@ -1173,7 +1173,7 @@ static bool sys_execve_alloc(addralloc_t* alloc, size_t size) static void sys_execve_free(addralloc_t* alloc) { - Memory::UnmapRange(alloc->from, alloc->size, PAGE_USAGE_EXECUTE); + Memory::UnmapRange(alloc->from, alloc->size, PAGE_USAGE_EXECVE); Memory::Flush(); FreeKernelAddress(alloc); } diff --git a/kernel/syscall.cpp b/kernel/syscall.cpp index d76f73ba..a7dcfed2 100644 --- a/kernel/syscall.cpp +++ b/kernel/syscall.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2016, 2021 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2021-2022 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -203,6 +203,7 @@ void* syscall_list[SYSCALL_MAX_NUM + 1] = [SYSCALL_GETDNSCONFIG] = (void*) sys_getdnsconfig, [SYSCALL_SETDNSCONFIG] = (void*) sys_setdnsconfig, [SYSCALL_FUTEX] = (void*) sys_futex, + [SYSCALL_MEMUSAGE] = (void*) sys_memusage, [SYSCALL_MAX_NUM] = (void*) sys_bad_syscall, }; } /* extern "C" */ diff --git a/kernel/x86-family/memorymanagement.cpp b/kernel/x86-family/memorymanagement.cpp index 72666370..5d0c57c6 100644 --- a/kernel/x86-family/memorymanagement.cpp +++ b/kernel/x86-family/memorymanagement.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2014, 2015, 2017 Jonas 'Sortie' Termansen. + * Copyright (c) 2011, 2012, 2014, 2015, 2017, 2022 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -220,14 +220,20 @@ void Init(multiboot_info_t* bootinfo) } } -void Statistics(size_t* amountused, size_t* totalmem) +void Statistics(size_t* used, size_t* total, size_t* purposes) { + ScopedLock lock(&Page::pagelock); size_t memfree = (Page::stackused - Page::stackreserved) << 12UL; size_t memused = Page::totalmem - memfree; - if ( amountused ) - *amountused = memused; - if ( totalmem ) - *totalmem = Page::totalmem; + if ( used ) + *used = memused; + if ( total ) + *total = Page::totalmem; + if ( purposes ) + { + for ( size_t i = 0; i < PAGE_USAGE_NUM_KINDS; i++ ) + purposes[i] = Page::page_usage_counts[i] << 12UL; + } } } // namespace Memory diff --git a/libc/Makefile b/libc/Makefile index 2dcce012..6fde6d8e 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -393,6 +393,7 @@ langinfo/nl_langinfo_l.o \ langinfo/nl_langinfo.o \ locale/localeconv.o \ locale/setlocale.o \ +memusage/memusage.o \ msr/rdmsr.o \ msr/wrmsr.o \ netdb/endnetent.o \ diff --git a/libc/include/memusage.h b/libc/include/memusage.h new file mode 100644 index 00000000..3b0de197 --- /dev/null +++ b/libc/include/memusage.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022, 2023 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * memusage.h + * Memory usage statistics. + */ + +#ifndef _INCLUDE_MEMUSAGE_H +#define _INCLUDE_MEMUSAGE_H + +#include + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int memusage(const size_t*, size_t*, size_t); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/libc/memusage/memusage.c b/libc/memusage/memusage.c new file mode 100644 index 00000000..1bc708d1 --- /dev/null +++ b/libc/memusage/memusage.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022, 2023 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * memsage/memusage.c + * Memory usage statistics. + */ + +#include + +#include + +DEFN_SYSCALL3(int, sys_memusage, SYSCALL_MEMUSAGE, const size_t*, size_t*, + size_t); + +int memusage(const size_t* statistics, size_t* values, size_t length) +{ + return sys_memusage(statistics, values, length); +} diff --git a/ports/xz/xz.patch b/ports/xz/xz.patch index 4887d3cc..d102249a 100644 --- a/ports/xz/xz.patch +++ b/ports/xz/xz.patch @@ -1,6 +1,60 @@ diff -Paur --no-dereference -- xz.upstream/configure xz/configure --- xz.upstream/configure +++ xz/configure +@@ -16829,7 +16829,7 @@ + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then +- INCICONV="${INCICONV}${INCICONV:+ }-I$additional_includedir" ++ INCICONV="${INCICONV}${INCICONV:+ }" + fi + fi + fi +@@ -16876,7 +16876,7 @@ + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then +- LIBICONV="${LIBICONV}${LIBICONV:+ }-L$additional_libdir" ++ LIBICONV="${LIBICONV}${LIBICONV:+ }" + fi + fi + haveit= +@@ -16897,7 +16897,7 @@ + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then +- LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$additional_libdir" ++ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }" + fi + fi + fi +@@ -17772,7 +17772,7 @@ + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then +- INCINTL="${INCINTL}${INCINTL:+ }-I$additional_includedir" ++ INCINTL="${INCINTL}${INCINTL:+ }" + fi + fi + fi +@@ -17819,7 +17819,7 @@ + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then +- LIBINTL="${LIBINTL}${LIBINTL:+ }-L$additional_libdir" ++ LIBINTL="${LIBINTL}${LIBINTL:+ }" + fi + fi + haveit= +@@ -17840,7 +17840,7 @@ + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then +- LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$additional_libdir" ++ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }" + fi + fi + fi @@ -19269,6 +19269,7 @@ #if defined(_WIN32) || defined(__CYGWIN__) || defined(__OS2__) \ @@ -19,7 +73,7 @@ diff -Paur --no-dereference -- xz.upstream/src/common/tuklib_physmem.c xz/src/co + +// Sortix +#elif defined(__sortix__) -+# include ++# include + #endif @@ -29,9 +83,9 @@ diff -Paur --no-dereference -- xz.upstream/src/common/tuklib_physmem.c xz/src/co if (sysinfo(&si) == 0) ret = (uint64_t)si.totalram * si.mem_unit; +#elif defined(__sortix__) -+ size_t ret_size_t; -+ memstat(NULL, &ret_size_t); -+ ret = ret_size_t; ++ size_t statistic = MEMUSAGE_TOTAL; ++ memusage(&statistic, &statistic, 1); ++ ret = statistic; #endif return ret; diff --git a/share/man/man7/following-development.7 b/share/man/man7/following-development.7 index b51e19cb..3c78b1f8 100644 --- a/share/man/man7/following-development.7 +++ b/share/man/man7/following-development.7 @@ -69,6 +69,11 @@ releasing Sortix x.y, foo." to allow the maintainer to easily .Xr grep 1 for it after a release. .Sh CHANGES +.Ss Add memusage(2) +The +.Xr memusage 2 +system call has been added, which provides detailed system memory statistics. +This is a minor compatible ABI change. .Ss Add networking stack The network stack has been implemented in the kernel and exposed through additions to the system call interface. diff --git a/utils/memstat.1 b/utils/memstat.1 index 4a95945d..c966566e 100644 --- a/utils/memstat.1 +++ b/utils/memstat.1 @@ -1,19 +1,94 @@ -.Dd October 6, 2016 +.Dd March 8, 2023 .Dt MEMSTAT 1 .Os .Sh NAME .Nm memstat -.Nd print system memory usage information +.Nd system memory statistics .Sh SYNOPSIS .Nm +.Op Fl abegkmprt +.Op Ar statistic ... .Sh DESCRIPTION .Nm -prints the amount of memory in use, the total amount of memory, and the -percentage of memory used. +writes the requested system memory +.Ar statistics , +or the +.Sy total +system memory and +.Sy used +system memory by default. +Each statistic is written as a line with three columns aligned with spaces, +where the first column is the human readable value, the second column is the +name of the statistic, and the third column is how many percent the value is out +of total system memory. +.Pp +The options are as follows: +.Bl -tag -width "12345678" +.It Fl a +Write all statistics. +.It Fl b +Format values as bytes. +.It Fl e +Format values as exabytes (1024^6 bytes). +.It Fl g +Format values as gigabytes (1024^3 bytes). +.It Fl k +Format values as kilobytes (1024 bytes). +.It Fl m +Format values as megabytes (1024^2 bytes). +.It Fl p +Format values as petabytes (1024^5 bytes). +.It Fl r +Write statistics in the raw machine readable format consisting of the value in +the requested unit (bytes by default) with no unit suffix followed by a space +and then the statistic name. +.It Fl t +Format values as terabytes (1024^4 bytes). +.El +.Pp +The statistics are as follows: +.Pp +.Bl -tag -width filesystem -compact +.It Sy total +amount of total memory. +.It Sy used +amount of memory currently used for any purpose. +.It Sy userspace +amount of memory purposed for normal user-space pages. +.It Sy kernel +amount of memory purposed for normal kernel-space pages. +.It Sy filesystem +amount of memory purposed for kernel filesystem buffers. +.It Sy network +amount of memory purposed for kernel network buffers. +.It Sy paging +amount of memory purposed for paging overhead. +.It Sy driver +amount of memory purposed for kernel driver buffers. +.It Sy physical +amount of memory purposed for keep track of unused physical memory. +.It Sy execve +amount of memory purposed for the +.Xr execve 2 +system call. +.El +.Pp +.Nm +measures an instantaneous consistent snapshot of all the system memory +statistics using +.Xr memusage 2 . +Each page of memory is used for exactly one purpose and the purpose statistics +add up to the +.Sy used +statistic. .Sh EXIT STATUS .Nm will exit 0 on success and non-zero otherwise. .Sh SEE ALSO .Xr ps 1 , .Xr pstree 1 , -.Xr memstat 2 +.Xr memusage 2 +.Sh HISTORY +.Nm +originally appeared in Sortix 0.5. +The output was changed to the current table format in Sortix 1.1. diff --git a/utils/memstat.c b/utils/memstat.c index 4263333d..76854781 100644 --- a/utils/memstat.c +++ b/utils/memstat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Jonas 'Sortie' Termansen. + * Copyright (c) 2011, 2022, 2023 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,13 +14,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * memstat.c - * Prints system memory usage information. + * System memory statistics. */ #include +#include +#include #include -#include +#include #include +#include #define BYTES 0 #define KIBI 1 @@ -30,49 +33,173 @@ #define PEBI 5 #define EXBI 6 -void printbytes(unsigned long long bytes) +static char* format_bytes_amount(uintmax_t num_bytes, int unit, bool raw) { - unsigned unit = BYTES; - if ( (bytes >> 10ULL) & 1023ULL ) { unit = KIBI; } - if ( (bytes >> 20ULL) & 1023ULL ) { unit = MEBI; } - if ( (bytes >> 30ULL) & 1023ULL ) { unit = GIBI; } - if ( (bytes >> 40ULL) & 1023ULL ) { unit = TEBI; } - if ( (bytes >> 50ULL) & 1023ULL ) { unit = PEBI; } - if ( (bytes >> 60ULL) & 1023ULL ) { unit = EXBI; } - - switch ( unit ) + uintmax_t value = num_bytes; + uintmax_t value_fraction = 0; + uintmax_t exponent = 1024; + char suffixes[] = { 'B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' }; + size_t num_suffixes = sizeof(suffixes) / sizeof(suffixes[0]); + size_t suffix_index = 0; + while ( (unit < 0 ? exponent <= value : (int) suffix_index < unit) && + suffix_index + 1 < num_suffixes ) { - case EXBI: - printf("%llu ZiB ", (bytes >> 60ULL) & 1023ULL); - case PEBI: - printf("%llu PiB ", (bytes >> 50ULL) & 1023ULL); - case TEBI: - printf("%llu TiB ", (bytes >> 40ULL) & 1023ULL); - case GIBI: - printf("%llu GiB ", (bytes >> 30ULL) & 1023ULL); - case MEBI: - printf("%llu MiB ", (bytes >> 20ULL) & 1023ULL); - case KIBI: - printf("%llu KiB", (bytes >> 10ULL) & 1023ULL); - break; - case BYTES: - printf("%llu B", (bytes >> 0ULL) & 1023ULL); + value_fraction = value % exponent; + value /= exponent; + suffix_index++; } + char suffix_char = raw ? 0 : suffixes[suffix_index]; + char value_fraction_char = '0' + (value_fraction / (1024 / 10 + 1)) % 10; + char decimals[3] = {suffix_index ? '.' : 0, value_fraction_char, 0}; + char* result; + if ( asprintf(&result, "%ju%s%c", value, decimals, suffix_char) < 0 ) + return NULL; + return result; } -int main(void) +struct memusage { - size_t memused = 0; - size_t memtotal = 0; - if ( memstat(&memused, &memtotal) ) - err(1, "memstat"); + size_t counter; + const char* name; +}; - printf("memory usage: "); - printbytes(memused); - printf(" used / "); - printbytes(memtotal); - unsigned percent = ((unsigned long long) memused * 100ULL ) / memtotal; - printf(" total (%u%s)\n", percent, "%"); +static const struct memusage memusages[] = +{ + {MEMUSAGE_TOTAL, "total"}, + {MEMUSAGE_USED, "used"}, + {MEMUSAGE_PURPOSE_USERSPACE, "userspace"}, + {MEMUSAGE_PURPOSE_KERNEL, "kernel"}, + {MEMUSAGE_PURPOSE_FILESYSTEM, "filesystem"}, + {MEMUSAGE_PURPOSE_NETWORK, "network"}, + {MEMUSAGE_PURPOSE_PAGING, "paging"}, + {MEMUSAGE_PURPOSE_DRIVER, "driver"}, + {MEMUSAGE_PURPOSE_PHYSICAL, "physical"}, + {MEMUSAGE_PURPOSE_EXECVE, "execve"}, +}; + +int main(int argc, char* argv[]) +{ + bool all = false; + bool raw = false; + int unit = -1; + + int opt; + while ( (opt = getopt(argc, argv, "abegkmprt")) != -1 ) + { + switch ( opt ) + { + case 'a': all = true; break; + case 'b': unit = BYTES; break; + case 'e': unit = EXBI; break; + case 'g': unit = GIBI; break; + case 'k': unit = KIBI; break; + case 'm': unit = MEBI; break; + case 'p': unit = PEBI; break; + case 'r': raw = true; break; + case 't': unit = TEBI; break; + default: return 1; + } + } + + const size_t MAX_COUNTERS = sizeof(memusages) / sizeof(memusages[0]); + const struct memusage* usages[MAX_COUNTERS]; + size_t num_counters; + size_t start_counter = 0; + + if ( all ) + { + if ( optind < argc ) + errx(1, "extra operand: %s", argv[optind]); + num_counters = MAX_COUNTERS; + for ( size_t i = 0; i < num_counters; i++ ) + usages[i] = &memusages[i]; + } + else if ( optind < argc ) + { + num_counters = 1; + start_counter = 1; + usages[0] = &memusages[0]; + for ( int i = optind; i < argc; i++ ) + { + if ( num_counters == MAX_COUNTERS ) + errx(1, "too many counters"); + bool found = false; + for ( size_t n = 0; n < MAX_COUNTERS; n++ ) + { + if ( strcmp(argv[i], memusages[n].name) != 0 ) + continue; + usages[num_counters++] = &memusages[n]; + found = true; + break; + } + if ( !found ) + errx(1, "unknown statistic: %s", argv[i]); + } + } + else + { + num_counters = 2; + usages[0] = &memusages[0]; + usages[1] = &memusages[1]; + } + + size_t counts[MAX_COUNTERS]; + for ( size_t i = 0; i < num_counters; i++ ) + counts[i] = usages[i]->counter; + + size_t values[MAX_COUNTERS]; + if ( memusage(counts, values, num_counters) ) + err(1, "memusage"); + + if ( raw && unit == -1 ) + unit = BYTES; + + size_t total = values[0]; + + size_t usage_width = 0; + size_t name_width = 0; + for ( size_t i = start_counter; i < num_counters; i++ ) + { + size_t count = values[i]; + const char* name = usages[i]->name; + char* usage = format_bytes_amount(count, unit, raw); + if ( !usage ) + err(1, "malloc"); + size_t usage_len = strlen(usage); + if ( usage_width < usage_len ) + usage_width = usage_len; + size_t name_len = strlen(name); + if ( name_width < name_len ) + name_width = name_len; + free(usage); + } + + for ( size_t i = start_counter; i < num_counters; i++ ) + { + size_t count = values[i]; + const char* name = usages[i]->name; + char* usage = format_bytes_amount(count, unit, raw); + if ( !usage ) + err(1, "malloc"); + if ( raw ) + { + if ( num_counters - start_counter == 1 ) + printf("%s\n", usage); + else + printf("%s %s\n", usage, name); + } + else + { + printf("%*s", (int) usage_width, usage); + printf(" %-*s", (int) name_width, name); + unsigned percent = ((uintmax_t) count * 100) / total; + printf(" %3u%%\n", percent); + } + free(usage); + } + + if ( ferror(stdout) || fflush(stdout) == EOF ) + err(1, "stdout"); return 0; }