Add memusage(2).

Switch xz to memusage(2) and fix native self-cross issue.

This is a compatible ABI change.
This commit is contained in:
Jonas 'Sortie' Termansen 2023-03-08 22:20:59 +01:00
parent 7963da689d
commit 2cd7361294
18 changed files with 492 additions and 72 deletions

View File

@ -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"

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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 <stdlib.h>
#include <string.h>
#include <sortix/memusage.h>
#include <sortix/mman.h>
#include <sortix/seek.h>
@ -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 {

View File

@ -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<Packet> 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 )

View File

@ -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);
}

View File

@ -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" */

View File

@ -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

View File

@ -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 \

39
libc/include/memusage.h Normal file
View File

@ -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 <sys/cdefs.h>
#include <stddef.h>
#include <sortix/memusage.h>
#ifdef __cplusplus
extern "C" {
#endif
int memusage(const size_t*, size_t*, size_t);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

30
libc/memusage/memusage.c Normal file
View File

@ -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 <sys/syscall.h>
#include <memusage.h>
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);
}

View File

@ -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 <unistd.h>
+# include <memusage.h>
+
#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;

View File

@ -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.

View File

@ -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.

View File

@ -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 <err.h>
#include <memusage.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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;
}