btop/src/freebsd/btop_collect.cpp
Jos Dehaes 3d7bb52e48 Squashed commit of the following:
commit 7add05599de74d8047a9802fd673ae62a91a3372
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Jan 11 23:48:16 2022 +0100

    bring in line with upstream

commit ddb2fb0fac
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Jan 11 23:35:21 2022 +0100

    fix CPU name

commit 01a1dda734
Merge: 3b6dac6 96ac114
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Jan 11 23:29:17 2022 +0100

    merge main into freebsd

commit 3b6dac640e
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Jan 11 23:17:07 2022 +0100

    disk IO working

commit 30b33730b3
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Jan 11 22:47:44 2022 +0100

    reduce memleak drastically

commit 682746ff0e
Merge: 9fdf6d0 891051c
Author: Jakob P. Liljenberg <admin@qvantnet.com>
Date:   Sat Dec 18 19:50:13 2021 +0100

    Merge pull request #186 from GuillaumeGomez/freebsd

    Remove duplicated fstype

commit 891051c8ab
Author: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date:   Sun Dec 5 23:03:17 2021 +0100

    Remove duplicated fstype

commit 9fdf6d0204
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Nov 27 20:55:49 2021 +0100

    iterate over ALL pools

commit 860a9fe472
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Nov 27 20:30:43 2021 +0100

    RAII

commit 7c433be4a6
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Nov 21 21:36:44 2021 +0100

    regular filesystems + ZFS

commit 70d9777908
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Nov 20 22:57:00 2021 +0100

    some comment + minor cleanup

commit a61df3ff98
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Nov 20 22:51:49 2021 +0100

    it does some IO!!

commit f32358bd47
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Nov 16 22:30:55 2021 +0100

    correct unit number

commit d12b4d9d23
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Nov 16 21:58:12 2021 +0100

    readability

commit a6602ff22b
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Nov 16 21:53:33 2021 +0100

    correctly iterate over devstat. still no idea how to map the devices we get back here to mountpoints

commit e64610a163
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Nov 16 21:20:42 2021 +0100

    RAII

commit 98693aac25
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 24 21:04:04 2021 +0200

    devstat WIP

commit 8940d68f47
Author: aristocratos <gnmjpl@gmail.com>
Date:   Sun Oct 24 16:15:29 2021 +0200

    Fixed cpu usage in cpu box, process cpu usage still wrong

commit b547ccb258
Author: aristocratos <gnmjpl@gmail.com>
Date:   Sun Oct 24 11:25:28 2021 +0200

    Makefile even with OSX Makefile

commit a5eabe20b6
Author: aristocratos <gnmjpl@gmail.com>
Date:   Sun Oct 24 11:20:46 2021 +0200

    Updated Makefile

commit 18451ceb05
Author: aristocratos <gnmjpl@gmail.com>
Date:   Sun Oct 24 11:13:15 2021 +0200

    Updated Makefile

commit ca183b2b85
Author: aristocratos <gnmjpl@gmail.com>
Date:   Sun Oct 24 11:09:29 2021 +0200

    Fixed up Makefile

commit 235c95274f
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 23 23:49:51 2021 +0200

    fix boottime (not sure if needed)

commit cc2a4987ef
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 23 23:38:00 2021 +0200

    temp

commit 30ef6ee050
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 23 22:58:17 2021 +0200

    cpu freq

commit 735894b6ca
Author: aristocratos <gnmjpl@gmail.com>
Date:   Sat Oct 23 21:46:57 2021 +0200

    Fixed leaks and proc tree mode

commit cd3fd1a529
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 22 22:27:32 2021 +0200

    correct makefile

commit cd644cfc55
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 22 15:49:39 2021 +0200

    get rid of warnings

commit 81b63652bf
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 22 15:43:22 2021 +0200

    battery

commit 41ba986954
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 22 15:31:04 2021 +0200

    process args

commit 137e876da6
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 22 14:29:36 2021 +0200

    remove debug

commit 5249be0a40
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 22 14:28:40 2021 +0200

    net stats

commit 96461f4d94
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 22 13:54:00 2021 +0200

    sane memory stats

commit e5ceeee197
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 22 13:38:27 2021 +0200

    processes

commit 889433d4c6
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 22 12:08:47 2021 +0200

    cpu model  + cpu load

commit f037ab306a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 22 10:31:37 2021 +0200

    freebsd first work

commit de62167921
Merge: a590dd3 a0ee404
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 20 23:10:36 2021 +0200

    Merge branch 'main' into OSX

    # Conflicts:
    #	CHANGELOG.md
    #	Makefile
    #	src/btop.cpp

commit a590dd3f67
Author: aristocratos <admin@qvantnet.com>
Date:   Mon Oct 18 11:20:12 2021 +0200

    Ignore format-truncation

commit 4c30742d41
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 18 10:08:01 2021 +0200

    comments about temp sensors

commit 46030de77c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 18 08:10:37 2021 +0200

    available = total - used

commit 4c228de0ef
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 17 22:19:41 2021 +0200

    use sysconf to get arg_max - seems simpler

commit c60fc29f0f
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 17 22:10:50 2021 +0200

    arg_max should be int on macos

commit 0b5a931a6d
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 17 22:01:42 2021 +0200

    only fetch max_args once

commit 71d5cd5fd9
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 16 23:24:07 2021 +0200

    Reverted mutexes back to custom atomic bool based locks

commit 3f34a67df6
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 21:47:55 2021 +0200

    these helpers can be static

commit fc19c46c8a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 21:44:49 2021 +0200

    code cleanup: put the code in .cpp to enable incremental build

commit c252c618c0
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 21:09:21 2021 +0200

    don't crash on intel

commit 9f88187c29
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 21:06:18 2021 +0200

    small improvement

commit 808f09c974
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 20:59:48 2021 +0200

    don't iterate 3 times

commit d8408336e3
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 20:59:27 2021 +0200

    remove debug

commit 4f078c3beb
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 13 23:38:27 2021 +0200

    more temperature (M1 + intel)

commit 289880aaa6
Merge: 88a2528 3ffb212
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 16 19:37:09 2021 +0200

    Merge branch 'OSX' of github.com:aristocratos/btop into OSX

commit 88a2528ca3
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 16 19:34:10 2021 +0200

    Merge changes from main

commit 3ffb21203a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 19:20:45 2021 +0200

    release a bit more - still has negative temps for 2 cores on my system

commit 70b4871062
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 16 01:59:44 2021 +0200

    Fixed leaks in Mem and attempt at fixing leaks in sensors.cpp

commit fbae907720
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 15 23:41:37 2021 +0200

    temperature sensors via IOKit

commit cef0f0a68d
Author: aristocratos <admin@qvantnet.com>
Date:   Fri Oct 15 18:39:17 2021 +0200

    Process command line arguments

commit 921cfa01ff
Author: aristocratos <admin@qvantnet.com>
Date:   Wed Oct 13 23:20:15 2021 +0200

    Re-enable setuid and set default SU_GROUP to wheel for OSX

commit a416c888c7
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 13 21:16:41 2021 +0200

    temperature

commit e7afe00ce7
Author: aristocratos <admin@qvantnet.com>
Date:   Wed Oct 13 12:54:43 2021 +0200

    Cpu usage working again

commit 4193ef8921
Author: aristocratos <admin@qvantnet.com>
Date:   Wed Oct 13 10:36:51 2021 +0200

    Fixed cpu lazy sorting

commit 93fcb6ff04
Author: aristocratos <admin@qvantnet.com>
Date:   Tue Oct 12 22:22:45 2021 +0200

    Update README.md

commit 683354cd2e
Merge: 8a399c4 6d724d6
Author: aristocratos <admin@qvantnet.com>
Date:   Tue Oct 12 22:19:30 2021 +0200

    Merge pull request #80 from ShrirajHegde/OSX

    Add github workflow for MacOS

commit 8a399c499a
Author: aristocratos <admin@qvantnet.com>
Date:   Tue Oct 12 21:50:46 2021 +0200

    pointer to smart pointer, first pass

commit 772605003a
Author: aristocratos <admin@qvantnet.com>
Date:   Tue Oct 12 18:54:38 2021 +0200

    Fixed detailed memory not updating

commit 28cb677533
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 11 22:19:25 2021 +0200

    more memory free-up - still leaks like crazy

commit 304457863f
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 11 21:48:07 2021 +0200

    more RAII cleanup

commit 82e2e3c55c
Author: aristocratos <admin@qvantnet.com>
Date:   Mon Oct 11 12:40:25 2021 +0200

    Removed non present cpu fields and fixed calculation for selectable cpu field graphs

commit 68603f2b37
Author: aristocratos <admin@qvantnet.com>
Date:   Mon Oct 11 10:57:04 2021 +0200

    RAII Wrappers for Cpu::get_battery()

commit d5cb24fbeb
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 10 20:23:11 2021 +0200

    RAII

commit 8fad5a61be
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 9 21:44:16 2021 +0200

    get more disk IO stats

commit 7fa903cf16
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 9 21:43:48 2021 +0200

    fix build

commit 98036db660
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 9 21:18:25 2021 +0200

    remove unnecessary uptime param

commit aae7ae35ca
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 9 21:07:23 2021 +0200

    remove debug logging

commit 5187420b04
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 9 21:06:43 2021 +0200

    fix process elapsed time

commit 89582c0ea6
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 9 21:06:29 2021 +0200

    don't double free

commit 6d724d6155
Author: Shriraj Hegde <shriraj.hegde@gmail.com>
Date:   Sat Oct 9 21:46:33 2021 +0530

    Change Upload filename

commit 4f94ecc8ad
Author: Shriraj Hegde <shriraj.hegde@gmail.com>
Date:   Sat Oct 9 21:40:11 2021 +0530

    Fix upload path

commit e1d6d0a1f2
Author: Shriraj Hegde <shriraj.hegde@gmail.com>
Date:   Sat Oct 9 21:36:13 2021 +0530

    Skip installing gcc via Homebrew
    Change job name

commit 02cdd9d759
Author: Shriraj Hegde <shriraj.hegde@gmail.com>
Date:   Sat Oct 9 21:30:40 2021 +0530

    Fix uploading
    Remove distclean

commit 39eb6c396f
Author: Shriraj Hegde <shriraj.hegde@gmail.com>
Date:   Sat Oct 9 21:26:57 2021 +0530

    Disable static compilation

commit 099592bccd
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 9 17:52:10 2021 +0200

    Ignore empty pid 0 to fix tree mode

commit a28e17556e
Author: Shriraj Hegde <shriraj.hegde@gmail.com>
Date:   Sat Oct 9 21:17:11 2021 +0530

    Add workflow for MacOS

commit aee9179c0a
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 9 17:36:46 2021 +0200

    Disable failed tty mode detection for OSX

commit 4b7b98058d
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 9 11:28:32 2021 +0200

    Fixed disk io and added io activity based on read/write

commit bfa0629e7d
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 8 22:28:10 2021 +0200

    fill in 0 for ioticks

commit a016ff8a03
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 8 22:16:01 2021 +0200

    disk io from IOreg. Does not show any io though

commit f98606c6db
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 8 09:32:06 2021 +0200

    per process IO stats

commit c8b50ed488
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 8 00:11:08 2021 +0200

    don't show autofs, it's useless

commit c4df64d440
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 18:41:49 2021 +0200

    Fixed compile time display for gmake and command timings

commit b3e6f495f7
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 18:26:15 2021 +0200

    Fixed clk_tck -> clkTck

commit e53799188f
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 18:25:14 2021 +0200

    Fixed better detection for OSX

commit b864edf984
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 18:24:37 2021 +0200

    Fixed cumulative cpu usage

commit 6a3c5d9b97
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 13:20:30 2021 +0200

    Proc::collect() better cpu percent accurazy

commit 84d0596294
Merge: 3564f8e 98e1e87
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 12:56:55 2021 +0200

    Merge branch 'OSX' of github.com:aristocratos/btop into OSX

commit 3564f8e4c2
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 12:56:27 2021 +0200

    Proc::collect() fixed cputimes and cpu percentage calc

commit 98e1e87405
Merge: 60c5636 d96fdd7
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Thu Oct 7 11:32:41 2021 +0200

    Merge branch 'main' into OSX

commit 60c5636cd7
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 22:45:54 2021 +0200

    fix warning

commit 489e446152
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 22:38:40 2021 +0200

    details + process states

commit 7e5a808c73
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 22:38:19 2021 +0200

    avoid details crash

commit 9c9da4606b
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 21:03:21 2021 +0200

    fix quit on macos

commit ec7415384d
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 00:41:37 2021 +0200

    fix mistake in makefile

commit 5ac8fa4c8a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 18:56:13 2021 +0200

    don't show /dev

commit d901bbebd9
Author: aristocratos <gnmjpl@gmail.com>
Date:   Wed Oct 6 17:27:51 2021 +0200

     Ignore tags and other branches

commit c7f1e71e29
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 16:00:41 2021 +0200

    comment

commit b9d58e3faf
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 15:55:58 2021 +0200

    impossible to get CPU freq on M1 apparently

commit 66072711c2
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 15:33:43 2021 +0200

    detect full

commit 6bb0e930a2
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 00:38:46 2021 +0200

    CPU freq in GHz

commit a5f10f1a0f
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 15:13:18 2021 +0200

    check array length

commit 155c848b97
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 15:05:20 2021 +0200

    switch to other way to get CPU freq (still does not work)

commit cf51ba2ebe
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 14:45:44 2021 +0200

    remove some warnings

commit 775dff5f72
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 14:16:45 2021 +0200

    fix link

commit 8c67967775
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 14:16:38 2021 +0200

    reduce diff more

commit 70b47d2ca8
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 14:10:23 2021 +0200

    reduce diff with main branch

commit ca9cb48054
Merge: d0c6c0a c66b46f
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 13:49:20 2021 +0200

    Merge remote-tracking branch 'origin/main' into OSX

    # Conflicts:
    #	Makefile
    #	src/osx/btop_collect.cpp

commit c66b46f850
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 13:41:57 2021 +0200

    battery state via CoreFoundation

commit d0c6c0a362
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 10:51:36 2021 +0200

    all disks + load averages

commit ca67526dc1
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 10:33:55 2021 +0200

    show all disks

commit 56119f99a9
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 00:17:41 2021 +0200

    procs sorting/filtering

commit 8d86011d72
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 23:42:17 2021 +0200

    battery states

commit a9b64d62e4
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 23:24:59 2021 +0200

    battery hack works on M1

commit ce51031142
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 23:18:22 2021 +0200

    ugly hack to get battery

commit d5e6725c6c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 22:42:42 2021 +0200

    CPU stuff

commit 5c02bd8c83
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 21:25:42 2021 +0200

    network

commit d5da9d4983
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 15:43:05 2021 +0200

    correct cached size

commit 5f11aba504
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 12:03:48 2021 +0200

    vm stats from syscall + swap

commit 776fc96852
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 10:48:07 2021 +0200

    seems to work indeed

commit 7b40e2835a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 10:46:14 2021 +0200

    allow override optimization flag

commit 005ea24e4c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 10:09:24 2021 +0200

    update Makefile

commit af8cec9deb
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 15:32:55 2021 +0200

    some more params

commit 7ebe4f7594
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 15:15:55 2021 +0200

    show more disks

commit e50a56394a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 14:52:56 2021 +0200

    disks show something

commit 6497a8c202
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 09:15:35 2021 +0200

    reformat

commit 28e152b80c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 23:21:13 2021 +0200

    decrease diff with upstream

commit 40da88e9ca
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:56:14 2021 +0200

    try to get disks to show

commit eaf2bb56a5
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:42:01 2021 +0200

    don't crash on deque::back()

commit f66b6f712c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:08:21 2021 +0200

    cpu freq, name & process uid/name

commit 34a8a61f4d
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 21:46:11 2021 +0200

    basic process info

commit 29bb2dcc5f
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 21:45:39 2021 +0200

    initialize mutex (needed on macos apparently and not on linux)

commit fb5970b000
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:53:41 2021 +0200

    comment

commit 49d16cdddd
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:51:29 2021 +0200

    extract delimiters

commit 3db9d66476
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:48:28 2021 +0200

    first infos on macos: memory used & free

commit f8acb2f854
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Sep 28 23:37:03 2021 +0200

    make it compile on macos (M1 - arm64). Does not run though

commit bbba17cd35
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 10:51:36 2021 +0200

    all disks + load averages

commit 548203e93d
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 10:33:55 2021 +0200

    show all disks

commit 0ab2be3985
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 00:17:41 2021 +0200

    procs sorting/filtering

commit 096104c90b
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 23:42:17 2021 +0200

    battery states

commit 0ad93684c2
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 23:24:59 2021 +0200

    battery hack works on M1

commit c75b0f1cea
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 23:18:22 2021 +0200

    ugly hack to get battery

commit 600b4f72b3
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 22:42:42 2021 +0200

    CPU stuff

commit 4eb812d52c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 21:25:42 2021 +0200

    network

commit 899be68a78
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 15:43:05 2021 +0200

    correct cached size

commit a1c7f935e3
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 12:03:48 2021 +0200

    vm stats from syscall + swap

commit bd1050a740
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 10:48:07 2021 +0200

    seems to work indeed

commit 5094b73758
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 10:46:14 2021 +0200

    allow override optimization flag

commit 8811270332
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 10:09:24 2021 +0200

    update Makefile

commit 42f966f448
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 15:32:55 2021 +0200

    some more params

commit c1e6d6a62e
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 15:15:55 2021 +0200

    show more disks

commit 50fcdaa854
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 14:52:56 2021 +0200

    disks show something

commit 264bf2d7da
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 09:15:35 2021 +0200

    reformat

commit 1fd625086b
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 23:21:13 2021 +0200

    decrease diff with upstream

commit 17f9f3703c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:56:14 2021 +0200

    try to get disks to show

commit 8462ae6431
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:42:01 2021 +0200

    don't crash on deque::back()

commit 78bce5b5a6
Merge: 53e379d f9505a4
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:08:34 2021 +0200

    Merge branch 'aristocratos:main' into main

commit 53e379d74d
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:08:21 2021 +0200

    cpu freq, name & process uid/name

commit 2a44b307ef
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 21:46:11 2021 +0200

    basic process info

commit 66534eb5b5
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 21:45:39 2021 +0200

    initialize mutex (needed on macos apparently and not on linux)

commit 0983917f26
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:53:41 2021 +0200

    comment

commit 9732507248
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:51:29 2021 +0200

    extract delimiters

commit 6e704ce838
Merge: fe4db7c 7bfbd83
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:48:43 2021 +0200

    Merge branch 'main' of github.com:joske/btop

commit fe4db7c16c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:48:28 2021 +0200

    first infos on macos: memory used & free

commit 7bfbd83a47
Merge: 8c8139b a15f961
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 1 17:05:26 2021 +0200

    Merge branch 'aristocratos:main' into main

commit 8c8139bd1d
Merge: 679d21c a246c09
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Sep 29 21:50:00 2021 +0200

    Merge branch 'aristocratos:main' into main

commit 679d21cd22
Merge: 4c70c5b a49b8f9
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Sep 29 20:23:34 2021 +0200

    Merge branch 'aristocratos:main' into main

commit 4c70c5bdd9
Merge: 84a9746 c70667e
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Sep 29 08:30:05 2021 +0200

    Merge branch 'aristocratos:main' into main

commit 84a974695a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Sep 28 23:37:03 2021 +0200

    make it compile on macos (M1 - arm64). Does not run though
2022-01-12 00:04:26 +01:00

1349 lines
46 KiB
C++

/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include <arpa/inet.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ifaddrs.h>
#include <libproc.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netdb.h>
#include <netinet/tcp_fsm.h>
#include <pwd.h>
#include <sys/_timeval.h>
#include <sys/endian.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/statvfs.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <sys/vmmeter.h>
#include <sys/limits.h>
#include <vector>
#include <vm/vm_param.h>
#include <kvm.h>
#include <paths.h>
#include <fcntl.h>
#include <unistd.h>
#include <devstat.h>
#include <stdexcept>
#include <cmath>
#include <fstream>
#include <numeric>
#include <ranges>
#include <regex>
#include <string>
#include <memory>
#include <btop_config.hpp>
#include <btop_shared.hpp>
#include <btop_tools.hpp>
using std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater;
using std::ifstream, std::numeric_limits, std::streamsize, std::round, std::max, std::min;
namespace fs = std::filesystem;
namespace rng = std::ranges;
using namespace Tools;
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
namespace Cpu {
vector<long long> core_old_totals;
vector<long long> core_old_idles;
vector<string> available_fields = {"total"};
vector<string> available_sensors = {"Auto"};
cpu_info current_cpu;
bool got_sensors = false, cpu_temp_only = false;
//* Populate found_sensors map
bool get_sensors();
//* Get current cpu clock speed
string get_cpuHz();
//* Search /proc/cpuinfo for a cpu name
string get_cpuName();
struct Sensor {
fs::path path;
string label;
int64_t temp = 0;
int64_t high = 0;
int64_t crit = 0;
};
string cpu_sensor;
vector<string> core_sensors;
unordered_flat_map<int, int> core_mapping;
} // namespace Cpu
namespace Mem {
double old_uptime;
std::vector<string> zpools;
void get_zpools();
}
namespace Shared {
fs::path passwd_path;
uint64_t totalMem;
long pageSize, clkTck, coreCount, physicalCoreCount, arg_max;
int totalMem_len;
long bootTime;
void init() {
//? Shared global variables init
int mib[2];
mib[0] = CTL_HW;
mib[1] = HW_NCPU;
int ncpu;
size_t len = sizeof(ncpu);
if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
Logger::warning("Could not determine number of cores, defaulting to 1.");
} else {
coreCount = ncpu;
}
pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize <= 0) {
pageSize = 4096;
Logger::warning("Could not get system page size. Defaulting to 4096, processes memory usage might be incorrect.");
}
clkTck = sysconf(_SC_CLK_TCK);
if (clkTck <= 0) {
clkTck = 100;
Logger::warning("Could not get system clock ticks per second. Defaulting to 100, processes cpu usage might be incorrect.");
}
int64_t memsize = 0;
size_t size = sizeof(memsize);
if (sysctlbyname("hw.physmem", &memsize, &size, NULL, 0) < 0) {
Logger::warning("Could not get memory size");
}
totalMem = memsize;
struct timeval result;
size = sizeof(result);
if (sysctlbyname("kern.boottime", &result, &size, NULL, 0) < 0) {
Logger::warning("Could not get boot time");
} else {
bootTime = result.tv_sec;
}
//* Get maximum length of process arguments
arg_max = sysconf(_SC_ARG_MAX);
//? Init for namespace Cpu
Cpu::current_cpu.core_percent.insert(Cpu::current_cpu.core_percent.begin(), Shared::coreCount, {});
Cpu::current_cpu.temp.insert(Cpu::current_cpu.temp.begin(), Shared::coreCount + 1, {});
Cpu::core_old_totals.insert(Cpu::core_old_totals.begin(), Shared::coreCount, 0);
Cpu::core_old_idles.insert(Cpu::core_old_idles.begin(), Shared::coreCount, 0);
Cpu::collect();
for (auto &[field, vec] : Cpu::current_cpu.cpu_percent) {
if (not vec.empty() and not v_contains(Cpu::available_fields, field)) Cpu::available_fields.push_back(field);
}
Cpu::cpuName = Cpu::get_cpuName();
Cpu::got_sensors = Cpu::get_sensors();
Cpu::core_mapping = Cpu::get_core_mapping();
//? Init for namespace Mem
Mem::old_uptime = system_uptime();
Mem::collect();
Mem::get_zpools();
}
} // namespace Shared
namespace Cpu {
string cpuName;
string cpuHz;
bool has_battery = true;
tuple<int, long, string> current_bat;
const array<string, 10> time_names = {"user", "nice", "system", "idle"};
unordered_flat_map<string, long long> cpu_old = {
{"totals", 0},
{"idles", 0},
{"user", 0},
{"nice", 0},
{"system", 0},
{"idle", 0}
};
string get_cpuName() {
string name;
char buffer[1024];
size_t size = sizeof(buffer);
if (sysctlbyname("hw.model", &buffer, &size, NULL, 0) < 0) {
Logger::error("Failed to get CPU name");
return name;
}
name = string(buffer);
auto name_vec = ssplit(name);
if ((s_contains(name, "Xeon"s) or v_contains(name_vec, "Duo"s)) and v_contains(name_vec, "CPU"s)) {
auto cpu_pos = v_index(name_vec, "CPU"s);
if (cpu_pos < name_vec.size() - 1 and not name_vec.at(cpu_pos + 1).ends_with(')'))
name = name_vec.at(cpu_pos + 1);
else
name.clear();
} else if (v_contains(name_vec, "Ryzen"s)) {
auto ryz_pos = v_index(name_vec, "Ryzen"s);
name = "Ryzen" + (ryz_pos < name_vec.size() - 1 ? ' ' + name_vec.at(ryz_pos + 1) : "") + (ryz_pos < name_vec.size() - 2 ? ' ' + name_vec.at(ryz_pos + 2) : "");
} else if (s_contains(name, "Intel"s) and v_contains(name_vec, "CPU"s)) {
auto cpu_pos = v_index(name_vec, "CPU"s);
if (cpu_pos < name_vec.size() - 1 and not name_vec.at(cpu_pos + 1).ends_with(')') and name_vec.at(cpu_pos + 1) != "@")
name = name_vec.at(cpu_pos + 1);
else
name.clear();
} else
name.clear();
if (name.empty() and not name_vec.empty()) {
for (const auto &n : name_vec) {
if (n == "@") break;
name += n + ' ';
}
name.pop_back();
for (const auto& replace : {"Processor", "CPU", "(R)", "(TM)", "Intel", "AMD", "Core"}) {
name = s_replace(name, replace, "");
name = s_replace(name, " ", " ");
}
name = trim(name);
}
return name;
}
bool get_sensors() {
got_sensors = false;
if (Config::getB("show_coretemp") and Config::getB("check_temp")) {
int32_t temp;
size_t size = sizeof(temp);
if (sysctlbyname("dev.cpu.0.temperature", &temp, &size, NULL, 0) < 0) {
Logger::warning("Could not get temp sensor - maybe you need to load the coretemp module");
} else {
got_sensors = true;
int temp;
size_t size = sizeof(temp);
sysctlbyname("dev.cpu.0.coretemp.tjmax", &temp, &size, NULL, 0); //asuming the max temp is same for all cores
temp = (temp - 2732) / 10; // since it's an int, it's multiplied by 10, and offset to absolute zero...
current_cpu.temp_max = temp;
}
}
return got_sensors;
}
void update_sensors() {
int temp;
size_t size = sizeof(temp);
sysctlbyname("hw.acpi.thermal.tz0.temperature", &temp, &size, NULL, 0);
temp = (temp - 2732) / 10; // since it's an int, it's multiplied by 10, and offset to absolute zero...
current_cpu.temp.at(0).push_back(temp);
if (current_cpu.temp.at(0).size() > 20)
current_cpu.temp.at(0).pop_front();
for (int i = 0; i < Shared::coreCount; i++) {
string s = "dev.cpu." + std::to_string(i) + ".temperature";
if (sysctlbyname(s.c_str(), &temp, &size, NULL, 0) < 0) {
Logger::warning("Could not get temp sensor - maybe you need to load the coretemp module");
} else {
temp = (temp - 2732) / 10;
if (cmp_less(i + 1, current_cpu.temp.size())) {
current_cpu.temp.at(i + 1).push_back(temp);
if (current_cpu.temp.at(i + 1).size() > 20)
current_cpu.temp.at(i + 1).pop_front();
}
}
}
}
string get_cpuHz() {
unsigned int freq = 1;
size_t size = sizeof(freq);
if (sysctlbyname("dev.cpu.0.freq", &freq, &size, NULL, 0) < 0) {
return "";
}
return std::to_string(freq / 1000.0 ).substr(0, 3); // seems to be in MHz
}
auto get_core_mapping() -> unordered_flat_map<int, int> {
unordered_flat_map<int, int> core_map;
if (cpu_temp_only) return core_map;
for (long i = 0; i < Shared::coreCount; i++) {
core_map[i] = i;
}
//? If core mapping from cpuinfo was incomplete try to guess remainder, if missing completely, map 0-0 1-1 2-2 etc.
if (cmp_less(core_map.size(), Shared::coreCount)) {
if (Shared::coreCount % 2 == 0 and (long) core_map.size() == Shared::coreCount / 2) {
for (int i = 0, n = 0; i < Shared::coreCount / 2; i++) {
if (std::cmp_greater_equal(n, core_sensors.size())) n = 0;
core_map[Shared::coreCount / 2 + i] = n++;
}
} else {
core_map.clear();
for (int i = 0, n = 0; i < Shared::coreCount; i++) {
if (std::cmp_greater_equal(n, core_sensors.size())) n = 0;
core_map[i] = n++;
}
}
}
//? Apply user set custom mapping if any
const auto &custom_map = Config::getS("cpu_core_map");
if (not custom_map.empty()) {
try {
for (const auto &split : ssplit(custom_map)) {
const auto vals = ssplit(split, ':');
if (vals.size() != 2) continue;
int change_id = std::stoi(vals.at(0));
int new_id = std::stoi(vals.at(1));
if (not core_map.contains(change_id) or cmp_greater(new_id, core_sensors.size())) continue;
core_map.at(change_id) = new_id;
}
} catch (...) {
}
}
return core_map;
}
auto get_battery() -> tuple<int, long, string> {
if (not has_battery) return {0, 0, ""};
long seconds = -1;
uint32_t percent = -1;
size_t size = sizeof(percent);
string status = "discharging";
if (sysctlbyname("hw.acpi.battery.life", &percent, &size, NULL, 0) < 0) {
has_battery = false;
} else {
has_battery = true;
size_t size = sizeof(seconds);
if (sysctlbyname("hw.acpi.battery.time", &seconds, &size, NULL, 0) < 0) {
seconds = 0;
}
int state;
size = sizeof(state);
if (sysctlbyname("hw.acpi.battery.state", &state, &size, NULL, 0) < 0) {
status = "unknown";
} else {
if (state == 2) {
status = "charging";
}
}
if (percent == 100) {
status = "full";
}
}
return {percent, seconds, status};
}
auto collect(const bool no_update) -> cpu_info & {
if (Runner::stopping or (no_update and not current_cpu.cpu_percent.at("total").empty()))
return current_cpu;
auto &cpu = current_cpu;
double avg[3];
if (getloadavg(avg, sizeof(avg)) < 0) {
Logger::error("failed to get load averages");
}
cpu.load_avg = { (float)avg[0], (float)avg[1], (float)avg[2]};
vector<array<long, CPUSTATES>> cpu_time(Shared::coreCount);
size_t size = sizeof(long) * CPUSTATES * Shared::coreCount;
if (sysctlbyname("kern.cp_times", &cpu_time[0], &size, NULL, 0) == -1) {
Logger::error("failed to get CPU times");
}
long long global_totals = 0;
long long global_idles = 0;
vector<long long> times_summed = {0, 0, 0, 0};
for (long i = 0; i < Shared::coreCount; i++) {
vector<long long> times;
//? 0=user, 1=nice, 2=system, 3=idle
for (int x = 0; const unsigned int c_state : {CP_USER, CP_NICE, CP_SYS, CP_IDLE}) {
auto val = cpu_time[i][c_state];
times.push_back(val);
times_summed.at(x++) += val;
}
try {
//? All values
const long long totals = std::accumulate(times.begin(), times.end(), 0ll);
//? Idle time
const long long idles = times.at(3);
global_totals += totals;
global_idles += idles;
//? Calculate cpu total for each core
if (i > Shared::coreCount) break;
const long long calc_totals = max(0ll, totals - core_old_totals.at(i));
const long long calc_idles = max(0ll, idles - core_old_idles.at(i));
core_old_totals.at(i) = totals;
core_old_idles.at(i) = idles;
cpu.core_percent.at(i).push_back(clamp((long long)round((double)(calc_totals - calc_idles) * 100 / calc_totals), 0ll, 100ll));
//? Reduce size if there are more values than needed for graph
if (cpu.core_percent.at(i).size() > 40) cpu.core_percent.at(i).pop_front();
} catch (const std::exception &e) {
Logger::error("Cpu::collect() : " + (string)e.what());
throw std::runtime_error("collect() : " + (string)e.what());
}
}
const long long calc_totals = max(1ll, global_totals - cpu_old.at("totals"));
const long long calc_idles = max(1ll, global_idles - cpu_old.at("idles"));
//? Populate cpu.cpu_percent with all fields from syscall
for (int ii = 0; const auto &val : times_summed) {
cpu.cpu_percent.at(time_names.at(ii)).push_back(clamp((long long)round((double)(val - cpu_old.at(time_names.at(ii))) * 100 / calc_totals), 0ll, 100ll));
cpu_old.at(time_names.at(ii)) = val;
//? Reduce size if there are more values than needed for graph
while (cmp_greater(cpu.cpu_percent.at(time_names.at(ii)).size(), width * 2)) cpu.cpu_percent.at(time_names.at(ii)).pop_front();
ii++;
}
cpu_old.at("totals") = global_totals;
cpu_old.at("idles") = global_idles;
//? Total usage of cpu
cpu.cpu_percent.at("total").push_back(clamp((long long)round((double)(calc_totals - calc_idles) * 100 / calc_totals), 0ll, 100ll));
//? Reduce size if there are more values than needed for graph
while (cmp_greater(cpu.cpu_percent.at("total").size(), width * 2)) cpu.cpu_percent.at("total").pop_front();
if (Config::getB("show_cpu_freq")) {
auto hz = get_cpuHz();
if (hz != "") {
cpuHz = hz;
}
}
if (Config::getB("check_temp") and got_sensors)
update_sensors();
if (Config::getB("show_battery") and has_battery)
current_bat = get_battery();
return cpu;
}
} // namespace Cpu
namespace Mem {
bool has_swap = false;
vector<string> fstab;
fs::file_time_type fstab_time;
int disk_ios = 0;
vector<string> last_found;
mem_info current_mem{};
uint64_t get_totalMem() {
return Shared::totalMem;
}
void assign_values(struct disk_info& disk, int64_t readBytes, int64_t writeBytes) {
disk_ios++;
if (disk.io_read.empty()) {
disk.io_read.push_back(0);
} else {
disk.io_read.push_back(max((int64_t)0, (readBytes - disk.old_io.at(0))));
}
disk.old_io.at(0) = readBytes;
while (cmp_greater(disk.io_read.size(), width * 2)) disk.io_read.pop_front();
if (disk.io_write.empty()) {
disk.io_write.push_back(0);
} else {
disk.io_write.push_back(max((int64_t)0, (writeBytes - disk.old_io.at(1))));
}
disk.old_io.at(1) = writeBytes;
while (cmp_greater(disk.io_write.size(), width * 2)) disk.io_write.pop_front();
// no io times - need to push something anyway or we'll get an ABORT
if (disk.io_activity.empty())
disk.io_activity.push_back(0);
else
disk.io_activity.push_back(clamp((long)round((double)(disk.io_write.back() + disk.io_read.back()) / (1 << 20)), 0l, 100l));
while (cmp_greater(disk.io_activity.size(), width * 2)) disk.io_activity.pop_front();
}
class PipeWrapper {
public:
PipeWrapper(const char *file, const char *mode) {fd = popen(file, mode);}
virtual ~PipeWrapper() {if (fd) pclose(fd);}
auto operator()() -> FILE* { return fd;};
private:
FILE *fd;
};
// find all zpools in the system. Do this only at startup.
void get_zpools() {
PipeWrapper poolPipe = PipeWrapper("zpool list -H -o name", "r");
while (not std::feof(poolPipe())) {
char poolName[512];
size_t len = 512;
if (fgets(poolName, len, poolPipe())) {
poolName[strcspn(poolName, "\n")] = 0;
Logger::debug("zpool found: " + string(poolName));
Mem::zpools.push_back(poolName);
}
}
}
void collect_disk(unordered_flat_map<string, disk_info> &disks, unordered_flat_map<string, string> &mapping) {
// this bit is for 'regular' mounts
static struct statinfo cur;
long double etime = 0;
uint64_t total_bytes_read;
uint64_t total_bytes_write;
static std::unique_ptr<struct devinfo, decltype(std::free)*> curDevInfo (reinterpret_cast<struct devinfo*>(std::calloc(1, sizeof(struct devinfo))), std::free);
cur.dinfo = curDevInfo.get();
if (devstat_getdevs(NULL, &cur) != -1) {
for (int i = 0; i < cur.dinfo->numdevs; i++) {
auto d = cur.dinfo->devices[i];
string devStatName = "/dev/" + string(d.device_name) + std::to_string(d.unit_number);
for (auto& [ignored, disk] : disks) { // find matching mountpoints - could be multiple as d.device_name is only ada (and d.unit_number is the device number), while the disk.dev is like /dev/ada0s1
if (disk.dev.string().rfind(devStatName, 0) == 0) {
devstat_compute_statistics(&d, NULL, etime, DSM_TOTAL_BYTES_READ, &total_bytes_read, DSM_TOTAL_BYTES_WRITE, &total_bytes_write, DSM_NONE);
assign_values(disk, total_bytes_read, total_bytes_write);
string mountpoint = mapping.at(disk.dev);
Logger::debug("dev " + devStatName + " -> " + mountpoint + " read=" + std::to_string(total_bytes_read) + " write=" + std::to_string(total_bytes_write));
}
}
}
Logger::debug("");
}
// this code is for ZFS mounts
for (string poolName : Mem::zpools) {
char sysCtl[1024];
snprintf(sysCtl, sizeof(sysCtl), "sysctl kstat.zfs.%s.dataset | egrep \'dataset_name|nread|nwritten\'", poolName.c_str());
PipeWrapper f = PipeWrapper(sysCtl, "r");
if (f()) {
char buf[512];
size_t len = 512;
while (not std::feof(f())) {
uint64_t nread, nwritten;
if (fgets(buf, len, f())) {
char *name = std::strtok(buf, ": \n");
char *value = std::strtok(NULL, ": \n");
if (string(name).find("dataset_name") != string::npos) {
// create entry if datasetname matches with anything in mapping
// relies on the fact that the dataset name is last value in the list
// alternatively you could parse the objset-0x... when this changes, you have a new entry
string datasetname = string(value);// this is the zfs volume, like 'zroot/usr/home' -> this maps onto the device we get back from getmntinfo(3)
if (mapping.contains(datasetname)) {
string mountpoint = mapping.at(datasetname);
if (disks.contains(mountpoint)) {
auto& disk = disks.at(mountpoint);
assign_values(disk, nread, nwritten);
}
}
} else if (string(name).find("nread") != string::npos) {
nread = atoll(value);
} else if (string(name).find("nwritten") != string::npos) {
nwritten = atoll(value);
}
}
}
}
}
}
auto collect(const bool no_update) -> mem_info & {
if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty()))
return current_mem;
auto &show_swap = Config::getB("show_swap");
auto &show_disks = Config::getB("show_disks");
auto &swap_disk = Config::getB("swap_disk");
auto &mem = current_mem;
static const bool snapped = (getenv("BTOP_SNAPPED") != NULL);
int mib[4];
u_int memActive, memWire, cachedMem, freeMem;
size_t len;
len = 4; sysctlnametomib("vm.stats.vm.v_active_count", mib, &len);
len = sizeof(memActive);
sysctl(mib, 4, &(memActive), &len, NULL, 0);
memActive *= Shared::pageSize;
len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", mib, &len);
len = sizeof(memWire);
sysctl(mib, 4, &(memWire), &len, NULL, 0);
memWire *= Shared::pageSize;
mem.stats.at("used") = memWire + memActive;
mem.stats.at("available") = Shared::totalMem - memActive - memWire;
len = sizeof(cachedMem);
len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", mib, &len);
sysctl(mib, 4, &(cachedMem), &len, NULL, 0);
cachedMem *= Shared::pageSize;
mem.stats.at("cached") = cachedMem;
len = sizeof(freeMem);
len = 4; sysctlnametomib("vm.stats.vm.v_free_count", mib, &len);
sysctl(mib, 4, &(freeMem), &len, NULL, 0);
freeMem *= Shared::pageSize;
mem.stats.at("free") = freeMem;
if (show_swap and mem.stats.at("swap_total") > 0) {
for (const auto &name : swap_names) {
mem.percent.at(name).push_back(round((double)mem.stats.at(name) * 100 / mem.stats.at("swap_total")));
while (cmp_greater(mem.percent.at(name).size(), width * 2))
mem.percent.at(name).pop_front();
}
has_swap = true;
} else
has_swap = false;
//? Calculate percentages
for (const auto &name : mem_names) {
mem.percent.at(name).push_back(round((double)mem.stats.at(name) * 100 / Shared::totalMem));
while (cmp_greater(mem.percent.at(name).size(), width * 2))
mem.percent.at(name).pop_front();
}
if (show_disks) {
unordered_flat_map<string, string> mapping; // keep mapping from device -> mountpoint, since IOKit doesn't give us the mountpoint
double uptime = system_uptime();
auto &disks_filter = Config::getS("disks_filter");
bool filter_exclude = false;
// auto &only_physical = Config::getB("only_physical");
auto &disks = mem.disks;
vector<string> filter;
if (not disks_filter.empty()) {
filter = ssplit(disks_filter);
if (filter.at(0).starts_with("exclude=")) {
filter_exclude = true;
filter.at(0) = filter.at(0).substr(8);
}
}
struct statfs *stfs;
int count = getmntinfo(&stfs, MNT_WAIT);
vector<string> found;
found.reserve(last_found.size());
for (int i = 0; i < count; i++) {
auto fstype = string(stfs[i].f_fstypename);
if (fstype == "autofs" || fstype == "devfs" || fstype == "linprocfs" || fstype == "procfs" || fstype == "tmpfs" || fstype == "linsysfs" ||
fstype == "fdesckfs") {
// in memory filesystems -> not useful to show
continue;
}
std::error_code ec;
string mountpoint = stfs[i].f_mntonname;
string dev = stfs[i].f_mntfromname;
mapping[dev] = mountpoint;
//? Match filter if not empty
if (not filter.empty()) {
bool match = v_contains(filter, mountpoint);
if ((filter_exclude and match) or (not filter_exclude and not match))
continue;
}
found.push_back(mountpoint);
if (not disks.contains(mountpoint)) {
disks[mountpoint] = disk_info{fs::canonical(dev, ec), fs::path(mountpoint).filename()};
if (disks.at(mountpoint).dev.empty())
disks.at(mountpoint).dev = dev;
if (disks.at(mountpoint).name.empty())
disks.at(mountpoint).name = (mountpoint == "/" ? "root" : mountpoint);
}
if (not v_contains(last_found, mountpoint))
redraw = true;
disks.at(mountpoint).free = stfs[i].f_bfree;
disks.at(mountpoint).total = stfs[i].f_iosize;
}
//? Remove disks no longer mounted or filtered out
if (swap_disk and has_swap) found.push_back("swap");
for (auto it = disks.begin(); it != disks.end();) {
if (not v_contains(found, it->first))
it = disks.erase(it);
else
it++;
}
if (found.size() != last_found.size()) redraw = true;
last_found = std::move(found);
//? Get disk/partition stats
for (auto &[mountpoint, disk] : disks) {
if (std::error_code ec; not fs::exists(mountpoint, ec))
continue;
struct statvfs vfs;
if (statvfs(mountpoint.c_str(), &vfs) < 0) {
Logger::warning("Failed to get disk/partition stats with statvfs() for: " + mountpoint);
continue;
}
disk.total = vfs.f_blocks * vfs.f_frsize;
disk.free = vfs.f_bfree * vfs.f_frsize;
disk.used = disk.total - disk.free;
disk.used_percent = round((double)disk.used * 100 / disk.total);
disk.free_percent = 100 - disk.used_percent;
}
//? Setup disks order in UI and add swap if enabled
mem.disks_order.clear();
if (snapped and disks.contains("/mnt"))
mem.disks_order.push_back("/mnt");
else if (disks.contains("/"))
mem.disks_order.push_back("/");
if (swap_disk and has_swap) {
mem.disks_order.push_back("swap");
if (not disks.contains("swap"))
disks["swap"] = {"", "swap"};
disks.at("swap").total = mem.stats.at("swap_total");
disks.at("swap").used = mem.stats.at("swap_used");
disks.at("swap").free = mem.stats.at("swap_free");
disks.at("swap").used_percent = mem.percent.at("swap_used").back();
disks.at("swap").free_percent = mem.percent.at("swap_free").back();
}
for (const auto &name : last_found)
if (not is_in(name, "/", "swap", "/dev"))
mem.disks_order.push_back(name);
disk_ios = 0;
collect_disk(disks, mapping);
old_uptime = uptime;
}
return mem;
}
} // namespace Mem
namespace Net {
unordered_flat_map<string, net_info> current_net;
net_info empty_net = {};
vector<string> interfaces;
string selected_iface;
int errors = 0;
unordered_flat_map<string, uint64_t> graph_max = {{"download", {}}, {"upload", {}}};
unordered_flat_map<string, array<int, 2>> max_count = {{"download", {}}, {"upload", {}}};
bool rescale = true;
uint64_t timestamp = 0;
//* RAII wrapper for getifaddrs
class getifaddr_wrapper {
struct ifaddrs *ifaddr;
public:
int status;
getifaddr_wrapper() { status = getifaddrs(&ifaddr); }
~getifaddr_wrapper() { freeifaddrs(ifaddr); }
auto operator()() -> struct ifaddrs * { return ifaddr; }
};
auto collect(const bool no_update) -> net_info & {
auto &net = current_net;
auto &config_iface = Config::getS("net_iface");
auto &net_sync = Config::getB("net_sync");
auto &net_auto = Config::getB("net_auto");
auto new_timestamp = time_ms();
if (not no_update and errors < 3) {
//? Get interface list using getifaddrs() wrapper
getifaddr_wrapper if_wrap{};
if (if_wrap.status != 0) {
errors++;
Logger::error("Net::collect() -> getifaddrs() failed with id " + to_string(if_wrap.status));
redraw = true;
return empty_net;
}
int family = 0;
char ip[NI_MAXHOST];
interfaces.clear();
string ipv4, ipv6;
//? Iteration over all items in getifaddrs() list
for (auto *ifa = if_wrap(); ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL) continue;
family = ifa->ifa_addr->sa_family;
const auto &iface = ifa->ifa_name;
//? Get IPv4 address
if (family == AF_INET) {
if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)
net[iface].ipv4 = ip;
}
//? Get IPv6 address
// else if (family == AF_INET6) {
// if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)
// net[iface].ipv6 = ip;
// }
//? Update available interfaces vector and get status of interface
if (not v_contains(interfaces, iface)) {
interfaces.push_back(iface);
net[iface].connected = (ifa->ifa_flags & IFF_RUNNING);
}
}
unordered_flat_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
size_t len;
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
Logger::error("failed getting network interfaces");
} else {
std::unique_ptr<char[]> buf(new char[len]);
if (sysctl(mib, 6, buf.get(), &len, NULL, 0) < 0) {
Logger::error("failed getting network interfaces");
} else {
char *lim = buf.get() + len;
char *next = NULL;
for (next = buf.get(); next < lim;) {
struct if_msghdr *ifm = (struct if_msghdr *)next;
next += ifm->ifm_msglen;
struct if_data ifm_data = ifm->ifm_data;
if (ifm->ifm_addrs & RTA_IFP) {
struct sockaddr_dl *sdl = (struct sockaddr_dl *)(ifm + 1);
char iface[32];
strncpy(iface, sdl->sdl_data, sdl->sdl_nlen);
iface[sdl->sdl_nlen] = 0;
ifstats[iface] = std::tuple(ifm_data.ifi_ibytes, ifm_data.ifi_obytes);
}
}
}
}
//? Get total recieved and transmitted bytes + device address if no ip was found
for (const auto &iface : interfaces) {
for (const string dir : {"download", "upload"}) {
auto &saved_stat = net.at(iface).stat.at(dir);
auto &bandwidth = net.at(iface).bandwidth.at(dir);
auto dirval = dir == "download" ? std::get<0>(ifstats[iface]) : std::get<1>(ifstats[iface]);
uint64_t val = saved_stat.last;
try {
val = max(dirval, val);
} catch (const std::invalid_argument &) {
} catch (const std::out_of_range &) {
}
//? Update speed, total and top values
saved_stat.speed = round((double)(val - saved_stat.last) / ((double)(new_timestamp - timestamp) / 1000));
if (saved_stat.speed > saved_stat.top) saved_stat.top = saved_stat.speed;
if (saved_stat.offset > val) saved_stat.offset = 0;
saved_stat.total = val - saved_stat.offset;
saved_stat.last = val;
//? Add values to graph
bandwidth.push_back(saved_stat.speed);
while (cmp_greater(bandwidth.size(), width * 2)) bandwidth.pop_front();
//? Set counters for auto scaling
if (net_auto and selected_iface == iface) {
if (saved_stat.speed > graph_max[dir]) {
++max_count[dir][0];
if (max_count[dir][1] > 0) --max_count[dir][1];
} else if (graph_max[dir] > 10 << 10 and saved_stat.speed < graph_max[dir] / 10) {
++max_count[dir][1];
if (max_count[dir][0] > 0) --max_count[dir][0];
}
}
}
}
//? Clean up net map if needed
if (net.size() > interfaces.size()) {
for (auto it = net.begin(); it != net.end();) {
if (not v_contains(interfaces, it->first))
it = net.erase(it);
else
it++;
}
net.compact();
}
timestamp = new_timestamp;
}
//? Return empty net_info struct if no interfaces was found
if (net.empty())
return empty_net;
//? Find an interface to display if selected isn't set or valid
if (selected_iface.empty() or not v_contains(interfaces, selected_iface)) {
max_count["download"][0] = max_count["download"][1] = max_count["upload"][0] = max_count["upload"][1] = 0;
redraw = true;
if (net_auto) rescale = true;
if (not config_iface.empty() and v_contains(interfaces, config_iface))
selected_iface = config_iface;
else {
//? Sort interfaces by total upload + download bytes
auto sorted_interfaces = interfaces;
rng::sort(sorted_interfaces, [&](const auto &a, const auto &b) {
return cmp_greater(net.at(a).stat["download"].total + net.at(a).stat["upload"].total,
net.at(b).stat["download"].total + net.at(b).stat["upload"].total);
});
selected_iface.clear();
//? Try to set to a connected interface
for (const auto &iface : sorted_interfaces) {
if (net.at(iface).connected) selected_iface = iface;
break;
}
//? If no interface is connected set to first available
if (selected_iface.empty() and not sorted_interfaces.empty())
selected_iface = sorted_interfaces.at(0);
else if (sorted_interfaces.empty())
return empty_net;
}
}
//? Calculate max scale for graphs if needed
if (net_auto) {
bool sync = false;
for (const auto &dir : {"download", "upload"}) {
for (const auto &sel : {0, 1}) {
if (rescale or max_count[dir][sel] >= 5) {
const uint64_t avg_speed = (net[selected_iface].bandwidth[dir].size() > 5
? std::accumulate(net.at(selected_iface).bandwidth.at(dir).rbegin(), net.at(selected_iface).bandwidth.at(dir).rbegin() + 5, 0) / 5
: net[selected_iface].stat[dir].speed);
graph_max[dir] = max(uint64_t(avg_speed * (sel == 0 ? 1.3 : 3.0)), (uint64_t)10 << 10);
max_count[dir][0] = max_count[dir][1] = 0;
redraw = true;
if (net_sync) sync = true;
break;
}
}
//? Sync download/upload graphs if enabled
if (sync) {
const auto other = (string(dir) == "upload" ? "download" : "upload");
graph_max[other] = graph_max[dir];
max_count[other][0] = max_count[other][1] = 0;
break;
}
}
}
rescale = false;
return net.at(selected_iface);
}
} // namespace Net
namespace Proc {
vector<proc_info> current_procs;
unordered_flat_map<string, string> uid_user;
string current_sort;
string current_filter;
bool current_rev = false;
fs::file_time_type passwd_time;
uint64_t cputimes;
int collapse = -1, expand = -1;
uint64_t old_cputimes = 0;
atomic<int> numpids = 0;
int filter_found = 0;
detail_container detailed;
//* Generate process tree list
void _tree_gen(proc_info &cur_proc, vector<proc_info> &in_procs, vector<std::reference_wrapper<proc_info>> &out_procs, int cur_depth, const bool collapsed, const string &filter, bool found = false, const bool no_update = false, const bool should_filter = false) {
auto cur_pos = out_procs.size();
bool filtering = false;
//? If filtering, include children of matching processes
if (not found and (should_filter or not filter.empty())) {
if (not s_contains(std::to_string(cur_proc.pid), filter) and not s_contains(cur_proc.name, filter) and not s_contains(cur_proc.cmd, filter) and not s_contains(cur_proc.user, filter)) {
filtering = true;
cur_proc.filtered = true;
filter_found++;
} else {
found = true;
cur_depth = 0;
}
} else if (cur_proc.filtered)
cur_proc.filtered = false;
//? Set tree index position for process if not filtered out or currently in a collapsed sub-tree
if (not collapsed and not filtering) {
out_procs.push_back(std::ref(cur_proc));
cur_proc.tree_index = out_procs.size() - 1;
//? Try to find name of the binary file and append to program name if not the same
if (cur_proc.short_cmd.empty() and not cur_proc.cmd.empty()) {
std::string_view cmd_view = cur_proc.cmd;
cmd_view = cmd_view.substr((size_t)0, min(cmd_view.find(' '), cmd_view.size()));
cmd_view = cmd_view.substr(min(cmd_view.find_last_of('/') + 1, cmd_view.size()));
cur_proc.short_cmd = (string)cmd_view;
}
} else {
cur_proc.tree_index = in_procs.size();
}
//? Recursive iteration over all children
int children = 0;
for (auto &p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) {
if (not no_update and not filtering and (collapsed or cur_proc.collapsed)) {
out_procs.back().get().cpu_p += p.cpu_p;
out_procs.back().get().mem += p.mem;
out_procs.back().get().threads += p.threads;
filter_found++;
}
if (collapsed and not filtering) {
cur_proc.filtered = true;
} else
children++;
_tree_gen(p, in_procs, out_procs, cur_depth + 1, (collapsed ? true : cur_proc.collapsed), filter, found, no_update, should_filter);
}
if (collapsed or filtering)
return;
//? Add tree terminator symbol if it's the last child in a sub-tree
if (out_procs.size() > cur_pos + 1 and not out_procs.back().get().prefix.ends_with("]─"))
out_procs.back().get().prefix.replace(out_procs.back().get().prefix.size() - 8, 8, " └─ ");
//? Add collapse/expand symbols if process have any children
out_procs.at(cur_pos).get().prefix = ""s * cur_depth + (children > 0 ? (cur_proc.collapsed ? "[+]─" : "[-]─") : " ├─ ");
}
string get_status(char s) {
if (s & SRUN) return "Running";
if (s & SSLEEP) return "Sleeping";
if (s & SIDL) return "Idle";
if (s & SSTOP) return "Stopped";
if (s & SZOMB) return "Zombie";
return "Unknown";
}
//* Get detailed info for selected process
void _collect_details(const size_t pid, vector<proc_info> &procs) {
if (pid != detailed.last_pid) {
detailed = {};
detailed.last_pid = pid;
detailed.skip_smaps = not Config::getB("proc_info_smaps");
}
//? Copy proc_info for process from proc vector
auto p_info = rng::find(procs, pid, &proc_info::pid);
detailed.entry = *p_info;
//? Update cpu percent deque for process cpu graph
if (not Config::getB("proc_per_core")) detailed.entry.cpu_p *= Shared::coreCount;
detailed.cpu_percent.push_back(clamp((long long)round(detailed.entry.cpu_p), 0ll, 100ll));
while (cmp_greater(detailed.cpu_percent.size(), width)) detailed.cpu_percent.pop_front();
//? Process runtime : current time - start time (both in unix time - seconds since epoch)
struct timeval currentTime;
gettimeofday(&currentTime, NULL);
detailed.elapsed = sec_to_dhms(currentTime.tv_sec - detailed.entry.cpu_s); // only interested in second granularity, so ignoring tc_usec
if (detailed.elapsed.size() > 8) detailed.elapsed.resize(detailed.elapsed.size() - 3);
//? Get parent process name
if (detailed.parent.empty()) {
auto p_entry = rng::find(procs, detailed.entry.ppid, &proc_info::pid);
if (p_entry != procs.end()) detailed.parent = p_entry->name;
}
//? Expand process status from single char to explanative string
detailed.status = get_status(detailed.entry.state);
detailed.mem_bytes.push_back(detailed.entry.mem);
detailed.memory = floating_humanizer(detailed.entry.mem);
if (detailed.first_mem == -1 or detailed.first_mem < detailed.mem_bytes.back() / 2 or detailed.first_mem > detailed.mem_bytes.back() * 4) {
detailed.first_mem = min((uint64_t)detailed.mem_bytes.back() * 2, Mem::get_totalMem());
redraw = true;
}
while (cmp_greater(detailed.mem_bytes.size(), width)) detailed.mem_bytes.pop_front();
// rusage_info_current rusage;
// if (proc_pid_rusage(pid, RUSAGE_INFO_CURRENT, (void **)&rusage) == 0) {
// // this fails for processes we don't own - same as in Linux
// detailed.io_read = floating_humanizer(rusage.ri_diskio_bytesread);
// detailed.io_write = floating_humanizer(rusage.ri_diskio_byteswritten);
// }
}
//* RAII wrapper for kvm_openfiles
class kvm_openfiles_wrapper {
kvm_t* kd = NULL;
public:
kvm_openfiles_wrapper(const char* execf, const char* coref, const char* swapf, int flags, char* err) {
this->kd = kvm_openfiles(execf, coref, swapf, flags, err);
}
~kvm_openfiles_wrapper() { kvm_close(kd); }
auto operator()() -> kvm_t* { return kd; }
};
//* Collects and sorts process information from /proc
auto collect(const bool no_update) -> vector<proc_info> & {
const auto &sorting = Config::getS("proc_sorting");
const auto &reverse = Config::getB("proc_reversed");
const auto &filter = Config::getS("proc_filter");
const auto &per_core = Config::getB("proc_per_core");
const auto &tree = Config::getB("proc_tree");
const auto &show_detailed = Config::getB("show_detailed");
const size_t detailed_pid = Config::getI("detailed_pid");
bool should_filter = current_filter != filter;
if (should_filter) current_filter = filter;
const bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
if (sorted_change) {
current_sort = sorting;
current_rev = reverse;
}
const int cmult = (per_core) ? Shared::coreCount : 1;
bool got_detailed = false;
vector<array<long, CPUSTATES>> cpu_time(Shared::coreCount);
size_t size = sizeof(long) * CPUSTATES * Shared::coreCount;
if (sysctlbyname("kern.cp_times", &cpu_time[0], &size, NULL, 0) == -1) {
Logger::error("failed to get CPU times");
}
cputimes = 0;
for (const auto core : cpu_time) {
for (const unsigned int c_state : {CP_USER, CP_NICE, CP_SYS, CP_IDLE}) {
cputimes += core[c_state];
}
}
//* Use pids from last update if only changing filter, sorting or tree options
if (no_update and not current_procs.empty()) {
if (show_detailed and detailed_pid != detailed.last_pid) _collect_details(detailed_pid, current_procs);
} else {
//* ---------------------------------------------Collection start----------------------------------------------
should_filter = true;
vector<size_t> found;
struct timeval currentTime;
gettimeofday(&currentTime, NULL);
const double timeNow = currentTime.tv_sec + (currentTime.tv_usec / 1'000'000);
int count = 0;
char buf[_POSIX2_LINE_MAX];
kvm_openfiles_wrapper kd(NULL, _PATH_DEVNULL, NULL, O_RDONLY, buf);
const struct kinfo_proc* kprocs = kvm_getprocs(kd(), KERN_PROC_PROC, 0, &count);
for (int i = 0; i < count; i++) {
const struct kinfo_proc* kproc = &kprocs[i];
const size_t pid = (size_t)kproc->ki_pid;
if (pid < 1) continue;
found.push_back(pid);
//? Check if pid already exists in current_procs
bool no_cache = false;
auto find_old = rng::find(current_procs, pid, &proc_info::pid);
if (find_old == current_procs.end()) {
current_procs.push_back({pid});
find_old = current_procs.end() - 1;
no_cache = true;
}
auto &new_proc = *find_old;
//? Get program name, command, username, parent pid, nice and status
if (no_cache) {
if (kproc->ki_comm == NULL or kproc->ki_comm == "idle"s) {
current_procs.pop_back();
continue;
}
new_proc.name = kproc->ki_comm;
char** argv = kvm_getargv(kd(), kproc, 0);
if (argv) {
for (int i = 0; argv[i]; i++) {
new_proc.cmd += argv[i] + " "s;
}
if (not new_proc.cmd.empty()) new_proc.cmd.pop_back();
}
if (new_proc.cmd.empty()) new_proc.cmd = new_proc.name;
new_proc.ppid = kproc->ki_ppid;
new_proc.cpu_s = round(kproc->ki_start.tv_sec);
struct passwd *pwd = getpwuid(kproc->ki_uid);
if (pwd)
new_proc.user = pwd->pw_name;
}
new_proc.p_nice = kproc->ki_nice;
new_proc.state = kproc->ki_stat;
int cpu_t = 0;
cpu_t = kproc->ki_rusage.ru_utime.tv_sec * 1'000'000 + kproc->ki_rusage.ru_utime.tv_usec
+ kproc->ki_rusage.ru_stime.tv_sec * 1'000'000 + kproc->ki_rusage.ru_stime.tv_usec;
new_proc.mem = kproc->ki_rssize * Shared::pageSize;
new_proc.threads = kproc->ki_numthreads;
//? Process cpu usage since last update
new_proc.cpu_p = clamp((100.0 * ((cpu_t - new_proc.cpu_t) / 1'000'000.0) / max((uint64_t)1, (cputimes - old_cputimes) * Shared::clkTck)) * cmult / 100'000.0, 0.0, 100.0 * Shared::coreCount);
//? Process cumulative cpu usage since process start
new_proc.cpu_c = (double)(cpu_t * Shared::clkTck / 1'000'000) / max(1.0, timeNow - new_proc.cpu_s);
//? Update cached value with latest cpu times
new_proc.cpu_t = cpu_t;
if (show_detailed and not got_detailed and new_proc.pid == detailed_pid) {
got_detailed = true;
}
// //? Clear dead processes from current_procs
auto eraser = rng::remove_if(current_procs, [&](const auto &element) { return not v_contains(found, element.pid); });
current_procs.erase(eraser.begin(), eraser.end());
//? Update the details info box for process if active
if (show_detailed and got_detailed) {
_collect_details(detailed_pid, current_procs);
} else if (show_detailed and not got_detailed and detailed.status != "Dead") {
detailed.status = "Dead";
redraw = true;
}
old_cputimes = cputimes;
}
}
//* ---------------------------------------------Collection done-----------------------------------------------
//* Sort processes
if (sorted_change or not no_update) {
switch (v_index(sort_vector, sorting)) {
case 0: rng::sort(current_procs, rng::greater{}, &proc_info::pid); break;
case 1: rng::sort(current_procs, rng::greater{}, &proc_info::name); break;
case 2: rng::sort(current_procs, rng::greater{}, &proc_info::cmd); break;
case 3: rng::sort(current_procs, rng::greater{}, &proc_info::threads); break;
case 4: rng::sort(current_procs, rng::greater{}, &proc_info::user); break;
case 5: rng::sort(current_procs, rng::greater{}, &proc_info::mem); break;
case 6: rng::sort(current_procs, rng::greater{}, &proc_info::cpu_p); break;
case 7: rng::sort(current_procs, rng::greater{}, &proc_info::cpu_c); break;
}
if (reverse) rng::reverse(current_procs);
//* When sorting with "cpu lazy" push processes over threshold cpu usage to the front regardless of cumulative usage
if (not tree and not reverse and sorting == "cpu lazy") {
double max = 10.0, target = 30.0;
for (size_t i = 0, x = 0, offset = 0; i < current_procs.size(); i++) {
if (i <= 5 and current_procs.at(i).cpu_p > max)
max = current_procs.at(i).cpu_p;
else if (i == 6)
target = (max > 30.0) ? max : 10.0;
if (i == offset and current_procs.at(i).cpu_p > 30.0)
offset++;
else if (current_procs.at(i).cpu_p > target) {
rotate(current_procs.begin() + offset, current_procs.begin() + i, current_procs.begin() + i + 1);
if (++x > 10) break;
}
}
}
}
//* Match filter if defined
if (should_filter) {
filter_found = 0;
for (auto &p : current_procs) {
if (not tree and not filter.empty()) {
if (not s_contains(to_string(p.pid), filter) and not s_contains(p.name, filter) and not s_contains(p.cmd, filter) and not s_contains(p.user, filter)) {
p.filtered = true;
filter_found++;
} else {
p.filtered = false;
}
} else {
p.filtered = false;
}
}
}
//* Generate tree view if enabled
if (tree and (not no_update or should_filter or sorted_change)) {
if (auto find_pid = (collapse != -1 ? collapse : expand); find_pid != -1) {
auto collapser = rng::find(current_procs, find_pid, &proc_info::pid);
if (collapser != current_procs.end()) {
if (collapse == expand) {
collapser->collapsed = not collapser->collapsed;
} else if (collapse > -1) {
collapser->collapsed = true;
} else if (expand > -1) {
collapser->collapsed = false;
}
}
collapse = expand = -1;
}
if (should_filter or not filter.empty()) filter_found = 0;
vector<std::reference_wrapper<proc_info>> tree_procs;
tree_procs.reserve(current_procs.size());
//? Stable sort to retain selected sorting among processes with the same parent
rng::stable_sort(current_procs, rng::less{}, &proc_info::ppid);
//? Start recursive iteration over processes with the lowest shared parent pids
for (auto &p : rng::equal_range(current_procs, current_procs.at(0).ppid, rng::less{}, &proc_info::ppid)) {
_tree_gen(p, current_procs, tree_procs, 0, false, filter, false, no_update, should_filter);
}
//? Final sort based on tree index
rng::sort(current_procs, rng::less{}, &proc_info::tree_index);
if (reverse) rng::reverse(current_procs);
}
numpids = (int)current_procs.size() - filter_found;
return current_procs;
}
} // namespace Proc
namespace Tools {
double system_uptime() {
struct timeval ts, currTime;
std::size_t len = sizeof(ts);
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
if (sysctl(mib, 2, &ts, &len, NULL, 0) != -1) {
gettimeofday(&currTime, NULL);
return currTime.tv_sec - ts.tv_sec;
}
return 0.0;
}
} // namespace Tools