mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-25 07:19:02 +00:00
Introduce libc/mem/tinymalloc.inc
This allocator shaves ~20kb off single-threaded tool programs and is slightly faster than proper malloc for simple non-demanding programs
This commit is contained in:
parent
3bf95ae7ec
commit
a6ecbb747d
17 changed files with 201 additions and 37 deletions
|
@ -8,6 +8,7 @@ LIBC_MEM = $(LIBC_MEM_A_DEPS) $(LIBC_MEM_A)
|
||||||
LIBC_MEM_A = o/$(MODE)/libc/mem/mem.a
|
LIBC_MEM_A = o/$(MODE)/libc/mem/mem.a
|
||||||
LIBC_MEM_A_FILES := $(wildcard libc/mem/*)
|
LIBC_MEM_A_FILES := $(wildcard libc/mem/*)
|
||||||
LIBC_MEM_A_HDRS = $(filter %.h,$(LIBC_MEM_A_FILES))
|
LIBC_MEM_A_HDRS = $(filter %.h,$(LIBC_MEM_A_FILES))
|
||||||
|
LIBC_MEM_A_INCS = $(filter %.inc,$(LIBC_MEM_A_FILES))
|
||||||
LIBC_MEM_A_SRCS = $(filter %.c,$(LIBC_MEM_A_FILES))
|
LIBC_MEM_A_SRCS = $(filter %.c,$(LIBC_MEM_A_FILES))
|
||||||
LIBC_MEM_A_OBJS = $(LIBC_MEM_A_SRCS:%.c=o/$(MODE)/%.o)
|
LIBC_MEM_A_OBJS = $(LIBC_MEM_A_SRCS:%.c=o/$(MODE)/%.o)
|
||||||
|
|
||||||
|
@ -46,6 +47,7 @@ $(LIBC_MEM_A_OBJS): private \
|
||||||
LIBC_MEM_LIBS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)))
|
LIBC_MEM_LIBS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)))
|
||||||
LIBC_MEM_SRCS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_SRCS))
|
LIBC_MEM_SRCS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_SRCS))
|
||||||
LIBC_MEM_HDRS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_HDRS))
|
LIBC_MEM_HDRS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_HDRS))
|
||||||
|
LIBC_MEM_INCS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_INCS))
|
||||||
LIBC_MEM_BINS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_BINS))
|
LIBC_MEM_BINS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_BINS))
|
||||||
LIBC_MEM_CHECKS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_CHECKS))
|
LIBC_MEM_CHECKS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_CHECKS))
|
||||||
LIBC_MEM_OBJS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_OBJS))
|
LIBC_MEM_OBJS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_OBJS))
|
||||||
|
|
160
libc/mem/tinymalloc.inc
Normal file
160
libc/mem/tinymalloc.inc
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
// Copyright 2024 Justine Alexandra Roberts Tunney
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or 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.
|
||||||
|
|
||||||
|
#include "libc/assert.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
|
#include "libc/stdckdint.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
|
||||||
|
#ifndef MODE_DBG /* don't interfere with asan dlmalloc hooking */
|
||||||
|
|
||||||
|
_Alignas(65536) static struct {
|
||||||
|
char memory[1024 * 1024 * 1024];
|
||||||
|
unsigned used, last, free;
|
||||||
|
} heap;
|
||||||
|
|
||||||
|
static inline bool isheap(char *mem) {
|
||||||
|
return heap.memory <= mem && mem < heap.memory + heap.used;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void *ptr) {
|
||||||
|
char *mem;
|
||||||
|
unsigned base;
|
||||||
|
if (ptr) {
|
||||||
|
mem = (char *)ptr;
|
||||||
|
unassert(isheap(mem));
|
||||||
|
base = mem - heap.memory;
|
||||||
|
*(unsigned *)mem = heap.free;
|
||||||
|
heap.free = base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t malloc_usable_size(void *ptr) {
|
||||||
|
char *mem = (char *)ptr;
|
||||||
|
unassert(isheap(mem));
|
||||||
|
return ((unsigned *)mem)[-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memalign(size_t align, size_t need) {
|
||||||
|
char *res;
|
||||||
|
unsigned next, next2, base, toto, *link, *link2;
|
||||||
|
|
||||||
|
// normalize arguments
|
||||||
|
while (align & (align - 1))
|
||||||
|
++align;
|
||||||
|
if (need < sizeof(unsigned))
|
||||||
|
need = sizeof(unsigned);
|
||||||
|
if (align < sizeof(unsigned))
|
||||||
|
align = sizeof(unsigned);
|
||||||
|
if (align > 65536)
|
||||||
|
goto InvalidArgument;
|
||||||
|
if (ckd_add(&need, need, sizeof(unsigned) - 1))
|
||||||
|
goto OutOfMemory;
|
||||||
|
need &= -sizeof(unsigned);
|
||||||
|
|
||||||
|
// allocate from free list
|
||||||
|
next = heap.free;
|
||||||
|
link = &heap.free;
|
||||||
|
while (next) {
|
||||||
|
next2 = *(unsigned *)(heap.memory + next);
|
||||||
|
link2 = (unsigned *)(heap.memory + next);
|
||||||
|
if (need <= ((unsigned *)(heap.memory + next))[-1]) {
|
||||||
|
*link = next2;
|
||||||
|
return (void *)(heap.memory + next);
|
||||||
|
}
|
||||||
|
next = next2;
|
||||||
|
link = link2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate new static memory
|
||||||
|
base = heap.used;
|
||||||
|
base += sizeof(unsigned);
|
||||||
|
base += align - 1;
|
||||||
|
base &= -align;
|
||||||
|
if (ckd_add(&toto, base, need))
|
||||||
|
goto OutOfMemory;
|
||||||
|
if (toto > sizeof(heap.memory))
|
||||||
|
goto OutOfMemory;
|
||||||
|
res = heap.memory + base;
|
||||||
|
((unsigned *)res)[-1] = need;
|
||||||
|
heap.used = toto;
|
||||||
|
heap.last = base;
|
||||||
|
return res;
|
||||||
|
|
||||||
|
// we require more vespene gas
|
||||||
|
OutOfMemory:
|
||||||
|
errno = ENOMEM;
|
||||||
|
return 0;
|
||||||
|
InvalidArgument:
|
||||||
|
errno = EINVAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *malloc(size_t need) {
|
||||||
|
return memalign(sizeof(max_align_t), need);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *calloc(size_t count, size_t size) {
|
||||||
|
char *res;
|
||||||
|
unsigned need, used;
|
||||||
|
if (ckd_mul(&need, count, size))
|
||||||
|
need = -1;
|
||||||
|
used = heap.used;
|
||||||
|
if ((res = (char *)malloc(need)))
|
||||||
|
if (res - heap.memory < used)
|
||||||
|
bzero(res, need);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *realloc(void *ptr, size_t need) {
|
||||||
|
char *res, *mem;
|
||||||
|
unsigned base, have, toto;
|
||||||
|
if (!ptr) {
|
||||||
|
res = (char *)malloc(need);
|
||||||
|
} else {
|
||||||
|
mem = (char *)ptr;
|
||||||
|
unassert(isheap(mem));
|
||||||
|
have = ((unsigned *)mem)[-1];
|
||||||
|
base = mem - heap.memory;
|
||||||
|
if (need < have) {
|
||||||
|
res = mem;
|
||||||
|
} else if (base == heap.last) {
|
||||||
|
if (need < sizeof(unsigned))
|
||||||
|
need = sizeof(unsigned);
|
||||||
|
if (ckd_add(&need, need, sizeof(unsigned) - 1))
|
||||||
|
goto OutOfMemory;
|
||||||
|
need &= -sizeof(unsigned);
|
||||||
|
if (ckd_add(&toto, base, need))
|
||||||
|
goto OutOfMemory;
|
||||||
|
if (toto > sizeof(heap.memory))
|
||||||
|
goto OutOfMemory;
|
||||||
|
((unsigned *)mem)[-1] = need;
|
||||||
|
heap.used = toto;
|
||||||
|
res = mem;
|
||||||
|
} else if ((res = (char *)malloc(need))) {
|
||||||
|
if (have > need)
|
||||||
|
have = need;
|
||||||
|
memcpy(res, mem, have);
|
||||||
|
free(mem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
OutOfMemory:
|
||||||
|
errno = ENOMEM;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* MODE_DBG */
|
|
@ -259,6 +259,8 @@ static Elf64_Xword notesize;
|
||||||
|
|
||||||
static char *r_off32_e_lfanew;
|
static char *r_off32_e_lfanew;
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
static wontreturn void Die(const char *thing, const char *reason) {
|
static wontreturn void Die(const char *thing, const char *reason) {
|
||||||
tinyprint(2, thing, ": ", reason, "\n", NULL);
|
tinyprint(2, thing, ": ", reason, "\n", NULL);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
|
@ -67,6 +67,8 @@
|
||||||
#define FORMAT_MACHO 2
|
#define FORMAT_MACHO 2
|
||||||
#define FORMAT_PE 3
|
#define FORMAT_PE 3
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
static int g_arch;
|
static int g_arch;
|
||||||
static int g_format;
|
static int g_format;
|
||||||
static bool g_force;
|
static bool g_force;
|
||||||
|
|
|
@ -226,6 +226,8 @@ const char *const kSafeEnv[] = {
|
||||||
"SYSTEMROOT", // needed by socket()
|
"SYSTEMROOT", // needed by socket()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
void OnAlrm(int sig) {
|
void OnAlrm(int sig) {
|
||||||
++gotalrm;
|
++gotalrm;
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,8 @@ char linkbuf[PATH_MAX];
|
||||||
|
|
||||||
void Cp(char *, char *);
|
void Cp(char *, char *);
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
bool IsDirectory(const char *path) {
|
bool IsDirectory(const char *path) {
|
||||||
int e;
|
int e;
|
||||||
bool res;
|
bool res;
|
||||||
|
|
|
@ -158,6 +158,8 @@ static const char *stubpath;
|
||||||
static long FLAG_SizeOfStackCommit = 64 * 1024;
|
static long FLAG_SizeOfStackCommit = 64 * 1024;
|
||||||
static long FLAG_SizeOfStackReserve = 8 * 1024 * 1024;
|
static long FLAG_SizeOfStackReserve = 8 * 1024 * 1024;
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
static wontreturn void Die(const char *thing, const char *reason) {
|
static wontreturn void Die(const char *thing, const char *reason) {
|
||||||
tinyprint(2, thing, ": ", reason, "\n", NULL);
|
tinyprint(2, thing, ": ", reason, "\n", NULL);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
|
@ -67,6 +67,8 @@ static Elf64_Ehdr *elf;
|
||||||
static const char *epath;
|
static const char *epath;
|
||||||
static Elf64_Xword symcount;
|
static Elf64_Xword symcount;
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
static wontreturn void Die(const char *reason) {
|
static wontreturn void Die(const char *reason) {
|
||||||
tinyprint(2, epath, ": ", reason, "\n", NULL);
|
tinyprint(2, epath, ": ", reason, "\n", NULL);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
|
@ -71,6 +71,8 @@ const char *prog;
|
||||||
char databuf[32768];
|
char databuf[32768];
|
||||||
char pathbuf[PATH_MAX];
|
char pathbuf[PATH_MAX];
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
wontreturn void PrintUsage(int rc, FILE *f) {
|
wontreturn void PrintUsage(int rc, FILE *f) {
|
||||||
fputs("usage: ", f);
|
fputs("usage: ", f);
|
||||||
fputs(prog, f);
|
fputs(prog, f);
|
||||||
|
|
|
@ -50,6 +50,8 @@ static const char *prog;
|
||||||
static char16_t **filters;
|
static char16_t **filters;
|
||||||
static uint32_t pids[10000];
|
static uint32_t pids[10000];
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
static wontreturn void PrintUsage(int rc, FILE *f) {
|
static wontreturn void PrintUsage(int rc, FILE *f) {
|
||||||
fprintf(f,
|
fprintf(f,
|
||||||
"Usage: %s [-nshv] NAME...\n"
|
"Usage: %s [-nshv] NAME...\n"
|
||||||
|
|
|
@ -146,6 +146,8 @@ static const char *buildroot;
|
||||||
static const char *genroot;
|
static const char *genroot;
|
||||||
static const char *outpath;
|
static const char *outpath;
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
static inline bool IsBlank(int c) {
|
static inline bool IsBlank(int c) {
|
||||||
return c == ' ' || c == '\t';
|
return c == ' ' || c == '\t';
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,8 @@ char linkbuf[PATH_MAX];
|
||||||
|
|
||||||
void Mv(char *, char *);
|
void Mv(char *, char *);
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
wontreturn void Die(const char *path, const char *reason) {
|
wontreturn void Die(const char *path, const char *reason) {
|
||||||
tinyprint(2, path, ": ", reason, "\n", NULL);
|
tinyprint(2, path, ": ", reason, "\n", NULL);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
|
@ -43,8 +43,6 @@
|
||||||
#include "third_party/xed/x86.h"
|
#include "third_party/xed/x86.h"
|
||||||
#include "tool/build/lib/getargs.h"
|
#include "tool/build/lib/getargs.h"
|
||||||
|
|
||||||
__static_yoink("realloc");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview Build Package Script.
|
* @fileoverview Build Package Script.
|
||||||
*
|
*
|
||||||
|
@ -153,6 +151,8 @@ struct Relas {
|
||||||
} *p;
|
} *p;
|
||||||
} prtu;
|
} prtu;
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
static wontreturn void Die(const char *path, const char *reason) {
|
static wontreturn void Die(const char *path, const char *reason) {
|
||||||
tinyprint(2, path, ": ", reason, "\n", NULL);
|
tinyprint(2, path, ": ", reason, "\n", NULL);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/serialize.h"
|
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/nt/struct/imageimportbyname.internal.h"
|
#include "libc/nt/struct/imageimportbyname.internal.h"
|
||||||
#include "libc/nt/struct/imageimportdescriptor.internal.h"
|
#include "libc/nt/struct/imageimportdescriptor.internal.h"
|
||||||
|
@ -25,6 +24,7 @@
|
||||||
#include "libc/nt/struct/imageoptionalheader.internal.h"
|
#include "libc/nt/struct/imageoptionalheader.internal.h"
|
||||||
#include "libc/nt/struct/imagesectionheader.internal.h"
|
#include "libc/nt/struct/imagesectionheader.internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/serialize.h"
|
||||||
#include "libc/stdckdint.h"
|
#include "libc/stdckdint.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "libc/elf/struct/shdr.h"
|
#include "libc/elf/struct/shdr.h"
|
||||||
#include "libc/elf/struct/sym.h"
|
#include "libc/elf/struct/sym.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/serialize.h"
|
#include "libc/serialize.h"
|
||||||
#include "libc/stdio/sysparam.h"
|
#include "libc/stdio/sysparam.h"
|
||||||
|
@ -32,6 +33,8 @@ const char *FLAG_prefix;
|
||||||
const char *FLAG_suffix;
|
const char *FLAG_suffix;
|
||||||
const char *path;
|
const char *path;
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
wontreturn void PrintUsage(int fd, int exitcode) {
|
wontreturn void PrintUsage(int fd, int exitcode) {
|
||||||
tinyprint(fd, "\n\
|
tinyprint(fd, "\n\
|
||||||
NAME\n\
|
NAME\n\
|
||||||
|
@ -74,42 +77,17 @@ wontreturn void DieOom(void) {
|
||||||
Die("out of memory");
|
Die("out of memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct {
|
static void *Malloc(size_t n) {
|
||||||
char *last;
|
void *p;
|
||||||
size_t used;
|
if (!(p = malloc(n)))
|
||||||
union {
|
|
||||||
char memory[1024 * 1024 * 1024];
|
|
||||||
size_t align;
|
|
||||||
};
|
|
||||||
} heap;
|
|
||||||
|
|
||||||
void *Malloc(size_t need) {
|
|
||||||
if (need <= sizeof(heap.memory)) {
|
|
||||||
int align = sizeof(size_t);
|
|
||||||
size_t base = heap.used;
|
|
||||||
base += align - 1;
|
|
||||||
base &= -align;
|
|
||||||
size_t toto = base + sizeof(size_t) + need;
|
|
||||||
if (toto >= heap.used && toto <= sizeof(heap.memory)) {
|
|
||||||
char *res = heap.memory + base;
|
|
||||||
*(size_t *)res = need;
|
|
||||||
heap.used = toto;
|
|
||||||
return res + sizeof(size_t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DieOom();
|
DieOom();
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *Realloc(void *ptr, size_t need) {
|
static void *Realloc(void *p, size_t n) {
|
||||||
if (ptr == heap.last) {
|
if (!(p = realloc(p, n)))
|
||||||
heap.used = (char *)ptr - heap.memory;
|
DieOom();
|
||||||
return Malloc(need);
|
return p;
|
||||||
} else {
|
|
||||||
void *res = Malloc(need);
|
|
||||||
size_t size = *(size_t *)((char *)ptr - sizeof(size_t));
|
|
||||||
memcpy(res, ptr, MIN(need, size));
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessFile(void) {
|
void ProcessFile(void) {
|
||||||
|
|
|
@ -48,6 +48,8 @@ static bool recursive;
|
||||||
static bool doemptydirs;
|
static bool doemptydirs;
|
||||||
static const char *prog;
|
static const char *prog;
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
static wontreturn void PrintUsage(int rc, int fd) {
|
static wontreturn void PrintUsage(int rc, int fd) {
|
||||||
tinyprint(fd, "USAGE\n\n ", prog, USAGE, NULL);
|
tinyprint(fd, "USAGE\n\n ", prog, USAGE, NULL);
|
||||||
exit(rc);
|
exit(rc);
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
* @fileoverview elf to symbol table file dump tool
|
* @fileoverview elf to symbol table file dump tool
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "libc/mem/tinymalloc.inc"
|
||||||
|
|
||||||
void PrintUsage(FILE *f) {
|
void PrintUsage(FILE *f) {
|
||||||
fprintf(f, "%s%s%s\n", "usage: ", program_invocation_name,
|
fprintf(f, "%s%s%s\n", "usage: ", program_invocation_name,
|
||||||
" [-?h] -o PATH COMDBG");
|
" [-?h] -o PATH COMDBG");
|
||||||
|
|
Loading…
Add table
Reference in a new issue