Skip to content

Commit

Permalink
version 1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
zfdang committed Dec 31, 2023
1 parent 5c2d9d5 commit caf80d6
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#

PKG = free-for-macos
VER = 0.2
VER = 1.0

CWD = $(shell pwd)
PREFIX ?= /usr/local
Expand Down
88 changes: 76 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ A memory usage utility for macOS (similar with Linux Free Command)

This utility is designed to act like 'free' on Linux systems. It is a tool for displaying memory usage statistics. Since the Mach microkernel design is different than Linux, the values reported are not an exact duplicate of what you can get from a Linux system. Be sure to read the man page for an explanation of the program output.

```
❯ free -h
total used free cached app wired
Mem: 32.00 GB 17.31 GB 2.30 GB 14.40 GB 14.11 GB 2.09 GB
Swap: 0.00 B 0.00 B 0.00 B
```

# Installation

### HOMEBREW:
Expand All @@ -17,13 +24,11 @@ This utility is designed to act like 'free' on Linux systems. It is a tool for

### COMPILATION:

You need a compiler, some headers, and a Mach-based system. On Darwin,
you can type make:
You need a compiler, some headers, and a Mach-based system. On Darwin, you can type make:

make

And out will pop a 'free' executable. On other systems, it may require
modification of the Makefile and/or source.
And out will pop a 'free' executable. On other systems, it may require modification of the Makefile and/or source.

Either run

Expand All @@ -33,34 +38,93 @@ This utility is designed to act like 'free' on Linux systems. It is a tool for

# USAGE:

See free(1).
```
❯ free -v
free: version 1.0
❯ free
total used free cached app wired
Mem: 34359738368 18562465792 2487943168 15471935488 15431581696 1932247040
Swap: 0 0 0
❯ free -h
total used free cached app wired
Mem: 32.00 GB 17.31 GB 2.30 GB 14.40 GB 14.11 GB 2.09 GB
Swap: 0.00 B 0.00 B 0.00 B
❯ free -h -s 1
total used free cached app wired
Mem: 32.00 GB 17.30 GB 2.31 GB 14.41 GB 14.31 GB 1.87 GB
Swap: 0.00 B 0.00 B 0.00 B
total used free cached app wired
Mem: 32.00 GB 17.31 GB 2.30 GB 14.41 GB 14.14 GB 2.05 GB
Swap: 0.00 B 0.00 B 0.00 B
total used free cached app wired
Mem: 32.00 GB 17.31 GB 2.30 GB 14.41 GB 14.16 GB 2.03 GB
Swap: 0.00 B 0.00 B 0.00 B
total used free cached app wired
Mem: 32.00 GB 17.31 GB 2.30 GB 14.41 GB 14.16 GB 2.03 GB
Swap: 0.00 B 0.00 B 0.00 B
^C
```

or

# Reference
man free

## how to calculate memory in macOS:
# Explanation:

## used memory
```
https://blog.guillaume-gomez.fr/articles/2021-09-06+sysinfo%3A+how+to+extract+systems%27+information
// truely-free = free-count - speculative_count
// used = total - truely-free - cached; partially verified, still small discrepancy
formatBytes(hbi.max_mem - (vm_stat.free_count - vm_stat.speculative_count + vm_stat.purgeable_count + vm_stat.external_page_count) * page_size, mem.used, sizeof(mem.used), human);
```

## cached memory

```
https://apple.stackexchange.com/questions/81581/why-does-free-active-inactive-speculative-wired-not-equal-total-ram
// cached file memory = (purgeable_count + external_page_count) * page_size
formatBytes((vm_stat.purgeable_count + vm_stat.external_page_count) * page_size, mem.cached, sizeof(mem.cached), human);
```

## app memory

```
https://stackoverflow.com/questions/14789672/why-does-host-statistics64-return-inconsistent-results
// app memory = internal_page_count - purgeable_count; verified
formatBytes((vm_stat.internal_page_count - vm_stat.purgeable_count) * page_size, mem.app, sizeof(mem.app), human);
```

## wired memory

```
Memory that is used by the kernel to run essential system processes.
It is essentially occupied by the kernel and cannot be freed.
```


# Reference

## mdoc online editor

```
https://roperzh.github.io/grapse/
```

## how to calculate memory in macOS:

```
https://novov.me/blog/posts/mixedmemories
```

```
https://www.jianshu.com/p/c31f63f14afb
https://stackoverflow.com/questions/14789672/why-does-host-statistics64-return-inconsistent-results
```

## code and brew package

## related codes

```
https://github.com/dcantrell/darwin-free
Expand Down
6 changes: 2 additions & 4 deletions free.1
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,9 @@ Display version information.
.Nm
is modeled after the free utility on Linux systems (the one from the procps project). This version aims to offer similar functionality, but due to design differences between Linux and Darwin, that is not always possible.
.Pp
The columns that free displays may be a bit confusing. The first one, total, display the total amount of memory for that area. The used column displays the amount of total memory that is currently in use. The free column displays the amount of total memory that is currently in use. The active column displays the number of active pages in the used memory. The inactive column shows the number of inactive pages in the used memory. And the wired column displays the number of wired down pages in the used memory.

.Pp
The Mem: row uses these formulas:
.Dl total = used + free
.Dl used = active + inactive + wired
For detail explanation, please see https://github.com/zfdang/free-for-macOS
.Sh SEE ALSO
.Xr hostinfo 1 ,
.Xr ps 1 ,
Expand Down
77 changes: 62 additions & 15 deletions free.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,51 @@ void formatBytes(unsigned long long bytes, char *buffer, int bufferSize, int hum
snprintf(buffer, bufferSize, "%.2f %s", result, suffixes[suffixIndex]);
}

// void dumpVmStat(vm_statistics64_data_t *vm, long pagesize, long TotalRAM) {
// char value[32];

// // raw counts
// formatBytes(vm->active_count * pagesize, value, sizeof(value), 1);
// printf("active_count memory %s\n", value);

// formatBytes(vm->inactive_count * pagesize, value, sizeof(value), 1);
// printf("inactive_count memory %s\n", value);

// formatBytes(vm->wire_count * pagesize, value, sizeof(value), 1);
// printf("wire_count memory %s\n", value);

// formatBytes(vm->free_count * pagesize, value, sizeof(value), 1);
// printf("free_count memory %s\n", value);

// formatBytes(vm->internal_page_count * pagesize, value, sizeof(value), 1);
// printf("internal_page_count memory %s\n", value);

// formatBytes(vm->speculative_count * pagesize, value, sizeof(value), 1);
// printf("speculative_count memory %s\n", value);

// formatBytes(vm->compressor_page_count * pagesize, value, sizeof(value), 1);
// printf("compressor_page_count memory %s\n", value);

// formatBytes(vm->purgeable_count * pagesize, value, sizeof(value), 1);
// printf("purgeable_count memory %s\n", value);

// formatBytes(vm->external_page_count * pagesize, value, sizeof(value), 1);
// printf("external_page_count memory %s\n", value);

// // derived numbers
// formatBytes((vm->purgeable_count + vm->external_page_count )* pagesize, value, sizeof(value), 1);
// printf("cached = purgeable_count + external_page_count %s\n", value);

// formatBytes((vm->internal_page_count - vm->purgeable_count )* pagesize, value, sizeof(value), 1);
// printf("app = internal_page_count - purgeable_count %s\n", value);

// formatBytes((vm->free_count - vm->speculative_count )* pagesize, value, sizeof(value), 1);
// printf("truely free = free_count - speculative_count %s\n", value);

// formatBytes(TotalRAM - (vm->free_count - vm->speculative_count + vm->purgeable_count + vm->external_page_count)* pagesize, value, sizeof(value), 1);
// printf("used = total - truely-free - cached) %s\n", value);
// }


/* print usage information */
int main(int argc, char **argv) {
Expand Down Expand Up @@ -101,11 +146,6 @@ int main(int argc, char **argv) {
return EXIT_FAILURE;
}

/* print the header */
printf("%20s %14s %14s %14s %14s %14s\n",
"total", "used", "free", "cached", "app", "available");


/* loop over this in case we are polling */
while (1) {
/* gather virtual memory statistics */
Expand All @@ -114,34 +154,41 @@ int main(int argc, char **argv) {
mach_error("host_statistics", ke);
return EXIT_FAILURE;
}

// method to dump metrics for calculation verification
// dumpVmStat(&vm_stat, page_size, hbi.max_mem);

/* we have collected data, put it into our structure */
// total; verified
formatBytes(hbi.max_mem, mem.total, sizeof(mem.total), human);
formatBytes((vm_stat.active_count + vm_stat.inactive_count + vm_stat.wire_count + vm_stat.speculative_count
+ vm_stat.compressor_page_count - vm_stat.purgeable_count - vm_stat.external_page_count) * page_size, mem.used, sizeof(mem.used), human);
// free; verified
formatBytes(vm_stat.free_count * page_size, mem.free, sizeof(mem.free), human);
formatBytes(vm_stat.active_count * page_size, mem.active, sizeof(mem.active), human);
formatBytes(vm_stat.inactive_count * page_size, mem.inactive, sizeof(mem.inactive), human);
// wired; verified
formatBytes(vm_stat.wire_count * page_size, mem.wired, sizeof(mem.wired), human);
// cached file = purgeable_count + external_page_count; verified
formatBytes((vm_stat.purgeable_count + vm_stat.external_page_count) * page_size, mem.cached, sizeof(mem.cached), human);
formatBytes((vm_stat.active_count + vm_stat.speculative_count) * page_size, mem.app, sizeof(mem.app), human);
// available = TotalRAM - (active_count + inactive_count + wire_count + speculative_count - purgeable_count)
formatBytes(hbi.max_mem - (vm_stat.active_count + vm_stat.inactive_count + vm_stat.wire_count + vm_stat.speculative_count
- vm_stat.purgeable_count) * page_size, mem.available, sizeof(mem.available), human);
// truely-free = free-count - speculative_count
// used = total - truely-free - cached; partially verified, still small discrepancy
formatBytes(hbi.max_mem - (vm_stat.free_count - vm_stat.speculative_count + vm_stat.purgeable_count + vm_stat.external_page_count) * page_size, mem.used, sizeof(mem.used), human);
// app memory = internal_page_count - purgeable_count; verified
formatBytes((vm_stat.internal_page_count - vm_stat.purgeable_count) * page_size, mem.app, sizeof(mem.app), human);

// get swap info
if (sysctl(vmmib, 2, &swapinfo, &swapinfo_sz, NULL, 0) == -1) {
fprintf(stderr, "Could not collect VM info, errno %d - %s", errno, strerror(errno));
return EXIT_FAILURE;
}
// printf("swapinfo: %lld %lld %lld\n", swapinfo.xsu_total, swapinfo.xsu_used, swapinfo.xsu_avail);

formatBytes(swapinfo.xsu_total, swap.total, sizeof(swap.total), human);
formatBytes(swapinfo.xsu_used, swap.used, sizeof(swap.used), human);
formatBytes(swapinfo.xsu_avail, swap.free, sizeof(swap.free), human);

/* print the header */
printf("%20s %14s %14s %14s %14s %14s\n",
"total", "used", "free", "cached", "app", "wired");
/* display the memory usage statistics */
printf("Mem: %15s %14s %14s %14s %14s %14s\n",
mem.total, mem.used, mem.free, mem.cached, mem.app, mem.available);
mem.total, mem.used, mem.free, mem.cached, mem.app, mem.wired);
printf("Swap: %14s %14s %14s\n", swap.total, swap.used, swap.free);

/* does the loop continue? */
Expand Down
5 changes: 1 addition & 4 deletions free.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ typedef struct mem {
char total[14];
char used[14];
char free[14];
char active[14];
char inactive[14];
char app[14];
char wired[14];
char available[14];
char cached[14];
char app[14];
} mem_t;

0 comments on commit caf80d6

Please sign in to comment.