├── http
├── favicon.ico
├── help.html
├── css
│ └── atop.css
├── index.html
├── js
│ ├── atop_compare_fc.js
│ ├── atop.js
│ └── atop_parse.js
└── template
│ └── html
│ ├── command_line.html
│ ├── disk.html
│ ├── container.html
│ ├── generic.html
│ └── memory.html
├── .gitmodules
├── CONTRIBUTING.md
├── atophttpd.service
├── config.h
├── json.h
├── packaging
└── debian
│ ├── control
│ └── makedeb.sh
├── .gitignore
├── Makefile
├── output.h
├── cache.h
├── httpd.h
├── man
└── atophttpd.1
├── bench.py
├── output.c
├── gen-cert.sh
├── README.md
├── connection.h
├── socket.c
├── connection.c
├── cache.c
├── tls.c
├── rawlog.c
├── LICENSE
├── httpd.c
└── json.c
/http/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pizhenwei/atophttpd/HEAD/http/favicon.ico
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "atop"]
2 | path = atop
3 | url = https://github.com/Atoptool/atop.git
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Maintainer
2 | - zhenwei pi (HTTP backend)
3 | - Enhua Zhou (HTTP frontend)
4 |
5 | # Patch is always welcomed!
6 | - Sending patch to maintainer by email
7 | - Create a PR on github
8 |
--------------------------------------------------------------------------------
/atophttpd.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=A web style atop(https://www.atoptool.nl)
3 | Documentation=man:atophttpd(1)
4 | Requires=atop.service
5 |
6 | [Service]
7 | Type=simple
8 | Environment="PORT=2867"
9 | Environment="LOGPATH=/var/log/atop"
10 | ExecStart=/bin/sh -c 'exec /usr/bin/atophttpd -P ${LOGPATH} -p ${PORT}'
11 |
12 | [Install]
13 | WantedBy=multi-user.target
14 |
--------------------------------------------------------------------------------
/config.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 zhenwei pi
3 | *
4 | * Authors:
5 | * zhenwei pi
6 | *
7 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
8 | * See the COPYING file in the top-level directory.
9 | */
10 |
11 | #ifndef _CONFIG_H_
12 | #define _CONFIG_H_
13 |
14 | typedef long long count_t;
15 | extern unsigned int pagesize;
16 | extern unsigned short hertz;
17 | extern struct utsname utsname;
18 | extern unsigned int hidecmdline;
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/json.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 zhenwei pi
3 | *
4 | * Authors:
5 | * zhenwei pi
6 | * Fei Li
7 | *
8 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
9 | * See the COPYING file in the top-level directory.
10 | */
11 |
12 | #ifndef _JSON_H_
13 | #define _JSON_H_
14 |
15 | #include "photosyst.h"
16 | #include "photoproc.h"
17 | #include "connection.h"
18 |
19 | int jsonout(int, char *, time_t, int, struct devtstat *, struct sstat *, int, unsigned int, char, connection* connection);
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/packaging/debian/control:
--------------------------------------------------------------------------------
1 | Package: atophttpd
2 | Version: ATOP_VERSION
3 | Architecture: amd64
4 | Maintainer: zhenwei pi
5 | Installed-Size: ATOPHTTPD_SIZE
6 | Depends: atop (= ATOP_VERSION)
7 | Section: admin
8 | Priority: optional
9 | Homepage: https://github.com/pizhenwei/atophttpd
10 | Description: A web style atop(https://www.atoptool.nl).
11 | atophttpd depends on atop and reads atop rawlog, provides web
12 | service(HTTP 1.1). A user accesses atop by a web browser without
13 | login permission of a server, it's also possible to get/analysis
14 | statistics of many servers in batch.
15 | .
16 | Author: zhenwei pi Enhua Zhou
17 |
--------------------------------------------------------------------------------
/packaging/debian/makedeb.sh:
--------------------------------------------------------------------------------
1 | ROOT_PATH=$1
2 | PACKAGE_PATH=$ROOT_PATH/packaging/debian/deb
3 | CONTROL_PATH=$ROOT_PATH/packaging/debian/deb/DEBIAN
4 |
5 | mkdir -p $CONTROL_PATH
6 | make -C $ROOT_PATH install prefix=$PACKAGE_PATH
7 |
8 | cp $ROOT_PATH/packaging/debian/control $CONTROL_PATH
9 | # replace version info
10 | ATOP_VERSION=`grep ATOPVERS atop/version.h | tr "\"" " " | awk '{print $3}'`
11 | sed -i "s/ATOP_VERSION/$ATOP_VERSION/" $CONTROL_PATH/control
12 |
13 | # replace package size info
14 | ATOPHTTPD_SIZE=`du -k -d 0 $PACKAGE_PATH | awk '{print $1}'`
15 | sed -i "s/ATOPHTTPD_SIZE/$ATOPHTTPD_SIZE/" $CONTROL_PATH/control
16 |
17 | dpkg-deb -b $PACKAGE_PATH $ROOT_PATH/atophttpd_$ATOP_VERSION.deb
18 |
19 | # cleanup
20 | rm -rf $PACKAGE_PATH
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Object files
5 | *.o
6 | *.ko
7 | *.obj
8 | *.elf
9 |
10 | # Linker output
11 | *.ilk
12 | *.map
13 | *.exp
14 |
15 | # Precompiled Headers
16 | *.gch
17 | *.pch
18 |
19 | # Libraries
20 | *.lib
21 | *.a
22 | *.la
23 | *.lo
24 |
25 | # Shared objects (inc. Windows DLLs)
26 | *.dll
27 | *.so
28 | *.so.*
29 | *.dylib
30 |
31 | # Executables
32 | *.exe
33 | *.out
34 | *.app
35 | *.i*86
36 | *.x86_64
37 | *.hex
38 |
39 | # Debug files
40 | *.dSYM/
41 | *.su
42 | *.idb
43 | *.pdb
44 |
45 | # Kernel Module Compile Results
46 | *.mod*
47 | *.cmd
48 | .tmp_versions/
49 | modules.order
50 | Module.symvers
51 | Mkfile.old
52 | dkms.conf
53 |
54 | # target binary file
55 | atophttpd
56 |
57 | # development
58 | tags
59 |
60 | # debian package
61 | *.deb
62 |
--------------------------------------------------------------------------------
/http/help.html:
--------------------------------------------------------------------------------
1 | ATOPHTTPD HELP PAGE
2 |
3 | Locations
4 |
5 |
6 | ping: ping and test atophttpd service.
7 | help: show this help page.
8 | index.html: get index.html.
9 | favicon.ico: get favicon.ico.
10 | js/atop.js: get atop.js.
11 | css/atop.css: get css/atop.css.
12 | template: get template for atop rendering. Supported argument type (required, available options: generic/memory/disk/command_line).
13 | showsamp: get atop sample data. Supported argument timestamp (required, UNIX timestamp to query), lables (required, available options: ALL/CPU/cpu/CPL/GPU/MEM/SWP/PAG/PSI/LVM/MDD/DSK/NFM/NFC/NFS/NET/IFB/NUM/NUC/LLC/PRG/PRC/PRM/PRD/PRN/PRE. Select one lable, Ex lables=CPU; or select multiple lables, Ex lables=CPU,cpu,CPL), encoding (optional, available options: deflate/none).
14 |
15 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CFLAGS = -Iatop -g -O2 -lz -Wall -Wcast-align -std=gnu11
2 | OBJS = cache.o httpd.o json.o output.o rawlog.o version.o connection.o socket.o tls.o
3 | BIN = atophttpd
4 | PREFIX := $(prefix)
5 | CC=gcc
6 |
7 | ifneq (,$(filter $(USE_TLS),yes YES y Y 1))
8 | CFLAGS += -lssl -lcrypto -DUSE_TLS
9 | endif
10 |
11 | all: submodule bin
12 | $(CC) -o $(BIN) $(OBJS) $(CFLAGS)
13 |
14 | install: bin
15 | install -D -s $(BIN) $(PREFIX)/usr/bin/$(BIN)
16 | install -D atophttpd.service $(PREFIX)/lib/systemd/system/atophttpd.service
17 | install -D man/atophttpd.1 $(PREFIX)/usr/share/man/man1/atophttpd.1
18 |
19 | deb: bin
20 | @sh packaging/debian/makedeb.sh `pwd`
21 |
22 | bin: $(OBJS)
23 | $(CC) -o $(BIN) $(OBJS) $(CFLAGS)
24 |
25 | %.o: %.c
26 | $(CC) -c $(CFLAGS) $*.c -o $*.o
27 |
28 | version.o:
29 | @make -C atop versdate.h
30 | $(CC) -c $(CFLAGS) atop/version.c -o version.o
31 |
32 | submodule:
33 | git submodule update --init --recursive
34 |
35 | clean:
36 | @rm -f $(BIN) *.o *.deb
37 |
--------------------------------------------------------------------------------
/output.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 zhenwei pi
3 | *
4 | * Authors:
5 | * zhenwei pi
6 | *
7 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
8 | * See the COPYING file in the top-level directory.
9 | */
10 |
11 | #ifndef _OUTPUT_H_
12 | #define _OUTPUT_H_
13 |
14 | #include "connection.h"
15 |
16 | enum {
17 | OUTPUT_STDOUT,
18 | OUTPUT_FD,
19 | OUTPUT_BUF
20 | };
21 |
22 | struct output {
23 | int output_type;
24 | union {
25 | /* OUTPUT_STDOUT needs no more argument */
26 | int fd; /* for OUTPUT_FD */
27 | struct output_buf {
28 | char *buf;
29 | int size; /* size of buf, auto grow if not enough */
30 | int offset; /* offset of buf, reset to 0 for next record */
31 | } ob; /* OUTPUT_BUF */
32 | };
33 | void (*done)(struct output *op, connection *conn);
34 | char *encoding;
35 | };
36 |
37 | void output_samp(struct output *op, char *buf, int size);
38 | void output_samp_done(struct output *op, connection *conn);
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/cache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 zhenwei pi
3 | *
4 | * Authors:
5 | * zhenwei pi
6 | *
7 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
8 | * See the COPYING file in the top-level directory.
9 | */
10 |
11 | #ifndef _CACHE_H_
12 | #define _CACHE_H_
13 |
14 | #include
15 | #include
16 |
17 | struct cache_elem_t {
18 | time_t time;
19 | off_t off;
20 | };
21 |
22 | struct cache_t {
23 | char *name;
24 | int flags;
25 | int max_elems;
26 | int nr_elems;
27 | struct cache_elem_t *elems;
28 | off_t st_size;
29 | struct timespec st_mtim;
30 | };
31 |
32 | struct cache_t *cache_alloc(const char *name);
33 | struct cache_t *cache_find(const char *name);
34 | void cache_free(const char *name);
35 | void cache_set(struct cache_t *cache, time_t time, off_t off);
36 | struct cache_t *cache_get(time_t time, off_t *off);
37 | void cache_done(struct cache_t *cache);
38 | void cache_sort();
39 | struct cache_t *cache_get_recent();
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/httpd.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 zhenwei pi
3 | *
4 | * Authors:
5 | * zhenwei pi
6 | *
7 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
8 | * See the COPYING file in the top-level directory.
9 | */
10 |
11 | #ifndef _HTTPD_H_
12 | #define _HTTPD_H_
13 |
14 | extern int __debug;
15 |
16 | #include "connection.h"
17 |
18 | #define log_debug(fmt, args...) \
19 | if (__debug) { \
20 | fprintf(stdout, "%s[%d] " fmt, \
21 | __func__, __LINE__, ##args); \
22 | }
23 |
24 | int rawlog_parse_all(const char *path);
25 | int rawlog_get_record(time_t ts, char *lables, connection *conn);
26 |
27 | typedef struct atophttpd_tls_context_config {
28 | int tls_port;
29 | char *tls_addr;
30 | char *ca_cert_file;
31 | char *cert_file;
32 | char *key_file;
33 | } atophttpd_tls_context_config;
34 |
35 | struct atophttd_context {
36 | int port;
37 | int daemonmode;
38 | char *addr;
39 | char *log_path;
40 |
41 | atophttpd_tls_context_config tls_ctx_config;
42 |
43 | connection* listeners[CONN_TYPE_MAX];
44 | };
45 |
46 | #endif
47 |
--------------------------------------------------------------------------------
/man/atophttpd.1:
--------------------------------------------------------------------------------
1 | .TH ATOPHTTPD 1 "Jan 2023" "Linux"
2 | .SH NAME
3 | .B atophttpd
4 | - A web style atop(https://www.atoptool.nl)
5 | .SH SYNOPSIS
6 | .B atophttpd
7 | [\-h] [\-d] [\-D] [\-H] [-p PORT] [-P PATH]
8 | .SH DESCRIPTION
9 | .I atophttpd
10 | depends on atop and reads atop rawlog, provides web
11 | service(HTTP 1.1). A user accesses atop by a web browser without
12 | login permission of a server, it's also possible to get/analyze
13 | statistics of many servers in batch.
14 |
15 | Since atophttpd access the rawlog of atop, this daemon requires the
16 | permission of
17 | .B PATH.
18 | .SH OPTIONS
19 | .TP
20 | \-h
21 | Print usage message.
22 | .TP
23 | \-d
24 | Run in daemon mode.
25 | .TP
26 | \-D
27 | Run with debug message. (not in daemon mode)
28 | .TP
29 | \-H
30 | Hide cmdline for security protection. (Ex, mysql -pPASSWD)
31 | .TP
32 | \-p PORT
33 | Listen to PORT, default 2867.
34 | .TP
35 | \-P PATH
36 | Specify atop log path, default
37 | .B
38 | /var/log/atop.
39 | .SH SOURCE
40 | https://github.com/pizhenwei/atophttpd
41 | .SH OS
42 | Linux
43 | .SH AUTHOR
44 | zhenwei pi / Enhua Zhou
45 | .SH SEE ALSO
46 | atop(1)
47 |
--------------------------------------------------------------------------------
/bench.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | import requests
3 | import time
4 |
5 | url_status = {"favicon.ico": 200,\
6 | "index.html": 200,\
7 | "js/atop.js": 200,\
8 | "css/atop.css": 200,\
9 | "template?type=generic": 200,\
10 | "template?type=memory": 200,\
11 | "template?type=disk": 200,\
12 | "template?type=command_line": 200,\
13 | "showsamp": 200,\
14 | "showsamp?lables=ALL×tamp=" + str(int(time.time())): 200,\
15 | "notexist": 404}
16 |
17 | def bench():
18 | for num in range(0, 100):
19 | for url, status in url_status.items():
20 | request = 'http://127.0.0.1:2867/' + url
21 | result = requests.get(request)
22 | if result.status_code != status:
23 | sys.exit(request + " failed!")
24 |
25 | def main():
26 | pingurl = 'http://127.0.0.1:2867/ping'
27 | ping = requests.get(pingurl)
28 | if ping.status_code != 200:
29 | sys.exit(pingurl + " failed!")
30 |
31 | bench()
32 |
33 | if __name__ == "__main__":
34 | main()
35 |
--------------------------------------------------------------------------------
/http/css/atop.css:
--------------------------------------------------------------------------------
1 | button {
2 | width: 100px;
3 | height: 100px;
4 | }
5 |
6 | div.atop {
7 | min-width:1500px;
8 | font-family: monospace
9 | }
10 |
11 | div.global_header_entry {
12 | width: 25%;
13 | }
14 |
15 | div.global_header_elapsed_entry {
16 | width: 25%;
17 | text-align: right;
18 | margin-right: 2%;
19 | }
20 |
21 | div.bar {
22 | width: 99%;
23 | display: flex;
24 | margin-left: 1%;
25 | margin-right: 1%;
26 | border-style: groove;
27 | }
28 |
29 | /* HEADER */
30 | div.header_key {
31 | width: 3%;
32 | text-align: left;
33 | display: contents;
34 | }
35 |
36 | /* 16 entry */
37 | div.header_entry {
38 | width: 6.5%;
39 | display: flex;
40 | }
41 |
42 | div.header_entry_name {
43 | width: 50%;
44 | white-space: nowrap;
45 | text-align: left;
46 | }
47 |
48 | div.header_entry_value {
49 | width: 50%;
50 | padding-right: 1%;
51 | white-space: nowrap;
52 | text-align: right;
53 | direction: rtl;
54 | }
55 |
56 |
57 | div.header_entry_split {
58 | width: 1%;
59 | display: flex;
60 | }
61 |
62 |
63 | /* PROCESS */
64 | div.process_entry {
65 | width: 5%;
66 | text-align: right;
67 | }
68 |
69 | div.process_last_entry {
70 | padding-left: 1%;
71 | text-align: right;
72 | }
73 |
74 | div.outer-container{
75 | width: 100%;
76 | height: 500px;
77 | position: relative;
78 | overflow: hidden;
79 | }
80 | div.inner-container{
81 | position: absolute;
82 | left: 0;
83 | top: 0;
84 | right: -16px;
85 | bottom: 0;
86 | overflow-y: auto;
87 | overflow-x: hidden;
88 | }
89 |
90 |
91 |
--------------------------------------------------------------------------------
/output.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 zhenwei pi
3 | *
4 | * Authors:
5 | * zhenwei pi
6 | *
7 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
8 | * See the COPYING file in the top-level directory.
9 | */
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #include "output.h"
18 |
19 | #define OUTBUF_DEF_SIZE (1024 * 1024)
20 |
21 | static void output_buf(struct output *op, char *buf, int size)
22 | {
23 | if (!op->ob.buf)
24 | {
25 | op->ob.size = OUTBUF_DEF_SIZE;
26 | op->ob.buf = calloc(1, op->ob.size);
27 | }
28 |
29 | /* no enought buf, grow it */
30 | if (op->ob.size - op->ob.offset < size)
31 | {
32 | op->ob.size *= 2;
33 | op->ob.buf = realloc(op->ob.buf, op->ob.size);
34 | }
35 |
36 | memcpy(op->ob.buf + op->ob.offset, buf, size);
37 | op->ob.offset += size;
38 | }
39 |
40 | void output_samp(struct output *op, char *buf, int size)
41 | {
42 | switch (op->output_type)
43 | {
44 | case OUTPUT_STDOUT:
45 | printf("%s", buf);
46 | break;
47 |
48 | case OUTPUT_FD:
49 | write(op->fd, buf, size);
50 | break;
51 |
52 | case OUTPUT_BUF:
53 | output_buf(op, buf, size);
54 | break;
55 |
56 | default:
57 | fprintf(stderr, "Error, unknown output type\n");
58 | exit(EINVAL);
59 | }
60 | }
61 |
62 | void output_samp_done(struct output *op, connection *conn)
63 | {
64 | if (op->done)
65 | op->done(op, conn);
66 |
67 | if (op->output_type == OUTPUT_BUF)
68 | {
69 | memset(op->ob.buf, 0x00, op->ob.offset);
70 | op->ob.offset = 0;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/http/index.html:
--------------------------------------------------------------------------------
1 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/http/js/atop_compare_fc.js:
--------------------------------------------------------------------------------
1 | function compareOrderByCpu(a, b) {
2 | acpu = a["utime"] + a["stime"];
3 | bcpu = b["utime"] + b["stime"];
4 | if (acpu > bcpu) {
5 | return -1;
6 | }
7 | if (acpu < bcpu) {
8 | return 1;
9 | }
10 | return 0;
11 | }
12 |
13 | function compareOrderByDisk(a, b) {
14 | if (a["wsz"] > a["cwsz"]) {
15 | adsk = a["rio"] + a["wsz"] - a["cwsz"];
16 | } else {
17 | adsk = a["rio"];
18 | }
19 |
20 | if (b["wsz"] > b["cwsz"]) {
21 | bdsk = b["rio"] + b["wsz"] - b["cwsz"];
22 | } else {
23 | bdsk = b["rio"];
24 | }
25 |
26 | if (adsk > bdsk) {
27 | return -1;
28 | }
29 |
30 | if (adsk < bdsk) {
31 | return 1;
32 | }
33 |
34 | return compareOrderByCpu(a, b);
35 | }
36 |
37 | function compareOrderByDiskIoms(a, b) {
38 | adsk_value = a["io_ms"]
39 | bdsk_value = b["io_ms"]
40 |
41 | if (adsk_value > bdsk_value) {
42 | return -1;
43 | }
44 |
45 | if (adsk_value < bdsk_value) {
46 | return 1;
47 | }
48 |
49 | return 0;
50 | }
51 |
52 | function compareOrderByMem(a, b) {
53 | amem = a["rmem"];
54 | bmem = b["rmem"];
55 |
56 | if (amem > bmem) {
57 | return -1;
58 | }
59 | if (amem < bmem) {
60 | return 1;
61 | }
62 |
63 | return 0;
64 | }
65 |
66 | function compareOrderByNet(a, b) {
67 | anet_value = a["rpack"] + a["spack"]
68 | bnet_value = b["rpack"] + b["bpack"]
69 |
70 | if (anet_value > bnet_value) {
71 | return -1;
72 | }
73 |
74 | if (anet_value < bnet_value) {
75 | return 1;
76 | }
77 |
78 | return 0;
79 | }
80 |
81 | function compareOrderLLC(a, b) {
82 | aLLC = a["occupancy"] * 1
83 | bLLC = b["occupancy"] * 1
84 |
85 | if (aLLC > bLLC) {
86 | return -1;
87 | }
88 |
89 | if (aLLC < bLLC) {
90 | return 1;
91 | }
92 |
93 | return 0;
94 | }
95 |
--------------------------------------------------------------------------------
/http/template/html/command_line.html:
--------------------------------------------------------------------------------
1 |
26 |
27 |
PID
28 |
TID
29 |
S
30 |
DSK
31 |
COMMAND-LINE
32 |
33 |
34 |
35 |
36 |
37 |
38 |
{pid}
39 |
{tid}
40 |
{state}
41 |
{diskbusy}
42 |
{cmdline}
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/gen-cert.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Generate some test certificates which are used by the regression test suite:
4 | #
5 | # tls/ca.{crt,key} Self signed CA certificate.
6 | # tls/atophttpd.{crt,key} A certificate with no key usage/policy restrictions.
7 | # tls/client.{crt,key} A certificate restricted for SSL client usage.
8 | # tls/server.{crt,key} A certificate restricted for SSL server usage.
9 | # tls/atophttpd.dh DH Params file.
10 |
11 | generate_cert() {
12 | local name=$1
13 | local cn=$2
14 | local opts="$3"
15 |
16 | local keyfile=tls/${name}.key
17 | local certfile=tls/${name}.crt
18 |
19 | [ -f $keyfile ] || openssl genrsa -out $keyfile 2048
20 | openssl req \
21 | -new -sha256 \
22 | -subj "/O=Atophttpd Test/CN=$cn" \
23 | -key $keyfile | \
24 | openssl x509 \
25 | -req -sha256 \
26 | -CA tls/ca.crt \
27 | -CAkey tls/ca.key \
28 | -CAserial tls/ca.txt \
29 | -CAcreateserial \
30 | -days 365 \
31 | $opts \
32 | -out $certfile
33 | }
34 |
35 | read -p "Enter DNS name: " DNS
36 | read -p "Enter IP address: " IP
37 |
38 | SAN="DNS:localhost"
39 | if [ ! -z "$DNS" ]; then
40 | SAN="$SAN,DNS:$DNS"
41 | fi
42 |
43 | if [ ! -z "$IP" ]; then
44 | SAN="$SAN,IP:$IP"
45 | fi
46 |
47 | mkdir -p tls
48 | [ -f tls/ca.key ] || openssl genrsa -out tls/ca.key 4096
49 | openssl req \
50 | -x509 -new -nodes -sha256 \
51 | -key tls/ca.key \
52 | -days 3650 \
53 | -subj '/O=Atophttpd Test/CN=Certificate Authority' \
54 | -out tls/ca.crt
55 |
56 | cat > tls/openssl.cnf <<_END_
57 | [ server_cert ]
58 | keyUsage = digitalSignature, keyEncipherment
59 | nsCertType = server
60 | subjectAltName = ${SAN}
61 |
62 | [ client_cert ]
63 | keyUsage = digitalSignature, keyEncipherment
64 | nsCertType = client
65 | _END_
66 |
67 | generate_cert server "Server-only" "-extfile tls/openssl.cnf -extensions server_cert"
68 | generate_cert client "Client-only" "-extfile tls/openssl.cnf -extensions client_cert"
69 | generate_cert atophttpd "Generic-cert"
70 |
71 | [ -f tls/atophttpd.dh ] || openssl dhparam -out tls/atophttpd.dh 2048
72 |
--------------------------------------------------------------------------------
/http/template/html/disk.html:
--------------------------------------------------------------------------------
1 |
26 |
27 |
PID
28 |
TID
29 |
RDDSK
30 |
WRDSK
31 |
WCANCL
32 |
DSK
33 |
CMD
34 |
35 |
36 |
37 |
38 |
39 |
40 |
{pid}
41 |
{tid}
42 |
{rsz}
43 |
{wsz}
44 |
{cwsz}
45 |
{diskbusy}
46 |
{name}
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/http/template/html/container.html:
--------------------------------------------------------------------------------
1 |
26 |
27 |
NPROCS
28 |
SYSCPU
29 |
USRCPU
30 |
VSIZE
31 |
RSIZE
32 |
PSIZE
33 |
LOCKSZ
34 |
SWAPSZ
35 |
RDDSK
36 |
WRDSK
37 |
RNET
38 |
SNET
39 |
DSK
40 |
CID
41 |
COMMAND-LINE
42 |
43 |
44 |
45 |
46 |
47 |
48 |
{pid}
49 |
{tid}
50 |
{state}
51 |
{diskbusy}
52 |
{cmdline}
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ATOPHTTPD
2 |
3 | ## Introduction of [atop](https://www.atoptool.nl)
4 |
5 |
6 | Atop is an ASCII full-screen performance monitor for Linux that is capable
7 | of reporting the activity of all processes (even if processes have finished
8 | during the interval), daily logging of system and process activity for
9 | long-term analysis, highlighting overloaded system resources by using colors,
10 | etcetera. At regular intervals, it shows system-level activity related to the
11 | CPU, memory, swap, disks (including LVM) and network layers, and for every
12 | process (and thread) it shows e.g. the CPU utilization, memory growth,
13 | disk utilization, priority, username, state, and exit code.
14 | In combination with the optional kernel module *netatop*,
15 | it even shows network activity per process/thread.
16 | In combination with the optional daemon *atopgpud*,
17 | it also shows GPU activity on system level and process level.
18 |
19 | ## Introduction of [atophttpd](https://github.com/pizhenwei/atophttpd)
20 | atop records the system level and process level information into log files
21 | as a daemon process, then an end user accesses the server, runs command
22 | `atop -r /var/log/atop/atop_20230105 -b 12:34` to analyze the performance,
23 | system status, process status at a specified time stamp.
24 |
25 | atophttpd runs as a daemon, reads the atop log files, and provide a HTTP 1.1
26 | service. This allows to use atop by a web browser without server login, it's
27 | also possible to query the system level and process level information in
28 | batch.
29 |
30 | ## HOWTO
31 | ### run atop daemon
32 | * By systemd: `systemctl status atop.service`(test atop service) and `systemctl start atop.service`(start atop service).
33 | * By command: `atop -w /var/log/atop/atop_20230105 10`.
34 |
35 | ### run atophttpd daemon:
36 | ```
37 | make
38 | ./atophttpd -d #run in daemon only on localhost
39 | ./atophttpd -d -a ${IP} #run in daemon on ip
40 | ```
41 |
42 | ### access atophttpd server:
43 | * By a web browser, for example: `192.168.1.100:2867`,
44 | to get the help page by `192.168.1.100:2867/help`.
45 |
46 | * By curl command: `curl 'http://127.0.0.1:2867/showsamp?lables=ALL×tamp=1675158274&encoding=none' | jq `.
47 |
48 | ### Generate TLS certification:
49 | ```
50 | bash gen-cert.sh
51 | ```
52 | * CertFile will be generated under `tls/`
53 |
54 | ### run atophttpd daemon with TLS:
55 | ```
56 | make USE_TLS=YES
57 | /atophttpd -t 2868 -C tls/ca.crt -c tls/server.crt -k tls/server.key
58 | ```
59 |
60 | Or use default TLS config:
61 |
62 | * CA cert file default use `/etc/pki/CA/ca.crt`
63 | * Server cert file default use `/etc/pki/atophttpd/server.crt`
64 | * Server key file default use `/etc/pki/atophttpd/server.key`
65 |
66 | ### access atophttpd server with TLS:
67 | * By curl command:
68 | ```
69 | curl --cacert tls/ca.crt --cert tls/client.crt --key tls/client.key 'https://127.0.0.1:2868/showsamp?lables=ALL×tamp=1684402523&encoding=none'
70 | ```
71 |
72 | ## Limitation
73 | Currently, atophttpd supports atop v2.8 only.
74 |
--------------------------------------------------------------------------------
/connection.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 enhua zhou
3 | *
4 | * Authors:
5 | * enhua zhou
6 | *
7 | * Design and some codes are taken from redis.
8 | *
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 | * See the COPYING file in the top-level directory.
11 | */
12 |
13 | #ifndef _CONNECTION_H_
14 | #define _CONNECTION_H_
15 |
16 | #define CONN_TYPE_SOCKET "tcp"
17 | #define CONN_TYPE_TLS "tls"
18 | #define CONN_TYPE_MAX 8
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | typedef struct connection connection;
26 |
27 | typedef struct connection_type {
28 | const char* (*get_type)(struct connection* conn);
29 |
30 | void (*init)(void);
31 | void (*cleanup)(void);
32 | int (*configure)(void* priv, int reconfiguration);
33 |
34 | connection* (*conn_create)(int port, char *addr);
35 | int (*listen)(connection* listener);
36 |
37 | int (*accept)(struct connection* listener, struct connection* conn);
38 | void (*close)(struct connection* conn);
39 | void (*shutdown)(struct connection* conn);
40 |
41 | int (*write)(struct connection* conn, const void* data, size_t data_len);
42 | int (*writev)(struct connection* conn, const struct iovec* iov, int iovcnt);
43 | int (*read)(struct connection* conn, void* buf, size_t buf_len);
44 | } connection_type;
45 |
46 | struct connection {
47 | connection_type* type;
48 | int fd;
49 | int port;
50 | int is_local;
51 | char *bindaddr;
52 | };
53 |
54 | static inline int conn_configure(connection_type* ct, void* priv, int reconfigure) {
55 | return ct->configure(priv, reconfigure);
56 | }
57 |
58 | static inline connection* conn_create(connection_type* ct, int port, char *addr) {
59 | return ct->conn_create(port, addr);
60 | }
61 |
62 | static inline int conn_listen(connection* listener) {
63 | return listener->type->listen(listener);
64 | }
65 |
66 | static inline int conn_accept(connection* listener, connection* conn) {
67 | return conn->type->accept(listener, conn);
68 | }
69 |
70 | static inline void conn_close(connection* conn) {
71 | return conn->type->close(conn);
72 | }
73 |
74 | static inline void conn_shutdown(connection* conn) {
75 | return conn->type->shutdown(conn);
76 | }
77 |
78 | static inline int conn_write(connection* conn, const void* data, size_t data_len) {
79 | return conn->type->write(conn, data, data_len);
80 | }
81 |
82 | static inline int conn_writev(connection* conn, const struct iovec* iovs, int iovcnt) {
83 | return conn->type->writev(conn, iovs, iovcnt);
84 | }
85 |
86 | static inline int conn_read(connection* conn, void* buf, size_t buf_len) {
87 | return conn->type->read(conn, buf, buf_len);
88 | }
89 |
90 | void conntype_initialize(void);
91 | int conntype_register(connection_type* ct);
92 |
93 | int register_conntype_socket();
94 | int register_conntype_tls();
95 |
96 | int listen_to_port(int port, char* bindaddr, int af);
97 | connection_type* get_conntype_by_name(const char* typename);
98 | int get_conntype_index_by_name(const char* typename);
99 |
100 | #endif /* _CONNECTION_H_ */
101 |
--------------------------------------------------------------------------------
/http/template/html/generic.html:
--------------------------------------------------------------------------------
1 |
26 |
27 |
28 |
PID
29 |
SYSCPU
30 |
USRCPU
31 |
RDELAY
32 |
BDELAY
33 |
VGROW
34 |
RGROW
35 |
RDDSK
36 |
WRDSK
37 |
RUID
38 |
EUID
39 |
ST
40 |
EXC
41 |
THR
42 |
S
43 |
CPUNR
44 |
CPU
45 |
CMD
46 |
47 |
48 |
49 |
50 |
51 |
52 |
{pid}
53 |
{stime_unit_time}
54 |
{utime_unit_time}
55 |
{rundelay}
56 |
{blkdelay}
57 |
{vgrow}
58 |
{rgrow}
59 |
{rsz}
60 |
{wsz}
61 |
{ruid}
62 |
{euid}
63 |
{st}
64 |
{exitcode}
65 |
{nthr}
66 |
{state}
67 |
{curcpu}
68 |
{cpubusy}
69 |
{name}
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/socket.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 enhua zhou
3 | *
4 | * Authors:
5 | * enhua zhou
6 | *
7 | * Design and some codes are taken from redis.
8 | *
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 | * See the COPYING file in the top-level directory.
11 | */
12 | #include "httpd.h"
13 |
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 | static connection_type CT_Socket;
20 |
21 | static const char* conn_socket_get_type(connection* conn) {
22 | return CONN_TYPE_SOCKET;
23 | }
24 |
25 | static connection* conn_socket_create(int port, char *addr) {
26 | connection* conn = calloc(1, sizeof(connection));
27 | conn->type = &CT_Socket;
28 | conn->fd = -1;
29 | conn->port = port;
30 | conn->bindaddr = addr;
31 |
32 | return conn;
33 | }
34 |
35 | static int conn_socket_listen(connection* listener) {
36 | int listenfd = -1;
37 |
38 | if (listener->port == -1) {
39 | return -EINVAL;
40 | }
41 |
42 | if (strchr(listener->bindaddr, ':')) {
43 | listenfd = listen_to_port(listener->port, listener->bindaddr, AF_INET6);
44 | } else {
45 | listenfd = listen_to_port(listener->port, listener->bindaddr, AF_INET);
46 | }
47 |
48 | if (listenfd == -1) {
49 | return -EINVAL;
50 | }
51 |
52 | listener->fd = listenfd;
53 | // printf("socket listen on port %d, addr %s\n", listener->port, listener->bindaddr);
54 | return 0;
55 | }
56 |
57 | static int conn_socket_accept(connection* listener, connection* conn) {
58 | struct sockaddr_in cliaddr;
59 | socklen_t addrlen = sizeof(cliaddr);
60 |
61 | int clifd = accept(listener->fd, (struct sockaddr*)&cliaddr, &addrlen);
62 | if (clifd < 0)
63 | return -errno;
64 |
65 | conn->fd = clifd;
66 | return 0;
67 | }
68 |
69 | static void conn_socket_shutdown(connection* conn) {
70 | if (conn->fd == -1)
71 | return;
72 |
73 | shutdown(conn->fd, SHUT_RDWR);
74 | }
75 |
76 | static void conn_socket_close(connection* conn) {
77 | if (conn->fd == -1)
78 | return;
79 |
80 | close(conn->fd);
81 | conn->fd = -1;
82 | }
83 |
84 | static int conn_socket_write(struct connection* conn, const void* data, size_t data_len) {
85 | if (conn->fd == -1)
86 | return -EINVAL;
87 |
88 | return write(conn->fd, data, data_len);
89 | }
90 |
91 | static int conn_socket_writev(struct connection* conn, const struct iovec* iov, int iovcnt) {
92 | if (conn->fd == -1)
93 | return -EINVAL;
94 |
95 | return writev(conn->fd, iov, iovcnt);
96 | }
97 |
98 | static int conn_socket_read(struct connection* conn, void* buf, size_t buf_len) {
99 | if (conn->fd == -1)
100 | return -EINVAL;
101 |
102 | int ret = read(conn->fd, buf, buf_len);
103 | if (ret <= 0) {
104 | if (errno != EAGAIN) {
105 | return -errno;
106 | }
107 | return -EAGAIN;
108 | }
109 |
110 | return ret;
111 | }
112 |
113 | static connection_type CT_Socket = {
114 | .get_type = conn_socket_get_type,
115 |
116 | .init = NULL,
117 | .configure = NULL,
118 | .cleanup = NULL,
119 |
120 | .conn_create = conn_socket_create,
121 |
122 | .listen = conn_socket_listen,
123 | .accept = conn_socket_accept,
124 | .shutdown = conn_socket_shutdown,
125 | .close = conn_socket_close,
126 |
127 | .write = conn_socket_write,
128 | .writev = conn_socket_writev,
129 | .read = conn_socket_read};
130 |
131 | int register_conntype_socket() {
132 | return conntype_register(&CT_Socket);
133 | }
134 |
--------------------------------------------------------------------------------
/http/template/html/memory.html:
--------------------------------------------------------------------------------
1 |
26 |
27 |
PID
28 |
TID
29 |
MINFLT
30 |
MAJFLT
31 |
VSTEXT
32 |
VSLIBS
33 |
VDATA
34 |
VSTACK
35 |
LOCKSZ
36 |
VSIZE
37 |
RSIZE
38 |
PSIZE
39 |
VGROW
40 |
RGROW
41 |
SWAPSZ
42 |
RUID
43 |
EUID
44 |
MEM
45 |
CMD
46 |
47 |
48 |
49 |
50 |
51 |
52 |
{pid}
53 |
{tid}
54 |
{minflt}
55 |
{majflt}
56 |
{vexec}
57 |
{vlibs}
58 |
{vdata}
59 |
{vstack}
60 |
{vlock}
61 |
{vmem}
62 |
{rmem}
63 |
{pmem}
64 |
{vgrow}
65 |
{rgrow}
66 |
{vswap}
67 |
{ruid}
68 |
{euid}
69 |
{membusy}
70 |
{name}
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/connection.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 enhua zhou
3 | *
4 | * Authors:
5 | * enhua zhou
6 | *
7 | * Design and some codes are taken from redis.
8 | *
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 | * See the COPYING file in the top-level directory.
11 | */
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | #include "assert.h"
21 | #include "connection.h"
22 |
23 | static connection_type* conn_types[CONN_TYPE_MAX];
24 |
25 | void conntype_initialize(void) {
26 | register_conntype_socket();
27 | register_conntype_tls();
28 | }
29 |
30 | int conntype_register(connection_type* ct) {
31 | const char* typename = ct->get_type(NULL);
32 | connection_type* tmpct;
33 | int type;
34 |
35 | /* find an empty slot to store the new connection type */
36 | for (type = 0; type < CONN_TYPE_MAX; type++) {
37 | tmpct = conn_types[type];
38 | if (!tmpct)
39 | break;
40 |
41 | /* ignore case, we really don't care "tls"/"TLS" */
42 | if (!strcasecmp(typename, tmpct->get_type(NULL))) {
43 | printf("Connection types %s already registered\n", typename);
44 | return -errno;
45 | }
46 | }
47 |
48 | if (type == CONN_TYPE_MAX)
49 | return -ENOMEM;
50 |
51 | conn_types[type] = ct;
52 |
53 | if (ct->init) {
54 | ct->init();
55 | }
56 |
57 | printf("Connection type %s registered\n", typename);
58 | return -errno;
59 | }
60 |
61 | connection_type* get_conntype_by_name(const char* typename) {
62 | connection_type* ct;
63 |
64 | for (int type = 0; type < CONN_TYPE_MAX; type++) {
65 | ct = conn_types[type];
66 | if (!ct)
67 | break;
68 |
69 | if (!strcasecmp(typename, ct->get_type(NULL)))
70 | return ct;
71 | }
72 |
73 | printf("Missing implement of connection type %s\n", typename);
74 |
75 | return NULL;
76 | }
77 |
78 | int get_conntype_index_by_name(const char* typename) {
79 | connection_type* ct;
80 | for (int type = 0; type < CONN_TYPE_MAX; type++) {
81 | ct = conn_types[type];
82 | if (!ct)
83 | break;
84 |
85 | if (!strcasecmp(typename, ct->get_type(NULL)))
86 | return type;
87 | }
88 |
89 | printf("Missing implement of connection type %s. Please re-compile atophttpd.\n", typename);
90 |
91 | return -1;
92 | }
93 |
94 | int listen_to_port(int port, char* bindaddr, int af) {
95 | struct addrinfo hints;
96 | struct addrinfo *res, *p;
97 | int ret, sockfd = -1;
98 | char _port[6];
99 | static const int reuse = 1;
100 |
101 | snprintf(_port, 6, "%d", port);
102 | memset(&hints, 0, sizeof(hints));
103 | hints.ai_family = af;
104 | hints.ai_socktype = SOCK_STREAM;
105 | hints.ai_flags = AI_PASSIVE;
106 |
107 | if (bindaddr && !strcmp("*", bindaddr))
108 | bindaddr = NULL;
109 | if (af == AF_INET6 && bindaddr && !strcmp("::*", bindaddr))
110 | bindaddr = NULL;
111 |
112 | ret = getaddrinfo(bindaddr, _port, &hints, &res);
113 | if (ret != 0) {
114 | perror("failed get addr info: ");
115 | goto error;
116 | }
117 |
118 | for (p = res; p != NULL; p = p->ai_next) {
119 | sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
120 | if (sockfd == -1) {
121 | perror("failed get socket fd: ");
122 | continue;
123 | }
124 |
125 | ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse));
126 | if (ret < 0) {
127 | perror("socket set REUSEADDR failed: ");
128 | goto error;
129 | }
130 | ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse));
131 | if (ret < 0) {
132 | perror("socket set REUSEPORT failed: ");
133 | goto error;
134 | }
135 |
136 | ret = bind(sockfd, p->ai_addr, p->ai_addrlen);
137 | if (ret == -1) {
138 | perror("socket bind failed: ");
139 | close(sockfd);
140 | sockfd = -1;
141 | continue;
142 | }
143 |
144 | ret = listen(sockfd, 128);
145 | if (ret == -1) {
146 | perror("socket listen failed: ");
147 | close(sockfd);
148 | sockfd = -1;
149 | continue;
150 | }
151 |
152 | goto end;
153 | }
154 |
155 | error:
156 | if (sockfd != -1) {
157 | close(sockfd);
158 | sockfd = -1;
159 | }
160 |
161 | end:
162 | freeaddrinfo(res);
163 | return sockfd;
164 | }
165 |
--------------------------------------------------------------------------------
/cache.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 zhenwei pi
3 | *
4 | * Authors:
5 | * zhenwei pi
6 | *
7 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
8 | * See the COPYING file in the top-level directory.
9 | */
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | #include "cache.h"
17 |
18 | #define RECORDS_TRUNK (60 * 60 / 10)
19 |
20 | static int nr_caches;
21 | static struct cache_t **caches;
22 |
23 | struct cache_t *cache_find(const char *name)
24 | {
25 | for (int i = 0; i < nr_caches; i++) {
26 | struct cache_t *cache = caches[i];
27 | assert(cache);
28 | if (!strcmp(cache->name, name))
29 | return cache;
30 | }
31 |
32 | return NULL;
33 | }
34 |
35 | struct cache_t *cache_alloc(const char *name)
36 | {
37 | struct cache_t *cache = cache_find(name);
38 |
39 | assert(!cache);
40 |
41 | cache = calloc(1, sizeof(*cache));
42 | assert(cache);
43 |
44 | cache->name = strdup(name);
45 | assert(cache->name);
46 |
47 | cache->nr_elems = 0;
48 | cache->max_elems = RECORDS_TRUNK;
49 | cache->elems = calloc(cache->max_elems, sizeof(struct cache_elem_t));
50 | assert(cache->elems);
51 |
52 | if (!caches)
53 | caches = calloc(1, sizeof(struct cache_t *));
54 | else
55 | caches = realloc(caches, sizeof(struct cache_t *) * (nr_caches + 1));
56 |
57 | caches[nr_caches++] = cache;
58 |
59 | return cache;
60 | }
61 |
62 | void cache_free(const char *name)
63 | {
64 | struct cache_t *cache = cache_find(name);
65 |
66 | if (!cache)
67 | return;
68 |
69 | free(cache->elems);
70 | free(cache->name);
71 | free(cache);
72 |
73 | for (int i = 0; i < nr_caches; i++) {
74 | if (caches[i] == cache) {
75 | memmove(&caches[i], &caches[i + 1], (nr_caches - i - 1) * sizeof(caches[0]));
76 | break;
77 | }
78 | }
79 |
80 | nr_caches--;
81 | caches = realloc(caches, sizeof(struct cache_t *) * (nr_caches));
82 | }
83 |
84 | static struct cache_t *__cache_get(time_t time)
85 | {
86 | for (int i = 0; i < nr_caches; i++) {
87 | struct cache_t *cache = caches[i];
88 | struct cache_elem_t *first_elem, *last_elem;
89 |
90 | assert(cache->nr_elems);
91 | first_elem = &cache->elems[0];
92 | last_elem = &cache->elems[cache->nr_elems - 1];
93 | if ((time >= first_elem->time) && (time <= last_elem->time))
94 | return cache;
95 | }
96 |
97 | return NULL;
98 | }
99 |
100 | struct cache_t *cache_get(time_t time, off_t *off)
101 | {
102 | struct cache_t *cache = __cache_get(time);
103 |
104 | if (!cache)
105 | return NULL;
106 |
107 | if (cache->nr_elems <= 2) {
108 | *off = cache->elems[0].off;
109 | return cache;
110 | }
111 |
112 | int left = 0;
113 | int right = cache->nr_elems;
114 |
115 | while (left < right) {
116 | int mid = (left + right) >> 1;
117 | struct cache_elem_t *elem = &cache->elems[mid];
118 | if (time == elem->time) {
119 | *off = elem->off;
120 | return cache;
121 | }
122 |
123 | if (time < elem->time) {
124 | right = mid;
125 | } else {
126 | left = mid + 1;
127 | }
128 | }
129 |
130 | struct cache_elem_t *elem = &cache->elems[left];
131 | *off = elem->off;
132 | return cache;
133 | }
134 |
135 | static int cache_elems_cmp(const void *p1, const void *p2)
136 | {
137 | struct cache_elem_t *e1 = (struct cache_elem_t *)p1;
138 | struct cache_elem_t *e2 = (struct cache_elem_t *)p2;
139 |
140 | return e1->time > e2->time;
141 | }
142 |
143 | void cache_set(struct cache_t *cache, time_t time, off_t off)
144 | {
145 | if (cache->nr_elems == cache->max_elems) {
146 | cache->max_elems += RECORDS_TRUNK;
147 | cache->elems = realloc(cache->elems, sizeof(struct cache_elem_t) * cache->max_elems);
148 | }
149 |
150 | struct cache_elem_t *new_elem = &cache->elems[cache->nr_elems++];
151 | new_elem->time = time;
152 | new_elem->off = off;
153 |
154 | if (cache->nr_elems == 1)
155 | return;
156 |
157 | struct cache_elem_t *last_elem = &cache->elems[cache->nr_elems - 2];
158 | if (last_elem->time > new_elem->time) {
159 | qsort(cache->elems, cache->nr_elems, sizeof(struct cache_elem_t), cache_elems_cmp);
160 | }
161 | }
162 |
163 | static int cache_cmp(const void *p1, const void *p2)
164 | {
165 | struct cache_t **c1 = (struct cache_t **)p1;
166 | struct cache_t **c2 = (struct cache_t **)p2;
167 |
168 | return (*c1)->elems[0].time > (*c2)->elems[0].time;
169 | }
170 |
171 | void cache_sort()
172 | {
173 | qsort(caches, nr_caches, sizeof(struct cache_t *), cache_cmp);
174 | }
175 |
176 | struct cache_t *cache_get_recent()
177 | {
178 | if (!nr_caches)
179 | return NULL;
180 |
181 | return caches[nr_caches - 1];
182 | }
183 |
184 | #ifdef CACHE_TEST
185 | int main()
186 | {
187 | assert(!cache_find("test0"));
188 |
189 | struct cache_t *cache0 = cache_alloc("test0");
190 | cache_set(cache0, 100, 1000);
191 | assert(cache0->elems[0].time == 100);
192 | cache_set(cache0, 102, 1002);
193 | assert(cache0->elems[0].time == 100);
194 | assert(cache0->elems[1].time == 102);
195 | cache_set(cache0, 101, 1001);
196 | assert(cache0->nr_elems == 3);
197 | assert(cache0->elems[0].time < cache0->elems[1].time);
198 | assert(cache0->elems[1].time < cache0->elems[2].time);
199 |
200 | struct cache_t *cache1 = cache_alloc("test1");
201 | cache_set(cache1, 200, 2000);
202 | cache_free("test0");
203 | assert(nr_caches == 1);
204 | assert(caches[0]->elems[0].time == 200);
205 | assert(caches[0]->elems[0].off == 2000);
206 |
207 | return 0;
208 | }
209 | #endif
210 |
--------------------------------------------------------------------------------
/tls.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 enhua zhou
3 | *
4 | * Authors:
5 | * enhua zhou
6 | *
7 | * Design and some codes are taken from redis.
8 | *
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 | * See the COPYING file in the top-level directory.
11 | */
12 |
13 | #include "httpd.h"
14 |
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #ifdef USE_TLS
22 | typedef struct tls_connection {
23 | connection c;
24 | SSL* ssl;
25 | } tls_connection;
26 |
27 | SSL_CTX* tls_ctx = NULL;
28 |
29 | static connection_type CT_TLS;
30 |
31 | static const char* conn_tls_get_type(connection* conn) {
32 | return CONN_TYPE_TLS;
33 | }
34 |
35 | static void tls_init(void) {
36 | SSL_library_init();
37 | OpenSSL_add_all_algorithms();
38 | SSL_load_error_strings();
39 | }
40 |
41 | static int tls_configure(void* priv, int reconfigure) {
42 | atophttpd_tls_context_config* ctx_config = (atophttpd_tls_context_config*)priv;
43 | SSL_CTX* ctx = NULL;
44 |
45 | if (!ctx_config->cert_file) {
46 | perror("No tls-cert-file configured!");
47 | goto error;
48 | }
49 |
50 | if (!ctx_config->key_file) {
51 | perror("No tls-key-file configured!");
52 | goto error;
53 | }
54 |
55 | if (!ctx_config->ca_cert_file) {
56 | perror("No tls-ca-cert-file configured!");
57 | goto error;
58 | }
59 |
60 | ctx = SSL_CTX_new(TLS_method());
61 | if (!ctx) {
62 | perror("Create SSL context Error");
63 | goto error;
64 | }
65 |
66 | if (SSL_CTX_use_certificate_file(ctx, ctx_config->cert_file, SSL_FILETYPE_PEM) <= 0) {
67 | perror("ERROR loading server certificate");
68 | goto error;
69 | }
70 |
71 | if (SSL_CTX_use_PrivateKey_file(ctx, ctx_config->key_file, SSL_FILETYPE_PEM) <= 0) {
72 | perror("ERROR loading server private key");
73 | goto error;
74 | }
75 |
76 | if (!SSL_CTX_check_private_key(ctx)) {
77 | perror("Private key does not match certificate\n");
78 | goto error;
79 | }
80 |
81 | if (SSL_CTX_load_verify_locations(ctx, ctx_config->ca_cert_file, NULL) <= 0) {
82 | perror("ERROR loading ca certificate");
83 | goto error;
84 | }
85 |
86 | SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
87 |
88 | SSL_CTX_free(tls_ctx);
89 | tls_ctx = ctx;
90 | return 0;
91 |
92 | error:
93 | if (ctx)
94 | SSL_CTX_free(ctx);
95 | return -errno;
96 | }
97 |
98 | static void tls_cleanup(void) {
99 | if (tls_ctx) {
100 | SSL_CTX_free(tls_ctx);
101 | tls_ctx = NULL;
102 | }
103 | }
104 |
105 | static connection* conn_tls_create(int port, char *addr) {
106 | tls_connection* conn = calloc(1, sizeof(tls_connection));
107 | conn->c.type = &CT_TLS;
108 | conn->c.fd = -1;
109 | conn->c.port = port;
110 | conn->c.bindaddr = addr;
111 |
112 | return (connection*)conn;
113 | }
114 |
115 | static int conn_tls_listen(connection* listener) {
116 | // printf("tls listen on port %d, addr %s\n", listener->port, listener->bindaddr);
117 | return get_conntype_by_name(CONN_TYPE_SOCKET)->listen(listener);
118 | }
119 |
120 |
121 | static int conn_tls_accept(connection * listener, connection* conn) {
122 | int ret = get_conntype_by_name(CONN_TYPE_SOCKET)->accept(listener, conn);
123 | if (ret < 0) {
124 | perror("tls conn accept failed at tcp accept.");
125 | return -errno;
126 | }
127 |
128 | tls_connection* tls_conn = (tls_connection*)conn;
129 | SSL_CTX* ctx = tls_ctx;
130 |
131 | if (!ctx) {
132 | perror("tls conn create but ctx is null");
133 | return -EINVAL;
134 | }
135 | tls_conn->ssl = SSL_new(ctx);
136 |
137 | if (tls_conn->ssl == NULL) {
138 | perror("accept ssl is null!");
139 | return -EINVAL;
140 | }
141 |
142 | if (tls_conn->c.fd == -1) {
143 | perror("tls clifd is illegal!");
144 | return -EINVAL;
145 | }
146 |
147 | SSL_set_fd(tls_conn->ssl, tls_conn->c.fd);
148 | if (SSL_accept(tls_conn->ssl) <= 0) {
149 | printf("SSL_TLS handshake failed: %s\n", ERR_error_string(ERR_get_error(), NULL));
150 | return -errno;
151 | }
152 |
153 | return 0;
154 | }
155 |
156 | static void conn_shutdown_tls(connection* conn) {
157 | tls_connection* tls_conn = (tls_connection*)conn;
158 |
159 | if (tls_conn->ssl) {
160 | SSL_shutdown(tls_conn->ssl);
161 | SSL_free(tls_conn->ssl);
162 |
163 | tls_conn->ssl = NULL;
164 | }
165 |
166 | get_conntype_by_name(CONN_TYPE_SOCKET)->shutdown(conn);
167 | return;
168 | }
169 |
170 | static void conn_close_tls(connection* conn) {
171 | tls_connection* tls_conn = (tls_connection*)conn;
172 |
173 | if (tls_conn->ssl) {
174 | SSL_shutdown(tls_conn->ssl);
175 | SSL_free(tls_conn->ssl);
176 |
177 | tls_conn->ssl = NULL;
178 | }
179 |
180 | get_conntype_by_name(CONN_TYPE_SOCKET)->close(conn);
181 | return;
182 | }
183 |
184 | static int conn_write_tls(connection* conn, const void* data, size_t data_len) {
185 | tls_connection* tls_conn = (tls_connection*)conn;
186 | if (tls_conn->ssl == NULL) {
187 | return -EINVAL;
188 | }
189 |
190 | int ret = SSL_write(tls_conn->ssl, data, data_len);
191 | if (ret < 0) {
192 | perror("SSL write error");
193 | return -errno;
194 | }
195 |
196 | return 0;
197 | }
198 |
199 | static int conn_writev_tls(connection* conn, const struct iovec* iov, int iovcnt) {
200 | tls_connection* tls_conn = (tls_connection*)conn;
201 |
202 | if (tls_conn->ssl == NULL) {
203 | return -EINVAL;
204 | }
205 |
206 | size_t iov_bytes_len = 0;
207 | for (int i = 0; i < 3; i++) {
208 | iov_bytes_len += iov[i].iov_len;
209 | }
210 |
211 | char *ssl_buf;
212 | ssl_buf = malloc(iov_bytes_len);
213 | if (!ssl_buf) {
214 | perror("SSL have no mem");
215 | return -ENOMEM;
216 | }
217 | size_t offset = 0;
218 | for (int i = 0; i < iovcnt; i++) {
219 | memcpy(ssl_buf + offset, iov[i].iov_base, iov[i].iov_len);
220 | offset += iov[i].iov_len;
221 | }
222 |
223 | int ret = SSL_write(tls_conn->ssl, ssl_buf, iov_bytes_len);
224 | if (ret < 0) {
225 | perror("SSL writev error");
226 | ret = -errno;
227 | }
228 |
229 | free(ssl_buf);
230 | return ret;
231 | }
232 |
233 | static int conn_read_tls(connection* conn, void* buf, size_t buf_len) {
234 | tls_connection* tls_conn = (tls_connection*)conn;
235 |
236 | if (tls_conn->ssl == NULL) {
237 | return -EINVAL;
238 | }
239 |
240 | int ret = SSL_read(tls_conn->ssl, buf, buf_len);
241 | if (ret <= 0) {
242 | if (errno != EAGAIN) {
243 | return -errno;
244 | }
245 |
246 | return -EAGAIN;
247 | }
248 | return ret;
249 | }
250 |
251 | static connection_type CT_TLS = {
252 | .get_type = conn_tls_get_type,
253 |
254 | .init = tls_init,
255 | .configure = tls_configure,
256 | .cleanup = tls_cleanup,
257 |
258 | .conn_create = conn_tls_create,
259 |
260 | .listen = conn_tls_listen,
261 |
262 | .accept = conn_tls_accept,
263 | .shutdown = conn_shutdown_tls,
264 | .close = conn_close_tls,
265 |
266 | .write = conn_write_tls,
267 | .writev = conn_writev_tls,
268 | .read = conn_read_tls};
269 |
270 | int register_conntype_tls() {
271 | return conntype_register(&CT_TLS);
272 | }
273 |
274 | #else
275 |
276 | int register_conntype_tls() {
277 | printf("ConnectionType %s not builtin\n", CONN_TYPE_TLS);
278 | return -EINVAL;
279 | }
280 |
281 | #endif
282 |
--------------------------------------------------------------------------------
/rawlog.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 zhenwei pi
3 | *
4 | * Authors:
5 | * zhenwei pi
6 | *
7 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
8 | * See the COPYING file in the top-level directory.
9 | */
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include "config.h"
27 |
28 | #include "atop.h"
29 | #include "cache.h"
30 | #include "httpd.h"
31 | #include "json.h"
32 | #include "rawlog.h"
33 |
34 | /* a little tricky: implemented in atop/version.c */
35 | unsigned short getnumvers(void);
36 |
37 | static int rawlog_verify_rawheader(struct rawheader *rh)
38 | {
39 | if (rh->magic != MYMAGIC)
40 | return -EINVAL;
41 |
42 | if ((rh->rawheadlen != sizeof(struct rawheader)) ||
43 | (rh->rawreclen != sizeof(struct rawrecord)) ||
44 | (rh->sstatlen != sizeof(struct sstat)) ||
45 | (rh->tstatlen != sizeof(struct tstat))) {
46 | return -EINVAL;
47 | }
48 |
49 | if (rh->aversion & 0x8000 && (rh->aversion & 0x7fff) != getnumvers())
50 | return -EINVAL;
51 |
52 | return 0;
53 | }
54 |
55 | static int rawlog_rebuild_one(struct cache_t *cache)
56 | {
57 | struct cache_elem_t *elem = &cache->elems[cache->nr_elems - 1];
58 | struct rawrecord rr;
59 | int ret = 0;
60 |
61 | int fd = open(cache->name, O_RDONLY);
62 | if (fd < 0) {
63 | printf("%s: open \"%s\" failed: %m\n", __func__, cache->name);
64 | return -errno;
65 | }
66 |
67 | off_t off = elem->off;
68 | ssize_t len = pread(fd, &rr, sizeof(rr), off);
69 | if (len < sizeof(rr))
70 | goto close_fd;
71 |
72 | off = off + sizeof(rr) + rr.scomplen + rr.pcomplen;
73 | while (1) {
74 | len = pread(fd, &rr, sizeof(rr), off);
75 | if (len < sizeof(rr))
76 | break;
77 |
78 | cache_set(cache, rr.curtime, off);
79 | off = off + sizeof(rr) + rr.scomplen + rr.pcomplen;
80 | }
81 |
82 | log_debug("\"%s\" has %d records of time[%ld - %ld]\n", cache->name,
83 | cache->nr_elems, cache->elems[0].time, cache->elems[cache->nr_elems - 1].time);
84 |
85 | struct stat statbuf;
86 | ret = fstat(fd, &statbuf);
87 | if (ret < 0) {
88 | printf("%s: fstat \"%s\" failed: %m\n", __func__, cache->name);
89 | ret = -errno;
90 | goto close_fd;
91 | }
92 |
93 | cache->st_size = statbuf.st_size;
94 | cache->st_mtim = statbuf.st_mtim;
95 |
96 | close_fd:
97 | close(fd);
98 | return ret;
99 | }
100 |
101 | static int rawlog_parse_one(const char *path)
102 | {
103 | struct rawheader rh;
104 | struct rawrecord rr;
105 | struct stat statbuf;
106 | struct cache_t *cache;
107 | ssize_t len;
108 | off_t off;
109 | int fd;
110 | int ret;
111 |
112 | /* if we have already build a cache, rebuild it */
113 | cache = cache_find(path);
114 | if (cache) {
115 | ret = stat(path, &statbuf);
116 | if (ret < 0) {
117 | printf("%s: stat \"%s\" failed: %m\n", __func__, path);
118 | return -errno;
119 | }
120 |
121 | if (cache->st_size == statbuf.st_size) {
122 | return 0;
123 | }
124 |
125 | return rawlog_rebuild_one(cache);
126 | }
127 |
128 | fd = open(path, O_RDONLY);
129 | if (fd < 0) {
130 | printf("%s: open \"%s\" failed: %m\n", __func__, path);
131 | return -errno;
132 | }
133 |
134 | /* 1, read rawheader */
135 | len = read(fd, &rh, sizeof(rh));
136 | if (len < sizeof(rh)) {
137 | printf("%s: \"%s\" is not atop rawlog file\n", __func__, path);
138 | ret = -EINVAL;
139 | goto close_fd;
140 | }
141 |
142 | ret = rawlog_verify_rawheader(&rh);
143 | if (ret) {
144 | printf("%s: \"%s\" verify rawheader failed\n", __func__, path);
145 | goto close_fd;
146 | }
147 |
148 | /* 2, create cache for this file */
149 | cache = cache_alloc(path);
150 | if (!cache) {
151 | ret = -ENOMEM;
152 | goto close_fd;
153 | }
154 | cache->flags = rh.supportflags;
155 |
156 | /* 3, read all rawrecords, cache time&off mapping */
157 | off = lseek(fd, 0, SEEK_CUR);
158 | if (off < 0) {
159 | printf("%s: move offset failed\n", __func__);
160 | ret = -errno;
161 | goto close_fd;
162 | }
163 | while (1) {
164 | len = pread(fd, &rr, sizeof(rr), off);
165 | if (len < sizeof(rr))
166 | break;
167 |
168 | cache_set(cache, rr.curtime, off);
169 | off = off + sizeof(rr) + rr.scomplen + rr.pcomplen;
170 | }
171 |
172 | /* 4, update rawlog size & st_mtim */
173 | ret = fstat(fd, &statbuf);
174 | if (ret < 0) {
175 | printf("%s: fstat \"%s\" failed", __func__, path);
176 | ret = -errno;
177 | goto close_fd;
178 | }
179 | cache->st_size = statbuf.st_size;
180 | cache->st_mtim = statbuf.st_mtim;
181 |
182 | if (!cache->nr_elems)
183 | goto free_cache;
184 |
185 | log_debug("\"%s\" has %d records of time[%ld - %ld]\n", path, cache->nr_elems,
186 | cache->elems[0].time, cache->elems[cache->nr_elems - 1].time);
187 | ret = 0;
188 | goto close_fd;
189 |
190 | free_cache:
191 | cache_free(path);
192 |
193 | close_fd:
194 | close(fd);
195 |
196 | return ret;
197 | }
198 |
199 | int rawlog_parse_all(const char *path)
200 | {
201 | struct dirent *dirent;
202 | DIR *dir = opendir(path);
203 | char name[PATH_MAX] = {0};
204 |
205 | if (!dir) {
206 | printf("%s: open \"%s\" failed: %m\n", __func__, path);
207 | return -errno;
208 | }
209 |
210 | while ((dirent = readdir(dir))) {
211 | if (dirent->d_type != DT_REG)
212 | continue;
213 |
214 | sprintf(name, "%s/%s", path, dirent->d_name);
215 | rawlog_parse_one(name);
216 | }
217 |
218 | cache_sort();
219 |
220 | closedir(dir);
221 |
222 | return 0;
223 | }
224 |
225 | static int rawlog_uncompress_record(int fd, void *outbuf, unsigned long *outlen, unsigned long inlen)
226 | {
227 | Byte *inbuf;
228 | int ret = 0;
229 |
230 | inbuf = malloc(inlen);
231 | if (!inbuf)
232 | return -ENOMEM;
233 |
234 | if (read(fd, inbuf, inlen) != inlen) {
235 | ret = -EIO;
236 | goto free_buf;
237 | }
238 |
239 | if (uncompress(outbuf, outlen, inbuf, inlen) != Z_OK)
240 | ret = -ENODATA;
241 |
242 | free_buf:
243 | free(inbuf);
244 |
245 | return ret;
246 | }
247 |
248 | static int rawlog_get_sstat(int fd, struct sstat *sstat, unsigned long len)
249 | {
250 | unsigned long outlen = sizeof(struct sstat);
251 |
252 | return rawlog_uncompress_record(fd, sstat, &outlen, len);
253 | }
254 |
255 | static void rawlog_free_devtstat(struct devtstat *devtstat)
256 | {
257 | free(devtstat->taskall);
258 | free(devtstat->procall);
259 | free(devtstat->procactive);
260 | }
261 |
262 | static int rawlog_get_devtstat(int fd, struct devtstat *devtstat, struct rawrecord *rr)
263 | {
264 | unsigned long outlen = sizeof(struct tstat) * rr->ndeviat;
265 | int ret;
266 | unsigned long ntaskall = 0, nprocall = 0, nprocactive = 0, ntaskactive = 0;
267 |
268 | /* 1, allocate memory */
269 | memset(devtstat, 0x00, sizeof(struct devtstat));
270 | devtstat->taskall = malloc(outlen);
271 | if (!devtstat->taskall) {
272 | ret = -ENOMEM;
273 | goto free_devtstat;
274 | }
275 |
276 | devtstat->procall = malloc(sizeof(struct tstat*) * rr->totproc);
277 | if (!devtstat->procall) {
278 | ret = -ENOMEM;
279 | goto free_devtstat;
280 | }
281 |
282 | devtstat->procactive = malloc(sizeof(struct tstat*) * rr->nactproc);
283 | if (!devtstat->procactive) {
284 | ret = -ENOMEM;
285 | goto free_devtstat;
286 | }
287 |
288 | /* 2, read record and uncompress */
289 | ret = rawlog_uncompress_record(fd, devtstat->taskall, &outlen, rr->pcomplen);
290 | if (ret)
291 | goto free_devtstat;
292 |
293 | /* 3, build devtstat */
294 | for ( ; ntaskall < rr->ndeviat; ntaskall++)
295 | {
296 | struct tstat *tstat = devtstat->taskall + ntaskall;
297 | if (tstat->gen.isproc)
298 | {
299 | devtstat->procall[nprocall++] = tstat;
300 | if (!tstat->gen.wasinactive)
301 | devtstat->procactive[nprocactive++] = tstat;
302 | }
303 |
304 | if (!tstat->gen.wasinactive)
305 | ntaskactive++;
306 | }
307 |
308 | devtstat->ntaskall = ntaskall;
309 | devtstat->nprocall = nprocall;
310 | devtstat->nprocactive = nprocactive;
311 | devtstat->ntaskactive = ntaskactive;
312 | devtstat->totrun = rr->totrun;
313 | devtstat->totslpi = rr->totslpi;
314 | devtstat->totslpu = rr->totslpu;
315 | devtstat->totzombie = rr->totzomb;
316 |
317 |
318 | return 0;
319 |
320 | free_devtstat:
321 | rawlog_free_devtstat(devtstat);
322 |
323 | return ret;
324 | }
325 |
326 | static int rawlog_record_flags(int hflags, int rflags)
327 | {
328 | int ret = 0;
329 |
330 | if (hflags & RAWLOGNG) {
331 | if (rflags & RRACCTACTIVE)
332 | ret |= ACCTACTIVE;
333 |
334 | if (rflags & RRIOSTAT)
335 | ret |= IOSTAT;
336 | }
337 |
338 | if (rflags & RRNETATOP)
339 | ret |= NETATOP;
340 |
341 | if (rflags & RRNETATOPD)
342 | ret |= NETATOPD;
343 |
344 | if (rflags & RRCGRSTAT)
345 | ret |= CGROUPV2;
346 |
347 | if (rflags & RRDOCKSTAT)
348 | ret |= DOCKSTAT;
349 |
350 | if (rflags & RRGPUSTAT)
351 | ret |= GPUSTAT;
352 |
353 | return ret;
354 | }
355 |
356 | int rawlog_get_record(time_t ts, char *labels, connection *conn)
357 | {
358 | struct rawrecord rr;
359 | struct sstat *sstat;
360 | struct devtstat devtstat;
361 | ssize_t len;
362 | int fd;
363 | int ret = 0;
364 | int flags;
365 | off_t off;
366 | time_t recent_ts;
367 |
368 | sstat = malloc(sizeof(struct sstat));
369 | if (sstat == NULL) {
370 | log_debug("can't alloc mem for sstat\n");
371 | return -ENOMEM;
372 | }
373 |
374 | struct cache_t *cache = cache_get(ts, &off);
375 | if (cache)
376 | goto found;
377 |
378 | log_debug("no record @%ld\n", ts);
379 |
380 | cache = cache_get_recent();
381 | if (!cache) {
382 | ret = -EIO;
383 | goto free_sstat;
384 | }
385 |
386 | recent_ts = cache->elems[cache->nr_elems - 1].time;
387 | if (ts < recent_ts) {
388 | ret = -EIO;
389 | goto free_sstat;
390 | }
391 |
392 | off = cache->elems[cache->nr_elems - 1].off;
393 | log_debug("use recent @%ld from %s\n", cache->elems[cache->nr_elems - 1].time, cache->name);
394 |
395 | found:
396 | log_debug("time %ld, off %ld in %s\n", ts, off, cache->name);
397 |
398 | fd = open(cache->name, O_RDONLY);
399 | if (fd < 0) {
400 | printf("%s: open \"%s\" failed: %m\n", __func__, cache->name);
401 | ret = -errno;
402 | goto free_sstat;
403 | }
404 |
405 | ret = lseek(fd, off, SEEK_CUR);
406 | if (ret < 0) {
407 | printf("%s: move offset failed\n", __func__);
408 | ret = -errno;
409 | goto close_fd;
410 | }
411 |
412 | len = read(fd, &rr, sizeof(rr));
413 | if (len != sizeof(rr)) {
414 | printf("%s: time %ld, off %ld in %s, incomplete record\n", __func__, ts, off, cache->name);
415 | ret = -EIO;
416 | goto close_fd;
417 | }
418 | log_debug("time %ld, off %ld in %s, rr.curtime %ld\n", ts, off, cache->name, rr.curtime);
419 |
420 | ret = rawlog_get_sstat(fd, sstat, rr.scomplen);
421 | if (ret) {
422 | printf("%s: time %ld, off %ld in %s, get sstat failed\n", __func__, ts, off, cache->name);
423 | goto close_fd;
424 | }
425 |
426 | ret = rawlog_get_devtstat(fd, &devtstat, &rr);
427 | if (ret) {
428 | printf("%s: time %ld, off %ld in %s, get devtstat failed\n", __func__, ts, off, cache->name);
429 | goto close_fd;
430 | }
431 |
432 | flags = rawlog_record_flags(cache->flags, rr.flags);
433 | jsonout(flags, labels, rr.curtime, rr.interval, &devtstat, sstat, rr.nexit, rr.noverflow, 0, conn);
434 |
435 | rawlog_free_devtstat(&devtstat);
436 |
437 | close_fd:
438 | close(fd);
439 | free_sstat:
440 | free(sstat);
441 |
442 | return ret;
443 | }
444 |
--------------------------------------------------------------------------------
/http/js/atop.js:
--------------------------------------------------------------------------------
1 | /*
2 | ** This is a JavaScript for ATOP Http Server
3 | **
4 | ** ==========================================================================
5 | ** Author: Enhua Zhou
6 | ** E-mail: zhouenhua@bytedance.com
7 | ** Date: September 2022
8 | ** --------------------------------------------------------------------------
9 | ** Copyright (C) 2022 Enhua Zhou
10 | **
11 | ** This program is free software; you can redistribute it and/or modify it
12 | ** under the terms of the GNU General Public License as published by the
13 | ** Free Software Foundation; either version 2, or (at your option) any
14 | ** later version.
15 | **
16 | ** This program is distributed in the hope that it will be useful, but
17 | ** WITHOUT ANY WARRANTY; without even the implied warranty of
18 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 | ** See the GNU General Public License for more details.
20 | **
21 | ** You should have received a copy of the GNU General Public License
22 | ** along with this program; if not, write to the Free Software
23 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 | ** --------------------------------------------------------------------------
25 | */
26 |
27 | const OrderType = {
28 | CPU: 'CPU',
29 | DISK: 'DISK',
30 | MEM: 'MEM',
31 | }
32 | Object.freeze(OrderType);
33 |
34 | const TempleteType = {
35 | CommandLine: 'command_line',
36 | Disk: 'disk',
37 | Generic: 'generic',
38 | Memory: 'memory'
39 | }
40 | Object.freeze(TempleteType);
41 |
42 | const DEFAULT_PROC_SHOW_NUM = 200;
43 |
44 | const DEFAULT_CPU_SHOW_NUM = 0;
45 | const DEFAULT_GPU_SHOW_NUM = 2;
46 | const DEFAULT_DISK_SHOW_NUM = 1;
47 | const DEFAULT_LVM_SHOW_NUM = 4;
48 | const DEFAULT_INTERFACE_SHOW_NUM = 2;
49 | const DEFAULT_INFINIBAND_SHOW_NUM = 2;
50 | const DEFAULT_NFS_SHOW_NUM = 2;
51 | const DEFAULT_CONTAINER_SHOW_NUM = 1;
52 | const DEFAULT_NUMA_SHOW_NUM = 0;
53 | const DEFAULT_LLC_SHOW_NUM = 0;
54 |
55 | var proc_show_num = DEFAULT_PROC_SHOW_NUM;
56 | var cpu_show_num = DEFAULT_CPU_SHOW_NUM;
57 | var gpu_show_num = DEFAULT_GPU_SHOW_NUM;
58 | var lvm_show_num = DEFAULT_LVM_SHOW_NUM;
59 | var disk_show_num = DEFAULT_DISK_SHOW_NUM;
60 | var interface_show_num = DEFAULT_INTERFACE_SHOW_NUM;
61 | var infiniband_show_num = DEFAULT_INFINIBAND_SHOW_NUM;
62 | var nfs_show_num = DEFAULT_NFS_SHOW_NUM;
63 | var container_show_num = DEFAULT_CONTAINER_SHOW_NUM;
64 | var numa_show_num = DEFAULT_NUMA_SHOW_NUM;
65 | var LLC_show_num = DEFAULT_LLC_SHOW_NUM;
66 |
67 | // second
68 | var pre_timestamp = Date.parse(new Date()) / 1000;
69 |
70 | var template_type = TempleteType.Generic;
71 | var order_type = OrderType.CPU;
72 | var only_proc = true;
73 |
74 | var change_template = false
75 | var change_data = false
76 | var filter_str = ""
77 | var init = true
78 |
79 | var delta = 0
80 | const queue = []
81 | let loading = false
82 | let p
83 |
84 | var cache_template_header = ""
85 | var cache_template_process = ""
86 | var cache_json_data = ""
87 |
88 | window.onkeydown = function (event) {
89 | var keyCode = event.key;
90 | now = Date.parse(new Date()) / 1000;
91 |
92 | switch (keyCode) {
93 | case 'b':
94 | var now_date_str = new Date(pre_timestamp * 1000).format("YYYYMMDDhhmm");
95 |
96 | var chosed_date_str = prompt("Enter new time (format [YYYYMMDD]hhmm): ", now_date_str);
97 | if (chosed_date_str != null) {
98 | var tmp_timestamp = 0;
99 | tmp_timestamp = ParseDateToTimesample(chosed_date_str)
100 | if (tmp_timestamp != 0) {
101 | delta = tmp_timestamp - pre_timestamp
102 | } else {
103 | alert("Wrong time!")
104 | }
105 | }
106 | change_data = true
107 | break;
108 | case 'T':
109 | delta -= 10
110 | if (loading) {
111 | return
112 | }
113 | change_data = true
114 | break;
115 | case 't':
116 | delta += 10
117 | if (loading) {
118 | return
119 | }
120 | change_data = true
121 | break;
122 | case 'l':
123 | atopList();
124 | break;
125 | case 'g':
126 | order_type = OrderType.CPU;
127 | change_template = true;
128 | template_type = TempleteType.Generic;
129 | break;
130 | case 'm':
131 | order_type = OrderType.MEM;
132 | change_template = true;
133 | template_type = TempleteType.Memory;
134 | break;
135 | case 'd':
136 | order_type = OrderType.DISK;
137 | change_template = true;
138 | template_type = TempleteType.Disk;
139 | break;
140 | case 'c':
141 | order_type = OrderType.DISK;
142 | change_template = true;
143 | template_type = TempleteType.CommandLine;
144 | break;
145 | case 'y':
146 | only_proc = !only_proc;
147 | change_template = true;
148 | break;
149 | case '/':
150 | filter_str = prompt("Command line string as regular expression (enter=no regex): ", "");
151 | break;
152 | default:
153 | return
154 | }
155 |
156 | main();
157 | }
158 |
159 | window.onload = function () {
160 | change_data = true
161 | change_template = true
162 | main();
163 | }
164 |
165 | function atopList() {
166 | var new_process_show_num = prompt("Maxinum lines for process statistics (now " + proc_show_num + "): ", DEFAULT_PROC_SHOW_NUM);
167 | if (new_process_show_num != null) {
168 | proc_show_num = new_process_show_num;
169 | }
170 |
171 | var new_cpu_show_num = prompt("Maxinum lines for per-cpu statistics (now " + cpu_show_num + "): ", DEFAULT_CPU_SHOW_NUM);
172 | if (new_cpu_show_num != null) {
173 | cpu_show_num = new_cpu_show_num;
174 | }
175 |
176 | var new_gpu_show_num = prompt("Maxinum lines for per-gpu statistics (now " + gpu_show_num + "): ", DEFAULT_GPU_SHOW_NUM);
177 | if (new_gpu_show_num != null) {
178 | gpu_show_num = new_gpu_show_num;
179 | }
180 |
181 | var new_lvm_show_num = prompt("Maxinum lines for LVM statistics (now " + lvm_show_num + "): ", DEFAULT_LVM_SHOW_NUM);
182 | if (new_lvm_show_num != null) {
183 | lvm_show_num = new_lvm_show_num;
184 | }
185 |
186 | var new_disk_show_num = prompt("Maxinum lines for disk statistics (now " + disk_show_num + "): ", DEFAULT_DISK_SHOW_NUM);
187 | if (new_disk_show_num != null) {
188 | disk_show_num = new_disk_show_num;
189 | }
190 |
191 | var new_interface_show_num = prompt("Maxinum lines for interface statistics (now " + interface_show_num + "): ", DEFAULT_INTERFACE_SHOW_NUM);
192 | if (new_interface_show_num != null) {
193 | interface_show_num = new_interface_show_num;
194 | }
195 |
196 | var new_infiniband_show_num = prompt("Maxinum lines for infiniband port statistics (now " + infiniband_show_num + "): ", DEFAULT_INFINIBAND_SHOW_NUM);
197 | if (new_infiniband_show_num != null) {
198 | infiniband_show_num = new_infiniband_show_num;
199 | }
200 |
201 | var new_nfs_show_num = prompt("Maxinum lines for NFS mount statistics (now " + nfs_show_num + "): ", DEFAULT_NFS_SHOW_NUM);
202 | if (new_nfs_show_num != null) {
203 | nfs_show_num = new_nfs_show_num;
204 | }
205 |
206 | var new_container_show_num = prompt("Maxinum lines for container statistics (now " + container_show_num + "): ", DEFAULT_CONTAINER_SHOW_NUM);
207 | if (new_container_show_num != null) {
208 | container_show_num = new_container_show_num;
209 | }
210 |
211 | var new_numa_show_num = prompt("Maxinum lines for numa statistics (now " + numa_show_num + "): ", DEFAULT_NUMA_SHOW_NUM);
212 | if (new_numa_show_num != null) {
213 | numa_show_num = new_numa_show_num;
214 | }
215 |
216 | var new_LLC_show_num = prompt("Maxinum lines for LLC statistics (now " + LLC_show_num + "): ", DEFAULT_LLC_SHOW_NUM);
217 | if (new_LLC_show_num != null) {
218 | LLC_show_num = new_LLC_show_num;
219 | }
220 | }
221 |
222 | function main() {
223 | var req_timestamp = pre_timestamp + delta
224 | var req_template_type = template_type
225 | var req_change_data = change_data
226 | var req_change_template = change_template
227 | if (delta == 0 && init == true) {
228 | getHtmlTemplateProcessFromServer(req_timestamp, req_template_type, req_change_data, req_change_template).then(getHtmlTemplateHeaderFromServer).then(getJsonRawDataFromServer).then(res => {
229 | LoadHtmlFromJson(cache_json_data, constructTemplate(cache_template_header, cache_template_process))
230 | while (p = queue.shift()) {
231 | p.resolve()
232 | }
233 | init = false
234 | loading = false
235 | change_data = false
236 | change_template = false
237 | })
238 | return
239 | } else {
240 | delta = 0
241 | getHtmlTemplateProcessFromServer(req_timestamp, req_template_type, req_change_data, req_change_template).then(getJsonRawDataFromServer).then(res => {
242 | LoadHtmlFromJson(cache_json_data, constructTemplate(cache_template_header, cache_template_process))
243 | while (p = queue.shift()) {
244 | p.resolve()
245 | }
246 | loading = false
247 | change_data = false
248 | change_template = false
249 | })
250 | }
251 | }
252 |
253 | function constructTemplate(template_header, template_process) {
254 | return '' + template_header + template_process + ' '
255 | }
256 |
257 | function getHtmlTemplateProcessFromServer(req_timestamp, req_template_type, req_change_data, req_change_template) {
258 | return new Promise((resolve, reject) => {
259 | if (loading) {
260 | queue.push({ resolve, reject })
261 | }
262 | if (!loading) {
263 | loading = true
264 |
265 | if (req_change_template) {
266 | host = document.location.host;
267 | var url = "http://" + host + "/template?type=" + req_template_type;
268 | var request = new XMLHttpRequest();
269 |
270 | request.open("GET", url, true);
271 | request.send();
272 | request.onload = function () {
273 | if (request.status === 200) {
274 | raw_template_process = request.responseText;
275 | cache_template_process = raw_template_process
276 | resolve({
277 | req_timestamp,
278 | req_change_data
279 | })
280 | }
281 | }
282 | } else {
283 | resolve({ req_timestamp, req_change_data })
284 | }
285 | }
286 | })
287 | }
288 |
289 | function getHtmlTemplateHeaderFromServer(paras) {
290 | return new Promise((resolve, reject) => {
291 | if (init) {
292 | host = document.location.host;
293 | var url = "http://" + host + "/template_header"
294 | var request = new XMLHttpRequest();
295 |
296 | var req_timestamp = paras.req_timestamp
297 | var req_change_data = paras.req_change_data
298 |
299 | request.open("GET", url, true);
300 | request.send();
301 | request.onload = function () {
302 | if (request.status === 200) {
303 | raw_template_header = request.responseText;
304 | cache_template_header = raw_template_header
305 | resolve({
306 | req_timestamp,
307 | req_change_data
308 | })
309 | }
310 | }
311 | } else {
312 | resolve({ req_timestamp, req_change_data })
313 | }
314 | })
315 | }
316 |
317 | function getJsonRawDataFromServer(paras) {
318 | return new Promise((resolve, reject) => {
319 | if (paras.req_change_data) {
320 | host = document.location.host;
321 | var url = "http://" + host + "/showsamp?timestamp=" + paras.req_timestamp + "&lables=ALL";
322 | var request = new XMLHttpRequest();
323 |
324 | request.open("GET", url, true);
325 | request.send();
326 | request.onload = function () {
327 | if (request.status === 200) {
328 | raw_json = request.responseText;
329 | cache_json_data = raw_json
330 | resolve()
331 | }
332 | }
333 | } else {
334 | resolve()
335 | }
336 | })
337 | }
338 |
339 | function LoadHtmlFromJson(raw_json, template) {
340 | var parser = new DOMParser();
341 | doc = parser.parseFromString(template, "text/html");
342 | let tpl = doc.getElementById('tpl').innerHTML;
343 | let node = HtmlToNode(tpl);
344 | document.getElementById('target').innerHTML = ParseJsonToHtml(node, raw_json);
345 | }
346 |
347 | function ParseJsonToHtml(node, raw_json) {
348 | var nest_json = "[" + raw_json + "]";
349 | var json = JSON.parse(nest_json);
350 | preprocessJson(json);
351 |
352 | // sec to us
353 | per_cpu_tot = json[0]["EXTRA"]["percputot"];
354 | hertz = json[0]["CPU"]["hertz"];
355 | return repeatAtopHtmlNode(node, json, per_cpu_tot, hertz);
356 | }
357 |
358 | function preprocessJson(json) {
359 | parseAtopHeader(json);
360 | parseAtopProcess(json, filter_str);
361 | }
362 |
363 | function repeatAtopHtmlNode(node, arr, percputot) {
364 | let out = [];
365 |
366 | for (let i = 0; i < arr.length; i++) {
367 | let tmp = node.outerHTML;
368 | tmp = tmp.replace(/\s/g, ' ');
369 |
370 | let map = arr[i];
371 | // parse inner array
372 | for (let j in map) {
373 | if (map[j] instanceof Array) {
374 | let subNode = node.querySelector('.' + j);
375 | if (subNode) {
376 | let subHTML = repeatAtopHtmlNode(subNode, map[j], percputot, hertz);
377 | let subTpl = subNode.outerHTML.replace(/\s/g, ' ');
378 | tmp = tmp.replace(subTpl, subHTML);
379 | }
380 | }
381 | }
382 |
383 | // parse Object
384 | for (let j in map) {
385 | if (map[j] instanceof Object && !(map[j] instanceof Array)) {
386 | let subNode = node.querySelector('.' + j);
387 | if (subNode) {
388 | let subHTML = repeatAtopHtmlNode(subNode, [map[j]], percputot, hertz);
389 | let subTpl = subNode.outerHTML.replace(/\s/g, ' ');
390 | tmp = tmp.replace(subTpl, subHTML);
391 | }
392 | }
393 | }
394 |
395 | for (let j in map) {
396 | if (typeof map[j] === 'string' || typeof map[j] === 'number') {
397 | if (["stime", "utime", "ntime", "itime", "wtime", "Itime", "Stime", "steal", "guest"].indexOf(j) !== -1) {
398 | map[j] = ParseCPUPercentValue(map[j], percputot);
399 | } else if (["cpusome", "memsome", "memfull", "iosome", "iofull"].indexOf(j) !== -1) {
400 | map[j] = ParsePSIPercentValue(map[j]);
401 | } else if (["freq"].indexOf(j) !== -1) {
402 | map[j] = ParseCPUCurfValue(map[j]);
403 | } else if (["stime_unit_time", "utime_unit_time", "rundelay", "blkdelay"].indexOf(j) !== -1) {
404 | map[j] = ParseTimeValue(map[j], hertz, j);
405 | } else if (["buffermem", "cachedrt", "cachemem", "commitlim", "committed", "freemem", "freeswap",
406 | "filepage", "physmem", "rgrow", "rsz", "shmem", "shmrss", "shmswp", "slabmem", "swcac", "totmem", "totswap", "slabreclaim", "pagetables",
407 | "vexec", "vdata", "vgrow", "vlibs", "vlock", "vmem", "vstack", "wsz", "rmem", "pmem", "vswap", "tcpsk", "udpsk", "dirtymem", "active",
408 | "mbm_total", "mbm_local", "inactive"].indexOf(j) !== -1) {
409 | map[j] = ParseMemValue(map[j], j);
410 | } else if (["rbyte", "sbyte", "speed"].indexOf(j) !== -1) {
411 | map[j] = ParseBandwidth(map[j], j);
412 | } else if (["minflt", "majflt"].indexOf(j) !== -1) {
413 | map[j] = Value2ValueStr(map[j], j);
414 | } else if (["cpubusy", "membusy", "diskbusy", "lvmbusy", "frag", "occupancy"].indexOf(j) !== -1) {
415 | map[j] = Value2ValuePercent(map[j], j);
416 | }
417 | else {
418 | map[j] = map[j];
419 | }
420 |
421 | if (typeof map[j] === 'string') {
422 | map[j] = map[j];
423 | }
424 |
425 | let re = new RegExp('{' + j + '}', 'g');
426 | tmp = tmp.replace(re, map[j]);
427 | }
428 | }
429 |
430 | out.push(tmp);
431 | }
432 | return out.join('');
433 | }
--------------------------------------------------------------------------------
/http/js/atop_parse.js:
--------------------------------------------------------------------------------
1 | function parseAtopHeader(json) {
2 | // Modified the global
3 | pre_timestamp = json[0]["timestamp"];
4 |
5 | // Parse date
6 | var now = new Date(json[0]["timestamp"] * 1000);
7 | y = now.getFullYear();
8 | m = now.getMonth() + 1;
9 | d = now.getDate();
10 | format_date = y + "-" + (m < 10 ? "0" + m : m) + "-" + (d < 10 ? "0" + d : d) + " " + now.toTimeString().slice(0, 8);
11 | json[0]["date"] = format_date;
12 |
13 | // Parse elapsed
14 | elapsed = json[0]["elapsed"];
15 |
16 | //CPU line
17 | CPUEntry = json[0]["CPU"];
18 | CPU_Tot = CPUEntry["stime"] + CPUEntry["utime"] + CPUEntry["ntime"] + CPUEntry["itime"] + CPUEntry["wtime"] + CPUEntry["Itime"] + CPUEntry["Stime"] + CPUEntry["steal"];
19 |
20 | if (CPU_Tot === 0) {
21 | CPU_Tot = 1;
22 | }
23 |
24 | per_cpu_tot = CPU_Tot / CPUEntry["nrcpu"];
25 | mstot = CPU_Tot * 1000 / CPUEntry["hertz"] / CPUEntry["nrcpu"];
26 |
27 | json[0]["CPU"]["cpubusy"] = ((CPU_Tot - CPUEntry["itime"] - CPUEntry["wtime"]) / per_cpu_tot * 100).toFixed(1);
28 |
29 | //cpu line
30 | cpulist = json[0]["cpu"];
31 | cpulist.sort(compareOrderByCpu);
32 |
33 | var temp_cpu_show_num = cpu_show_num
34 | if (temp_cpu_show_num > cpulist.length) {
35 | temp_cpu_show_num = cpulist.length;
36 | }
37 | json[0]["cpu"] = cpulist.slice(0, temp_cpu_show_num);
38 |
39 | //GPU line
40 | //TODO
41 |
42 | //CPL line
43 | json[0]["CPL"]["nrcpu"] = CPUEntry["nrcpu"];
44 |
45 | //MEM line
46 |
47 | //SWP line
48 |
49 | //NUM NUC line
50 | json[0]["MEM"]["numanode"] = json[0]["NUM"].length;
51 |
52 | numa_memlist = json[0]["NUM"];
53 | numa_memlist.forEach(function (numanode, index) {
54 | numanode["numanodeId"] = index;
55 | numanode["frag"] = (numanode["frag"] * 1).toFixed(2)
56 | })
57 |
58 | numa_cpulist = json[0]["NUC"];
59 | var temp_numa_show_num = numa_show_num
60 | if (temp_numa_show_num > numa_cpulist.length) {
61 | temp_numa_show_num = numa_cpulist.length;
62 | }
63 |
64 | json[0]["NUM"] = numa_memlist.slice(0, temp_numa_show_num);
65 | json[0]["NUC"] = numa_cpulist.slice(0, temp_numa_show_num);
66 |
67 | //PAG line
68 |
69 | //PSI line
70 | psiEntry = json[0]["PSI"];
71 |
72 | if (function(){
73 | for(var i in psiEntry) {
74 | return false
75 | }
76 | return true
77 | } === false ) {
78 | json[0]["PSI"]["cpusome"] = ((psiEntry["cstot"] / elapsed * 100) > 100) ? 100 : psiEntry["cstot"] / (elapsed * 10000);
79 | json[0]["PSI"]["iosome"] = ((psiEntry["iostot"] / elapsed * 100) > 100) ? 100 : psiEntry["iostot"] / (elapsed * 10000);
80 | json[0]["PSI"]["iofull"] = ((psiEntry["ioftot"] / elapsed * 100) > 100) ? 100 : psiEntry["ioftot"] / (elapsed * 10000);
81 | json[0]["PSI"]["memsome"] = ((psiEntry["mstot"] / elapsed * 100) > 100) ? 100 : psiEntry["mstot"] / (elapsed * 10000);
82 | json[0]["PSI"]["memfull"] = ((psiEntry["mftot"] / elapsed * 100) > 100) ? 100 : psiEntry["mftot"] / (elapsed * 10000);
83 | json[0]["PSI"]["cs"] = psiEntry["cs10"].toFixed(0) + "/" + psiEntry["cs60"].toFixed(0) + "/" + psiEntry["cs300"].toFixed(0);
84 | json[0]["PSI"]["ms"] = psiEntry["ms10"].toFixed(0) + "/" + psiEntry["ms60"].toFixed(0) + "/" + psiEntry["ms300"].toFixed(0);
85 | json[0]["PSI"]["mf"] = psiEntry["mf10"].toFixed(0) + "/" + psiEntry["mf60"].toFixed(0) + "/" + psiEntry["mf300"].toFixed(0);
86 | json[0]["PSI"]["is"] = psiEntry["ios10"].toFixed(0) + "/" + psiEntry["ios60"].toFixed(0) + "/" + psiEntry["ios300"].toFixed(0);
87 | json[0]["PSI"]["if"] = psiEntry["iof10"].toFixed(0) + "/" + psiEntry["iof60"].toFixed(0) + "/" + psiEntry["iof300"].toFixed(0);
88 | } else {
89 | json[0]["PSI"] = []
90 | }
91 |
92 | //LVM line
93 | lvmlist = json[0]["LVM"];
94 | lvmlist.forEach(function (lvm, index) {
95 | lvm["lvmname"] = lvm["lvmname"].substring(lvm["lvmname"].length - 10)
96 | iotot = lvm["nread"] + lvm["nwrite"];
97 | lvm["avio"] = (iotot ? lvm["io_ms"] / iotot : 0.0).toFixed(2);
98 | lvm["lvmbusy"] = (mstot ? lvm["io_ms"] / mstot : 0).toFixed(2);
99 | lvm["MBr/s"] = (lvm["nrsect"] / 2 / 1024 / elapsed).toFixed(1);
100 | lvm["MBw/s"] = (lvm["nwsect"] / 2 / 1024 / elapsed).toFixed(1);
101 | })
102 |
103 | //DSK line
104 | dsklist = json[0]["DSK"];
105 | dsklist.forEach(function (dsk, index) {
106 | iotot = dsk["nread"] + dsk["nwrite"];
107 | dsk["avio"] = (iotot ? dsk["io_ms"] / iotot : 0.0).toFixed(2);
108 | dsk["diskbusy"] = (mstot ? dsk["io_ms"] / mstot : 0).toFixed(2);
109 | dsk["MBr/s"] = (dsk["nrsect"] / 2 / 1024 / elapsed).toFixed(1);
110 | dsk["MBw/s"] = (dsk["nwsect"] / 2 / 1024 / elapsed).toFixed(1);
111 | })
112 | dsklist.sort(compareOrderByDiskIoms);
113 | var temp_disk_show_num = disk_show_num
114 | if (temp_disk_show_num > dsklist.length) {
115 | temp_disk_show_num = dsklist.length;
116 | }
117 | json[0]["DSK"] = dsklist.slice(0, temp_disk_show_num);
118 |
119 | //NET line
120 | netlist = json[0]["NET"];
121 | if (netlist !== undefined){
122 | netlist.sort(compareOrderByNet);
123 | var temp_interface_show_num = interface_show_num
124 | if (temp_interface_show_num > netlist.length) {
125 | temp_interface_show_num = netlist.length
126 | }
127 | json[0]["NET"] = netlist.slice(0, temp_interface_show_num);
128 | }
129 |
130 | //IFB line
131 | // TODO
132 |
133 | // LLC line
134 | llclist = json[0]["LLC"];
135 | if (llclist !== undefined){
136 | llclist.sort(compareOrderLLC);
137 | json[0]["LLC"] = llclist.slice(0, LLC_show_num)
138 | }
139 |
140 | // EXTRA
141 | extra = new Object();
142 | extra["mstot"] = mstot;
143 | extra["percputot"] = per_cpu_tot;
144 |
145 | extra["availmem"] = json[0]["MEM"]["physmem"] / 1024;
146 | json[0]["EXTRA"] = extra;
147 | }
148 |
149 | function parseAtopProcess(json, filter_str) {
150 | percputot = json[0]["EXTRA"]["percputot"];
151 | availmem = json[0]["EXTRA"]["availmem"];
152 |
153 | prc = json[0]["PRC"];
154 | prm = json[0]["PRM"];
155 | prd = json[0]["PRD"];
156 | prg = json[0]["PRG"];
157 |
158 | prcmap = ArrayToMap(prc);
159 | prmmap = ArrayToMap(prm);
160 | prdmap = ArrayToMap(prd);
161 | prgmap = ArrayToMap(prg);
162 |
163 | hprc = new Object();
164 | hprc["proc_count"] = 0;
165 | hprc["sleep_count"] = 0;
166 | hprc["zombie_count"] = 0;
167 | hprc["exit_count"] = 0;
168 | hprc["running_count"] = 0;
169 | hprc["sleep_interrupt_count"] = 0;
170 | hprc["sleep_uninterrupt_count"] = 0;
171 |
172 | hprc["stime_unit_time"] = 0;
173 | hprc["utime_unit_time"] = 0;
174 |
175 | prgmap.forEach(function (iprg, index) {
176 | if (iprg["state"] == "E") {
177 | hprc["exit_count"]++;
178 | } else {
179 | if (iprg["state"] == "Z") {
180 | hprc["zombie_count"]++;
181 | }
182 | hprc["sleep_interrupt_count"] += iprg["nthrslpi"];
183 | hprc["sleep_uninterrupt_count"] += iprg["nthrslpu"];
184 | hprc["running_count"] += iprg["nthrrun"];
185 | hprc["proc_count"]++;
186 | }
187 | })
188 |
189 | // get availdsk
190 | var availdisk = 0;
191 | prdmap.forEach(function (iprd, index) {
192 | var nett_wsz = 0;
193 | if (iprd["wsz"] > iprd["cwsz"]) {
194 | nett_wsz = iprd["wsz"] - iprd["cwsz"];
195 | } else {
196 | nett_wsz = 0;
197 | }
198 |
199 | availdisk += iprd["rsz"];
200 | availdisk += nett_wsz;
201 | })
202 |
203 | // filter str
204 | aggregate_pr_map = new Map();
205 | prgmap.forEach(function (iprg, index) {
206 | iprm = prmmap.get(index);
207 | iprd = prdmap.get(index);
208 | iprc = prcmap.get(index);
209 |
210 | // proc_count not contains exit proc
211 | if (iprg["isproc"] !== 1) {
212 | iprg["tid"] = iprg["tgid"];
213 | if (only_proc) {
214 | return;
215 | }
216 | } else {
217 | iprg["tid"] = '-';
218 | }
219 |
220 | if (typeof filter_str == 'string') {
221 | if (filter_str != "") {
222 | if (iprg["name"].indexOf(filter_str) == -1) {
223 | return
224 | }
225 | }
226 | }
227 |
228 | hprc["stime_unit_time"] += iprc["stime"];
229 | hprc["utime_unit_time"] += iprc["utime"];
230 |
231 | //cpu
232 | cputot = iprc["stime"] + iprc["utime"];
233 | iprc["cpubusy"] = ((cputot / percputot) * 100).toFixed(1);
234 |
235 | iprc["stime_unit_time"] = iprc["stime"];
236 | iprc["utime_unit_time"] = iprc["utime"];
237 | iprc["curcpu"] = iprc["curcpu"] ? iprc["curcpu"] : "-";
238 |
239 | //general
240 | pro_name = iprg["name"].replace(/[()]/ig, "");
241 | iprg["name"] = pro_name.length > 15 ? pro_name.substr(0, 15) : pro_name;
242 | cmdline = iprg["cmdline"].replace(/[()]/ig, "");
243 | if (cmdline.length == 0) {
244 | iprg["cmdline"] = iprg["name"]
245 | } else {
246 | iprg["cmdline"] = cmdline
247 | }
248 | iprg["exitcode"] = iprg["exitcode"] ? iprg["exitcode"] : "-";
249 |
250 | //mem
251 | membusy = (iprm["rmem"] * 100 / availmem).toFixed(1)
252 | if (membusy > 100) {
253 | iprm["membusy"] = 100;
254 | } else {
255 | iprm["membusy"] = membusy;
256 | }
257 |
258 | iprm["vexec"] = iprm["vexec"] * 1024;
259 | iprm["vlibs"] = iprm["vlibs"] * 1024;
260 | iprm["vdata"] = iprm["vdata"] * 1024;
261 | iprm["vstack"] = iprm["vstack"] * 1024;
262 | iprm["vlock"] = iprm["vlock"] * 1024;
263 | iprm["vmem"] = iprm["vmem"] * 1024;
264 | iprm["rmem"] = iprm["rmem"] * 1024;
265 | iprm["pmem"] = iprm["pmem"] * 1024;
266 | iprm["vgrow"] = iprm["vgrow"] * 1024;
267 | iprm["rgrow"] = iprm["rgrow"] * 1024;
268 | iprm["vswap"] = iprm["vswap"] * 1024;
269 |
270 | //disk
271 | var nett_wsz = 0;
272 | if (iprd["wsz"] > iprd["cwsz"]) {
273 | nett_wsz = iprd["wsz"] - iprd["cwsz"];
274 | } else {
275 | nett_wsz = 0;
276 | }
277 |
278 | diskbusy = ((iprd["rsz"] + nett_wsz) * 100 / availdisk).toFixed(1)
279 | if (diskbusy > 100) {
280 | iprd["diskbusy"] = 100;
281 | } else {
282 | iprd["diskbusy"] = diskbusy;
283 | }
284 |
285 | iprd["rsz"] = iprd["rsz"] * 512;
286 | iprd["wsz"] = iprd["wsz"] * 512;
287 | iprd["cwsz"] = iprd["cwsz"] * 512;
288 |
289 | aggregate_pr = Object.assign(iprc, iprm, iprd, iprg);
290 |
291 | aggregate_pr_map.set(index, aggregate_pr);
292 | })
293 |
294 | // map to arr for sort
295 | aggregate_pr_arr = [];
296 | aggregate_pr_map.forEach(function (value, key) {
297 | aggregate_pr_arr.push(value);
298 | })
299 |
300 | switch (order_type) {
301 | case OrderType.CPU:
302 | aggregate_pr_arr.sort(compareOrderByCpu);
303 | break;
304 | case OrderType.DISK:
305 | aggregate_pr_arr.sort(compareOrderByDisk);
306 | break;
307 | case OrderType.MEM:
308 | aggregate_pr_arr.sort(compareOrderByMem);
309 | break;
310 | default:
311 | aggregate_pr_arr.sort(compareOrderByCpu);
312 | }
313 |
314 | var temp_proc_show_num = proc_show_num
315 | if (temp_proc_show_num > aggregate_pr_arr.length) {
316 | temp_proc_show_num = aggregate_pr_arr.length;
317 | }
318 |
319 | json[0]["PRSUMMARY"] = aggregate_pr_arr.slice(0, temp_proc_show_num);
320 | json[0]["HPRC"] = hprc;
321 | delete json[0]["PRC"];
322 | delete json[0]["PRM"];
323 | delete json[0]["PRD"];
324 | delete json[0]["PRG"];
325 | }
326 |
327 | function ParseCPUPercentValue(value, percputot) {
328 | return Math.round(value * 100 / percputot).toFixed(0) + "%";
329 | }
330 |
331 | function ParseCPUCurfValue(value) {
332 | if (value < 1000) {
333 | return value + "MHz";
334 | } else {
335 | fval = value / 1000;
336 | prefix = 'G';
337 | if (fval >= 1000) {
338 | prefix = 'T';
339 | fval = fval / 1000;
340 | }
341 |
342 | if (fval < 10) {
343 | return fval.toFixed(2) + prefix + "Hz";
344 | } else {
345 | if (fval < 100) {
346 | return fval.toFixed(1) + prefix + "Hz";
347 | } else {
348 | return fval.toFixed(0) + prefix + "Hz";
349 | }
350 | }
351 | }
352 | }
353 |
354 | function ParsePSIPercentValue(value) {
355 | return Math.round(value).toFixed(0) + "%";
356 | }
357 |
358 | function ParseTimeValue(value, hertz, indicatorName) {
359 | // here is ms
360 | value = parseInt(value * 1000 / hertz);
361 |
362 | if (value < 100000) {
363 | return parseInt(value / 1000) + "." + parseInt(value % 1000 / 10) + "s";
364 | } else {
365 | //ms to sec
366 | value = parseInt((value + 500) / 1000);
367 | if (value < 6000) {
368 | //sec to min
369 | return parseInt(value / 60) + "m" + (value % 60) + "s";
370 | } else {
371 | //min to hour
372 | value = parseInt((value + 30) / 60);
373 | if (value < 6000) {
374 | return parseInt(value / 60) + "h" + (value % 60) + "m";
375 | } else {
376 | // hour to day
377 | value = parseInt((value + 30) / 60);
378 | return parseInt(value / 24) + "d" + (value % 24) + "h";
379 | }
380 | }
381 | }
382 | }
383 |
384 | function ParseMemValue(ByteValue, indicatorName) {
385 | var unit = 1024; // byte
386 | ByteValue = ByteValue - 0;
387 |
388 | var prefix = "";
389 | if (ByteValue < 0) {
390 | ByteValue = -ByteValue * 10;
391 | var prefix = "-";
392 | }
393 |
394 | if (ByteValue < unit) {
395 | result = ByteValue + 'B';
396 | } else if (ByteValue < Math.pow(unit, 2) * 0.8) {
397 | result = (ByteValue / unit).toFixed(1) + "KB";
398 | } else if (ByteValue < Math.pow(unit, 3) * 0.8) {
399 | result = (ByteValue / Math.pow(unit, 2)).toFixed(1) + "MB";
400 | } else if (ByteValue < Math.pow(unit, 4) * 0.8) {
401 | result = (ByteValue / Math.pow(unit, 3)).toFixed(1) + "G";
402 | } else {
403 | result = (ByteValue / Math.pow(unit, 4)).toFixed(1) + "T";
404 | }
405 |
406 | return prefix + result;
407 | }
408 |
409 | function ParseBandwidth(BandwidthValue, indicatorName) {
410 | var unit = 1000;
411 |
412 | if (BandwidthValue < unit) {
413 | return BandwidthValue + "Kbps";
414 | } else if (BandwidthValue < Math.pow(unit, 2)) {
415 | return (BandwidthValue / unit).toFixed(2) + "Mbps";
416 | } else if (BandwidthValue < Math.pow(unit, 3)) {
417 | return (BandwidthValue / Math.pow(unit, 2)).toFixed(2) + "Gbps";
418 | } else {
419 | return (BandwidthValue / Math.pow(unit, 3)).toFixed(2) + "Tbps";
420 | }
421 | }
422 |
423 | // for number toooo long
424 | function Value2ValueStr(value, indicatorName) {
425 | // TODO
426 | return value;
427 | }
428 |
429 | function Value2ValuePercent(value, indicatorName) {
430 | return value + "%";
431 | }
432 |
433 | function ArrayToMap(array) {
434 | map = new Map();
435 | for (i of array) {
436 | map.set(i["pid"], i);
437 | }
438 | return map;
439 | }
440 |
441 | function HtmlToNode(html) {
442 | let div = document.createElement('div');
443 | let pos = html.indexOf('<');
444 | div.innerHTML = html.substring(pos);
445 | return div.firstChild;
446 | }
447 |
448 | function ParseDateToTimesample(itim) {
449 | ilen = itim.length
450 | if (ilen != 4 && ilen != 5 && ilen != 12 && ilen != 13) {
451 | return 0;
452 | }
453 |
454 | var year, month, day, hour, minute
455 |
456 | if (ilen == 12 || ilen == 13) {
457 | year = itim.slice(0, 4);
458 | month = itim.slice(4, 6);
459 | day = itim.slice(6, 8);
460 | hour = itim.slice(8, 10);
461 | minute = itim.slice(-2);
462 | } else if (ilen == 4 || ilen == 5) {
463 | now_date = new Date();
464 | year = now_date.getFullYear();
465 | month = now_date.getMonth() + 1;
466 | day = now_date.getDate();
467 |
468 | hour = itim.slice(0, 2);
469 | minute = itim.slice(-2);
470 | }
471 |
472 | if (year < 100 || month < 0 || month > 12 || day < 1 || day > 31 || hour < 0 || hour > 23 || minute < 0 || minute > 59) {
473 | return 0;
474 | }
475 |
476 | parsed_timestamp = Date.parse(new Date(year, month - 1, day, hour, minute)) / 1000;
477 | return parsed_timestamp;
478 | }
479 |
480 | Date.prototype.format = function (fmt) {
481 | let ret;
482 | const opt = {
483 | "Y+": this.getFullYear().toString(),
484 | "M+": (this.getMonth() + 1).toString(),
485 | "D+": this.getDate().toString(),
486 | "h+": this.getHours().toString(),
487 | "m+": this.getMinutes().toString(),
488 | };
489 | for (let k in opt) {
490 | ret = new RegExp("(" + k + ")").exec(fmt);
491 | if (ret) {
492 | fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
493 | };
494 | };
495 | return fmt;
496 | }
497 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | , 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------
/httpd.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 zhenwei pi
3 | *
4 | * Authors:
5 | * zhenwei pi
6 | *
7 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
8 | * See the COPYING file in the top-level directory.
9 | */
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 |
32 | #include "httpd.h"
33 | #include "output.h"
34 |
35 | #include "version.h"
36 |
37 | #define DEFAULT_PORT 2867
38 | #define DEFAULT_TLS_PORT 2868
39 | #define DEFAULT_LOG_PATH "/var/log/atop"
40 |
41 | #define DEFAULT_CERT_FILE "/etc/pki/atophttpd/server.crt"
42 | #define DEFAULT_KEY_FILE "/etc/pki/atophttpd/server.key"
43 | #define DEFAULT_CA_FILE "/etc/pki/CA/ca.crt"
44 |
45 | static void http_show_samp_done(struct output *op, connection *conn);
46 |
47 | struct output defop = {
48 | .output_type = OUTPUT_BUF,
49 | .done = http_show_samp_done
50 | };
51 |
52 | static struct atophttd_context config = {
53 | .port = DEFAULT_PORT,
54 | .daemonmode = 0,
55 | .log_path = DEFAULT_LOG_PATH,
56 | .addr = "127.0.0.1",
57 |
58 | .tls_ctx_config.tls_port = -1,
59 | .tls_ctx_config.tls_addr = "::*",
60 | .tls_ctx_config.ca_cert_file = DEFAULT_CA_FILE,
61 | .tls_ctx_config.cert_file = DEFAULT_CERT_FILE,
62 | .tls_ctx_config.key_file = DEFAULT_KEY_FILE,
63 | };
64 |
65 |
66 | unsigned int pagesize;
67 | unsigned short hertz;
68 | struct utsname utsname;
69 | int hidecmdline = 0;
70 |
71 | #define INBUF_SIZE 4096
72 | #define URL_LEN 1024
73 | /* HTTP codes */
74 | static char *http_200 = "HTTP/1.1 200 OK\r\n";
75 | static char *http_404 = "HTTP/1.1 404 Not Found\r\n";
76 |
77 | /* HTTP content types */
78 | static char *http_content_type_none = "";
79 | static char *http_content_type_deflate = "Content-Encoding: deflate\r\n";
80 |
81 | /* HTTP generic header */
82 | static char *http_generic = "Server: atop\r\n"
83 | "%s" /* for http_content_type_XXX */
84 | "Content-Type: %s; charset=utf-8\r\n"
85 | "Content-Length: %d\r\n\r\n";
86 |
87 | /* HTTP content types */
88 | static char *http_content_type_html = "text/html";
89 | static char *http_content_type_css = "text/css";
90 | static char *http_content_type_javascript = "application/javascript";
91 |
92 | static int http_prepare_response(connection *conn)
93 | {
94 | /* try to send response data in a timeout */
95 | int onoff = 1;
96 | int ret;
97 | ret = setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY, &onoff, sizeof(onoff));
98 | if (ret) {
99 | printf("failed to set socket to TCP_NODELAY\n");
100 | return ret;
101 | }
102 |
103 | ret = fcntl(conn->fd, F_SETFL, fcntl(conn->fd, F_GETFL) & ~O_NONBLOCK);
104 | if (ret) {
105 | printf("failed to set conn to blocking\n");
106 | return ret;
107 | }
108 |
109 | struct timeval tv = {.tv_sec = 5, .tv_usec = 0};
110 | ret = setsockopt(conn->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
111 | if (ret) {
112 | printf("failed to set socket to SNDTIMEO\n");
113 | return ret;
114 | }
115 |
116 | return 0;
117 | }
118 |
119 | static void http_response_200(connection *conn, char *buf, size_t len, char *encoding, char* content_type)
120 | {
121 | struct iovec iovs[3], *iov;
122 | int ret;
123 | char content[128] = {0};
124 | int content_length = 0;
125 |
126 | ret = http_prepare_response(conn);
127 | if (ret) {
128 | goto closeconn;
129 | }
130 |
131 | /* 1, http code */
132 | iov = &iovs[0];
133 | iov->iov_base = http_200;
134 | iov->iov_len = strlen(http_200);
135 |
136 | /* 2, http generic content */
137 | iov = &iovs[1];
138 |
139 | content_length = sprintf(content, http_generic, encoding, content_type, len);
140 | iov->iov_base = content;
141 | iov->iov_len = content_length;
142 |
143 | /* 3, sample data record */
144 | iov = &iovs[2];
145 | iov->iov_base = buf;
146 | iov->iov_len = len;
147 |
148 | conn_writev(conn, iovs, sizeof(iovs) / sizeof(iovs[0]));
149 |
150 | closeconn:
151 | conn_close(conn);
152 | }
153 |
154 | static void http_show_samp_done(struct output *op, connection *conn)
155 | {
156 | if (op->encoding == http_content_type_none) {
157 | http_response_200(conn, op->ob.buf, op->ob.offset, op->encoding, http_content_type_html);
158 | return;
159 | }
160 |
161 | /* compress data for encoding deflate */
162 | char *compbuf = malloc(op->ob.offset);
163 | unsigned long complen = op->ob.offset;
164 |
165 | if (compress((Bytef *)compbuf, &complen, (Bytef *)op->ob.buf, op->ob.offset) != Z_OK){
166 | http_prepare_response(conn);
167 | conn_write(conn, http_404, strlen(http_404));
168 | }
169 |
170 | http_response_200(conn, compbuf, complen, http_content_type_deflate, http_content_type_html);
171 | free(compbuf);
172 | }
173 |
174 | static int http_arg_long(char *req, char *needle, long *l)
175 | {
176 | char *s = strstr(req, needle);
177 | if (!s)
178 | return -1;
179 |
180 | s += strlen(needle);
181 | if (*s != '=')
182 | return -1;
183 |
184 | *l = atol(++s);
185 |
186 | return 0;
187 | }
188 |
189 | static int http_arg_str(char *req, char *needle, char *p, int len)
190 | {
191 | char *s = strstr(req, needle);
192 | if (!s)
193 | return -1;
194 |
195 | s += strlen(needle);
196 | if (*s++ != '=')
197 | return -1;
198 |
199 | char *e = strchr(s, '&');
200 | if (!e)
201 | e = s + strlen(s);
202 |
203 | if (e - s > len)
204 | return -1;
205 |
206 | memcpy(p, s, e - s);
207 | p[e - s] = '\0';
208 | while ((p = strstr(p, "%2C")) != NULL)
209 | {
210 | *(p++) = ',';
211 | memmove(p, p + 2, strlen(p));
212 | }
213 |
214 | return 0;
215 | }
216 |
217 | static void http_showsamp(char *req, connection *conn)
218 | {
219 | time_t timestamp = 0;
220 | char lables[1024];
221 | char encoding[16];
222 |
223 | if (http_arg_long(req, "timestamp", ×tamp) < 0) {
224 | char *err = "missing timestamp\r\n";
225 | http_response_200(conn, err, strlen(err), http_content_type_none, http_content_type_html);
226 | return;
227 | }
228 |
229 | if (http_arg_str(req, "lables", lables, sizeof(lables)) < 0) {
230 | char *err = "missing lables\r\n";
231 | http_response_200(conn, err, strlen(err), http_content_type_none, http_content_type_html);
232 | return;
233 | }
234 |
235 | defop.encoding = http_content_type_deflate;
236 | if (http_arg_str(req, "encoding", encoding, sizeof(encoding)) == 0) {
237 | if (!strcmp(encoding, "none")) {
238 | defop.encoding = http_content_type_none;
239 | } else if (!strcmp(encoding, "deflate")) {
240 | defop.encoding = http_content_type_deflate;
241 | } else {
242 | char *err = "encoding supports none/deflate only\r\n";
243 | http_response_200(conn, err, strlen(err), http_content_type_none, http_content_type_html);
244 | return;
245 | }
246 | }
247 |
248 | if (rawlog_get_record(timestamp, lables, conn) < 0) {
249 | char *err = "missing sample\r\n";
250 | http_response_200(conn, err, strlen(err), http_content_type_none, http_content_type_html);
251 | return;
252 | }
253 | }
254 |
255 | /* Import a binary file */
256 | #define IMPORT_BIN(sect, file, sym) asm ( \
257 | ".section " #sect "\n" \
258 | ".global " #sym "\n" \
259 | ".global " #sym "_end\n" \
260 | #sym ":\n" \
261 | ".incbin \"" file "\"\n" \
262 | #sym "_end:\n" \
263 | ".section \".text\"\n")
264 |
265 | /* Build help.html into atop binary */
266 | IMPORT_BIN(".rodata", "http/help.html", help);
267 | extern char help[], help_end[];
268 |
269 | static void http_help(connection *conn)
270 | {
271 | http_response_200(conn, help, help_end - help, http_content_type_none, http_content_type_html);
272 | }
273 |
274 | /* Build favicon.ico into atop binary */
275 | IMPORT_BIN(".rodata", "http/favicon.ico", favicon);
276 | extern char favicon[], favicon_end[];
277 |
278 | static void http_favicon(connection *conn)
279 | {
280 | http_response_200(conn, favicon, favicon_end - favicon, http_content_type_none, http_content_type_html);
281 | }
282 |
283 | /* Build index.html into atop binary */
284 | IMPORT_BIN(".rodata", "http/index.html", http_index_html);
285 | extern char http_index_html[], http_index_html_end[];
286 |
287 | static void http_index(connection *conn)
288 | {
289 | http_response_200(conn, http_index_html, http_index_html_end - http_index_html, http_content_type_none, http_content_type_html);
290 | }
291 |
292 | /* Build atop.js into atop binary */
293 | IMPORT_BIN(".rodata", "http/js/atop.js", atop_js);
294 | extern char atop_js[], atop_js_end[];
295 |
296 | static void http_get_atop_js(connection *conn)
297 | {
298 | http_response_200(conn, atop_js, atop_js_end - atop_js, http_content_type_none, http_content_type_javascript);
299 | }
300 |
301 | /* Build atop_parse.js into atop binary */
302 | IMPORT_BIN(".rodata", "http/js/atop_parse.js", atop_parse_js);
303 | extern char atop_parse_js[], atop_parse_js_end[];
304 |
305 | static void http_get_atop_parse_js(connection *conn)
306 | {
307 | http_response_200(conn, atop_parse_js, atop_parse_js_end - atop_parse_js, http_content_type_none, http_content_type_javascript);
308 | }
309 |
310 | /* Build atop_compare_fc.js into atop binary */
311 | IMPORT_BIN(".rodata", "http/js/atop_compare_fc.js", atop_compare_fc_js);
312 | extern char atop_compare_fc_js[], atop_compare_fc_js_end[];
313 |
314 | static void http_get_atop_compare_fc_js(connection *conn)
315 | {
316 | http_response_200(conn, atop_compare_fc_js, atop_compare_fc_js_end - atop_compare_fc_js, http_content_type_none, http_content_type_javascript);
317 | }
318 |
319 | /* Build atop.css into atop binary */
320 | IMPORT_BIN(".rodata", "http/css/atop.css", http_css);
321 | extern char http_css[], http_css_end[];
322 |
323 | static void http_get_css(connection *conn)
324 | {
325 | http_response_200(conn, http_css, http_css_end - http_css, http_content_type_none, http_content_type_css);
326 | }
327 |
328 | /* Build template.html into atop binary */
329 | IMPORT_BIN(".rodata", "http/template/html/header.html", header_html_template);
330 | extern char header_html_template[], header_html_template_end[];
331 |
332 | IMPORT_BIN(".rodata", "http/template/html/generic.html", generic_html_template);
333 | extern char generic_html_template[], generic_html_template_end[];
334 |
335 | IMPORT_BIN(".rodata", "http/template/html/memory.html", memory_html_template);
336 | extern char memory_html_template[], memory_html_template_end[];
337 |
338 | IMPORT_BIN(".rodata", "http/template/html/disk.html", disk_html_template);
339 | extern char disk_html_template[], disk_html_template_end[];
340 |
341 | IMPORT_BIN(".rodata", "http/template/html/command_line.html", command_line_html_template);
342 | extern char command_line_html_template[], command_line_html_template_end[];
343 |
344 | static void http_get_template_header(connection *conn)
345 | {
346 | http_response_200(conn, header_html_template, header_html_template_end - header_html_template, http_content_type_none, http_content_type_html);
347 | }
348 |
349 | static void http_get_template(char *req, connection *conn)
350 | {
351 | char template_type[256];
352 | if (http_arg_str(req, "type", template_type, sizeof(template_type)) < 0)
353 | return;
354 |
355 | if (!strcmp(template_type, "generic")) {
356 | http_response_200(conn, generic_html_template, generic_html_template_end - generic_html_template, http_content_type_none, http_content_type_html);
357 | } else if (!strcmp(template_type, "memory")) {
358 | http_response_200(conn, memory_html_template, memory_html_template_end - memory_html_template, http_content_type_none, http_content_type_html);
359 | } else if (!strcmp(template_type, "disk")) {
360 | http_response_200(conn, disk_html_template, disk_html_template_end - disk_html_template, http_content_type_none, http_content_type_html);
361 | } else if (!strcmp(template_type, "command_line")) {
362 | http_response_200(conn, command_line_html_template, command_line_html_template_end - command_line_html_template, http_content_type_none, http_content_type_html);
363 | } else {
364 | http_prepare_response(conn);
365 | conn_write(conn, http_404, strlen(http_404));
366 | }
367 | }
368 |
369 | static void http_ping(connection *conn)
370 | {
371 | char *pong = "pong\r\n";
372 |
373 | http_response_200(conn, pong, strlen(pong), http_content_type_none, http_content_type_html);
374 | }
375 |
376 | static void http_process_request(char *req, connection *conn)
377 | {
378 | char location[URL_LEN] = {0};
379 | char *c;
380 |
381 | if (strlen(req) > URL_LEN) {
382 | http_prepare_response(conn);
383 | conn_write(conn, http_404, strlen(http_404));
384 | return;
385 | }
386 |
387 | c = strchr(req, '?');
388 | if (c)
389 | memcpy(location, req, c - req);
390 | else
391 | memcpy(location, req, strlen(req));
392 |
393 | if (strlen(location) == 0) {
394 | http_index(conn);
395 | return;
396 | }
397 |
398 | if (!strcmp(location, "ping"))
399 | http_ping(conn);
400 | else if (!strcmp(location, "help"))
401 | http_help(conn);
402 | else if (!strcmp(location, "favicon.ico"))
403 | http_favicon(conn);
404 | else if (!strcmp(location, "showsamp"))
405 | http_showsamp(req, conn);
406 | else if (!strcmp(location, "index.html"))
407 | http_index(conn);
408 | else if (!strcmp(location, "js/atop.js"))
409 | http_get_atop_js(conn);
410 | else if (!strcmp(location, "js/atop_parse.js"))
411 | http_get_atop_parse_js(conn);
412 | else if (!strcmp(location, "js/atop_compare_fc.js"))
413 | http_get_atop_compare_fc_js(conn);
414 | else if (!strcmp(location, "css/atop.css"))
415 | http_get_css(conn);
416 | else if (!strcmp(location, "template_header"))
417 | http_get_template_header(conn);
418 | else if (!strcmp(location, "template"))
419 | http_get_template(req, conn);
420 | else {
421 | http_prepare_response(conn);
422 | conn_write(conn, http_404, strlen(http_404));
423 | }
424 | }
425 |
426 | static time_t httpd_now_ms()
427 | {
428 | struct timeval now;
429 |
430 | gettimeofday(&now, NULL);
431 |
432 | return now.tv_sec * 1000 + now.tv_usec / 1000;
433 | }
434 |
435 | static void httpd_handle_request(connection *conn) {
436 | char inbuf[INBUF_SIZE] = {0};
437 | int inbytes = 0;
438 | char httpreq[URL_LEN] = {0};
439 | time_t timeout = httpd_now_ms() + 100;
440 | int ret;
441 | char *httpver;
442 |
443 | ret = fcntl(conn->fd, F_SETFL, fcntl(conn->fd, F_GETFL) | O_NONBLOCK);
444 | if (ret) {
445 | printf("failed to set conn to non_blocking");
446 | goto close_fd;
447 | }
448 |
449 | for ( ; ; ) {
450 | time_t now = httpd_now_ms();
451 | if (now >= timeout)
452 | goto close_fd;
453 |
454 | struct pollfd pfd = {.fd = conn->fd, .events = POLLIN, .revents = 0};
455 | ret = poll(&pfd, 1, timeout - now);
456 | if (ret <= 0)
457 | {
458 | if (ret != 0) {
459 | goto close_fd;
460 | }
461 | continue;
462 | }
463 |
464 | ret = conn_read(conn, inbuf + inbytes, sizeof(inbuf) - inbytes);
465 | if (ret < 0)
466 | {
467 | if (ret != -EAGAIN) {
468 | goto close_fd;
469 | }
470 | continue;
471 | }
472 |
473 | inbytes += ret;
474 | if ((inbytes >= 4) && strstr(inbuf, "\r\n\r\n"))
475 | break;
476 |
477 | /* buf is full, but we can not search the end of HTTP header */
478 | if (inbytes == sizeof(inbuf))
479 | goto close_fd;
480 | }
481 |
482 | /* support GET request only */
483 | if (strncmp("GET ", inbuf, 4))
484 | goto close_fd;
485 |
486 | /* support HTTP 1.1 request only */
487 | httpver = strstr(inbuf, "HTTP/1.1");
488 | if (!httpver)
489 | goto close_fd;
490 |
491 | /* Ex, GET /hello HTTP/1.1 */
492 | if ((httpver - inbuf > URL_LEN + 6) || (httpver - inbuf < 6))
493 | goto close_fd;
494 |
495 | memcpy(httpreq, inbuf + 5, httpver - inbuf - 6);
496 | http_process_request(httpreq, conn);
497 |
498 | close_fd:
499 | conn_close(conn);
500 | return;
501 | }
502 |
503 | static void httpd_update_cache(char *log_path)
504 | {
505 | static time_t update;
506 | time_t now = time(NULL);
507 |
508 | if (now - update < 3)
509 | return;
510 |
511 | if (rawlog_parse_all(log_path)) {
512 | printf("%s: rawlog parse failed\n", __func__);
513 | }
514 |
515 | update = now;
516 | }
517 |
518 | static void *httpd_routine(connection **listeners, char *log_path)
519 | {
520 | int epollfd;
521 | int ret = 0;
522 | int nr_listener = 0;
523 | connection *listener;
524 | struct epoll_event event;
525 |
526 | epollfd = epoll_create1(0);
527 | if (epollfd < 0) {
528 | printf("Failed create epollfd\n");
529 | exit(1);
530 | }
531 |
532 | for (int i = 0; i < CONN_TYPE_MAX; i++) {
533 | listener = listeners[i];
534 | if (listener == NULL) {
535 | continue;
536 | }
537 |
538 | ret = conn_listen(listener);
539 | if (ret < 0) {
540 | printf("ConnectionType %s failed listening on port %u, aborting.\n", listener->type->get_type(NULL), listener->port);
541 | exit(1);
542 | }
543 |
544 | event.events = EPOLLIN;
545 | event.data.ptr = listener;
546 | if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listener->fd, &event)) {
547 | printf("Add listener into epoll failed\n");
548 | exit(1);
549 | }
550 | nr_listener++;
551 | }
552 |
553 | if (nr_listener == 0) {
554 | printf("Listen Nothing, Exit.\n");
555 | exit(1);
556 | }
557 |
558 | printf("Ready to serve\n");
559 | while (1) {
560 | ret = epoll_wait(epollfd, &event, 1, 1000);
561 | if (!ret) {
562 | continue;
563 | }
564 |
565 | if (ret < 0) {
566 | if (errno == EINTR) {
567 | continue;
568 | } else {
569 | printf("Error in epoll_wait %m\n");
570 | exit(1);
571 | }
572 | }
573 |
574 | listener = event.data.ptr;
575 | connection *conn;
576 | conn = conn_create(listener->type, -1, NULL);
577 | ret = conn_accept(listener, conn);
578 | if (ret < 0) {
579 | conn_close(conn);
580 | free(conn);
581 | continue;
582 | }
583 |
584 | httpd_handle_request(conn);
585 | httpd_update_cache(log_path);
586 | free(conn);
587 | }
588 |
589 | return NULL;
590 | }
591 |
592 | static int httpd(struct atophttd_context ctx)
593 | {
594 | signal(SIGPIPE, SIG_IGN);
595 | connection *listener;
596 | int conn_index, ret;
597 |
598 | if (ctx.port > 0) {
599 | conn_index = get_conntype_index_by_name(CONN_TYPE_SOCKET);
600 | if (conn_index < 0) {
601 | printf("Failed finding connectin listener of %s\n", CONN_TYPE_SOCKET);
602 | exit(1);
603 | }
604 | listener = conn_create(get_conntype_by_name(CONN_TYPE_SOCKET), ctx.port, ctx.addr);
605 | ctx.listeners[conn_index] = listener;
606 | }
607 |
608 | if (ctx.tls_ctx_config.tls_port > 0) {
609 | conn_index = get_conntype_index_by_name(CONN_TYPE_TLS);
610 | if (conn_index < 0) {
611 | exit(1);
612 | }
613 | listener = conn_create(get_conntype_by_name(CONN_TYPE_TLS), ctx.tls_ctx_config.tls_port, ctx.tls_ctx_config.tls_addr);
614 |
615 | ret = conn_configure(get_conntype_by_name(CONN_TYPE_TLS), &ctx.tls_ctx_config, 1);
616 | if (ret < 0) {
617 | printf("Failed to configure TLS\n");
618 | exit(1);
619 | }
620 |
621 | ctx.listeners[conn_index] = listener;
622 | }
623 |
624 | if (ctx.daemonmode)
625 | daemon(0, 0);
626 |
627 | httpd_routine(ctx.listeners, ctx.log_path);
628 | return 0;
629 | }
630 |
631 | int __debug = 0;
632 | static char *short_opts = "dDhHp:a:P:t::A:C:c:k:V";
633 |
634 | static struct option long_opts[] = {
635 | { "daemon", no_argument, 0, 'd' },
636 | { "debug", no_argument, 0, 'D' },
637 | { "port", required_argument, 0, 'p' },
638 | { "addr", required_argument, 0, 'a' },
639 | { "path", required_argument, 0, 'P' },
640 | { "tls-port", optional_argument, 0, 't'},
641 | { "tls-addr", required_argument, 0, 'A'},
642 | { "ca-cert-file", required_argument, 0, 'C' },
643 | { "cert-file", required_argument, 0, 'c' },
644 | { "key-file", required_argument, 0, 'k' },
645 | { "help", no_argument, 0, 'h' },
646 | { "hide-cmdline", no_argument, 0, 'H' },
647 | { "version", no_argument, 0, 'V' },
648 | { 0, 0, 0, 0 }
649 | };
650 |
651 | static void httpd_showhelp(void)
652 | {
653 | printf("Usage:\n");
654 | printf(" -d/--daemon \n run in daemon mode\n");
655 | printf(" -D/--debug \n run with debug message\n");
656 | printf(" -p/--port PORT \n listen to PORT, default %d\n", DEFAULT_PORT);
657 | printf(" -a/--addr ADDR \n bind to ADDR, default bind local host\n");
658 | printf(" -P/--path PATH \n atop log path, default %s\n", DEFAULT_LOG_PATH);
659 | printf(" -t/--tls-port PORT \n listen to TLS PORT, default %d\n", DEFAULT_TLS_PORT);
660 | printf(" -A/--tls-addr ADDR \n bind to TLS ADDR, default bind * (all addresses)\n");
661 | printf(" -C/--ca-cert-file PATH\n Path to the server TLS trusted CA cert file, default %s\n", DEFAULT_CA_FILE);
662 | printf(" -c/--cert-file PATH \n Path to the server TLS cert file, default %s\n", DEFAULT_CERT_FILE);
663 | printf(" -k/--key-file PATH \n Path to the server TLS key file, default %s\n", DEFAULT_KEY_FILE);
664 | printf(" -H/--hide-cmdline \n hide cmdline for security protection\n");
665 | printf(" -h/--help \n show help\n\n");
666 | printf(" maintained by \n zhenwei pi (HTTP backend)\n");
667 | printf(" enhua zhou (HTTP frontend)\n");
668 | exit(0);
669 | }
670 |
671 | static void httpd_showversion(void)
672 | {
673 | printf("Version: %s\n", ATOPVERS);
674 | exit(0);
675 | }
676 |
677 | int main(int argc, char *argv[])
678 | {
679 | int args, errno;
680 | int ch;
681 |
682 | while (1) {
683 | ch = getopt_long(argc, argv, short_opts, long_opts, &args);
684 | if (ch == -1) {
685 | break;
686 | }
687 | switch (ch) {
688 | case 0:
689 | break;
690 | case 'd':
691 | config.daemonmode = 1;
692 | break;
693 | case 'D':
694 | __debug = 1;
695 | break;
696 | case 'p':
697 | config.port = atoi(optarg);
698 | break;
699 | case 'a':
700 | config.addr = optarg;
701 | break;
702 | case 'P':
703 | config.log_path = optarg;
704 | break;
705 | case 't':
706 | if (optarg)
707 | config.tls_ctx_config.tls_port = atoi(optarg);
708 | else
709 | config.tls_ctx_config.tls_port = DEFAULT_TLS_PORT;
710 | break;
711 | case 'A':
712 | config.tls_ctx_config.tls_addr = optarg;
713 | break;
714 | case 'C':
715 | config.tls_ctx_config.ca_cert_file = optarg;
716 | break;
717 | case 'c':
718 | config.tls_ctx_config.cert_file = optarg;
719 | break;
720 | case 'k':
721 | config.tls_ctx_config.key_file = optarg;
722 | break;
723 | case 'H':
724 | hidecmdline = 1;
725 | break;
726 | case 'V':
727 | httpd_showversion();
728 | case 'h':
729 | default:
730 | httpd_showhelp();
731 | }
732 | }
733 |
734 | pagesize = sysconf(_SC_PAGESIZE);
735 | hertz = sysconf(_SC_CLK_TCK);
736 | uname(&utsname);
737 |
738 | if (config.log_path == NULL) {
739 | printf("%s: log path is nil\n", __func__);
740 | return -1;
741 | }
742 |
743 | if (rawlog_parse_all(config.log_path)) {
744 | printf("%s: rawlog parse failed\n", __func__);
745 | return -1;
746 | }
747 |
748 | log_debug("%s runs with log path(%s), port(%d)\n", argv[0], config.log_path, config.port);
749 |
750 | printf("%s runs with log path(%s), port(%d)\n", argv[0], config.log_path, config.port);
751 | conntype_initialize();
752 | errno = httpd(config);
753 |
754 | return errno;
755 | }
756 |
--------------------------------------------------------------------------------
/json.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 zhenwei pi
3 | *
4 | * Authors:
5 | * zhenwei pi
6 | * Fei Li
7 | *
8 | * This work is licensed under the terms of the GNU GPL, version 2 or later.
9 | * See the COPYING file in the top-level directory.
10 | */
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include "config.h"
27 | #include "atop.h"
28 | #include "json.h"
29 | #include "output.h"
30 |
31 | #define LEN_HP_SIZE 64
32 | #define LINE_BUF_SIZE 1024
33 |
34 | static void json_print_CPU();
35 | static void json_print_cpu();
36 | static void json_print_CPL();
37 | static void json_print_GPU();
38 | static void json_print_MEM();
39 | static void json_print_SWP();
40 | static void json_print_PAG();
41 | static void json_print_PSI();
42 | static void json_print_LVM();
43 | static void json_print_MDD();
44 | static void json_print_DSK();
45 | static void json_print_NFM();
46 | static void json_print_NFC();
47 | static void json_print_NFS();
48 | static void json_print_NET();
49 | static void json_print_IFB();
50 | static void json_print_NUM();
51 | static void json_print_NUC();
52 | static void json_print_LLC();
53 | static void json_print_PRG();
54 | static void json_print_PRC();
55 | static void json_print_PRM();
56 | static void json_print_PRD();
57 | static void json_print_PRN();
58 | static void json_print_PRE();
59 |
60 | /*
61 | ** table with possible labels and the corresponding
62 | ** print-function for json style output
63 | */
64 | struct labeldef {
65 | char *label;
66 | int valid;
67 | void (*prifunc)(int, char *, struct sstat *, struct tstat *, int);
68 | };
69 |
70 | extern struct output defop;
71 |
72 | static int jsondef(char *pd, struct labeldef *labeldef, int numlabels)
73 | {
74 | int i;
75 | char *p, *ep = pd + strlen(pd);
76 |
77 | if (*pd == '-') {
78 | char *err = "json lables should be followed by label list\n";
79 | output_samp(&defop, err, strlen(err));
80 | return -EINVAL;
81 | }
82 |
83 | while (pd < ep) {
84 | /*
85 | ** exchange comma by null-byte
86 | */
87 | if ((p = strchr(pd, ',')))
88 | *p = 0;
89 | else
90 | p = ep - 1;
91 |
92 | /*
93 | ** check if the next label exists
94 | */
95 | for (i = 0; i < numlabels; i++)
96 | {
97 | if (!strcmp(labeldef[i].label, pd)) {
98 | labeldef[i].valid = 1;
99 | break;
100 | }
101 | }
102 |
103 | if (i == numlabels)
104 | {
105 | if (!strcmp("ALL", pd)) {
106 | for (i = 0; i < numlabels; i++)
107 | labeldef[i].valid = 1;
108 | break;
109 | } else {
110 | char err[64];
111 | snprintf(err, sizeof(err), "json lables not supported: %s\n", pd);
112 | output_samp(&defop, err, strlen(err));
113 | return -EINVAL;
114 | }
115 | }
116 |
117 | pd = p + 1;
118 | }
119 |
120 | return 0;
121 | }
122 |
123 | int jsonout(int flags, char *pd, time_t curtime, int numsecs,
124 | struct devtstat *devtstat, struct sstat *sstat,
125 | int nexit, unsigned int noverflow, char flag, connection *conn)
126 | {
127 | char header[256], general[256];
128 | struct tstat *tmp = devtstat->taskall;
129 | int buflen = 0;
130 | int i, ret;
131 |
132 | struct labeldef labeldef[] = {
133 | { "CPU", 0, json_print_CPU },
134 | { "cpu", 0, json_print_cpu },
135 | { "CPL", 0, json_print_CPL },
136 | { "GPU", 0, json_print_GPU },
137 | { "MEM", 0, json_print_MEM },
138 | { "SWP", 0, json_print_SWP },
139 | { "PAG", 0, json_print_PAG },
140 | { "PSI", 0, json_print_PSI },
141 | { "LVM", 0, json_print_LVM },
142 | { "MDD", 0, json_print_MDD },
143 | { "DSK", 0, json_print_DSK },
144 | { "NFM", 0, json_print_NFM },
145 | { "NFC", 0, json_print_NFC },
146 | { "NFS", 0, json_print_NFS },
147 | { "NET", 0, json_print_NET },
148 | { "IFB", 0, json_print_IFB },
149 | { "NUM", 0, json_print_NUM },
150 | { "NUC", 0, json_print_NUC },
151 | { "LLC", 0, json_print_LLC },
152 |
153 | { "PRG", 0, json_print_PRG },
154 | { "PRC", 0, json_print_PRC },
155 | { "PRM", 0, json_print_PRM },
156 | { "PRD", 0, json_print_PRD },
157 | { "PRN", 0, json_print_PRN },
158 | { "PRE", 0, json_print_PRE },
159 | };
160 |
161 | int numlabels = sizeof(labeldef) / sizeof(struct labeldef);
162 |
163 | ret = jsondef(pd, labeldef, numlabels);
164 | if (ret) {
165 | output_samp_done(&defop, conn);
166 | return ret;
167 | }
168 |
169 | buflen = snprintf(general, sizeof(general),
170 | "{\"host\": \"%s\", "
171 | "\"timestamp\": %ld, "
172 | "\"elapsed\": %d",
173 | utsname.nodename,
174 | curtime,
175 | numsecs
176 | );
177 |
178 | output_samp(&defop, general, buflen);
179 |
180 | /* Replace " with # in case json can not parse this out */
181 | for (int k = 0; k < devtstat->ntaskall; k++, tmp++) {
182 | for (int j = 0; (j < sizeof(tmp->gen.name)) && tmp->gen.name[j]; j++)
183 | if ((tmp->gen.name[j] == '\"') || (tmp->gen.name[j] == '\\'))
184 | tmp->gen.name[j] = '#';
185 |
186 | if (hidecmdline) {
187 | strcpy(tmp->gen.cmdline, "***");
188 | continue;
189 | }
190 |
191 | for (int j = 0; (j < sizeof(tmp->gen.cmdline) && tmp->gen.cmdline[j]); j++)
192 | if ((tmp->gen.cmdline[j] == '\"') || (tmp->gen.cmdline[j] == '\\'))
193 | tmp->gen.cmdline[j] = '#';
194 | }
195 |
196 | for (i = 0; i < numlabels; i++) {
197 | if (!labeldef[i].valid)
198 | continue;
199 |
200 | /* prepare generic columns */
201 | snprintf(header, sizeof header, "\"%s\"",
202 | labeldef[i].label);
203 | /* call all print-functions */
204 | (labeldef[i].prifunc)(flags, header, sstat, devtstat->taskall, devtstat->ntaskall);
205 | }
206 |
207 | output_samp(&defop, "}\n", 2);
208 | output_samp_done(&defop, conn);
209 |
210 | return 0;
211 | }
212 |
213 | /*
214 | ** print functions for system-level statistics
215 | */
216 | static void
217 | json_calc_freqscale(count_t maxfreq, count_t cnt, count_t ticks,
218 | count_t *freq, int *freqperc)
219 | {
220 | // if ticks != 0,do full calcs
221 | if (maxfreq && ticks) {
222 | *freq = cnt / ticks;
223 | *freqperc = 100* *freq / maxfreq;
224 | } else if (maxfreq) { // max frequency is known so % can be calculated
225 | *freq = cnt;
226 | *freqperc = 100*cnt/maxfreq;
227 | } else if (cnt) { // no max known, set % to 100
228 | *freq = cnt;
229 | *freqperc = 100;
230 | } else { // nothing is known: set freq to 0, % to 100
231 | *freq = 0;
232 | *freqperc = 100;
233 | }
234 | }
235 |
236 | static void json_print_CPU(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
237 | {
238 | count_t maxfreq = 0;
239 | count_t cnt = 0;
240 | count_t ticks = 0;
241 | count_t freq;
242 | int freqperc;
243 | int i;
244 | int buflen = 0;
245 | char buf[LINE_BUF_SIZE];
246 |
247 | // calculate average clock frequency
248 | for (i = 0; i < ss->cpu.nrcpu; i++) {
249 | cnt += ss->cpu.cpu[i].freqcnt.cnt;
250 | ticks += ss->cpu.cpu[i].freqcnt.ticks;
251 | }
252 | maxfreq = ss->cpu.cpu[0].freqcnt.maxfreq;
253 | json_calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc);
254 |
255 | if (ss->cpu.all.instr == 1) {
256 | ss->cpu.all.instr = 0;
257 | ss->cpu.all.cycle = 0;
258 | }
259 |
260 | buflen = snprintf(buf, sizeof(buf), ", %s: {"
261 | "\"hertz\": %u, "
262 | "\"nrcpu\": %lld, "
263 | "\"stime\": %lld, "
264 | "\"utime\": %lld, "
265 | "\"ntime\": %lld, "
266 | "\"itime\": %lld, "
267 | "\"wtime\": %lld, "
268 | "\"Itime\": %lld, "
269 | "\"Stime\": %lld, "
270 | "\"steal\": %lld, "
271 | "\"guest\": %lld, "
272 | "\"freq\": %lld, "
273 | "\"freqperc\": %d, "
274 | "\"instr\": %lld, "
275 | "\"cycle\": %lld}",
276 | hp,
277 | hertz,
278 | ss->cpu.nrcpu,
279 | ss->cpu.all.stime,
280 | ss->cpu.all.utime,
281 | ss->cpu.all.ntime,
282 | ss->cpu.all.itime,
283 | ss->cpu.all.wtime,
284 | ss->cpu.all.Itime,
285 | ss->cpu.all.Stime,
286 | ss->cpu.all.steal,
287 | ss->cpu.all.guest,
288 | freq,
289 | freqperc,
290 | ss->cpu.all.instr,
291 | ss->cpu.all.cycle
292 | );
293 |
294 | output_samp(&defop, buf, buflen);
295 | }
296 |
297 | static void json_print_cpu(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
298 | {
299 | int i;
300 | count_t maxfreq = 0;
301 | count_t cnt = 0;
302 | count_t ticks = 0;
303 | count_t freq;
304 | int freqperc;
305 | int buflen = 0;
306 | char buf[LINE_BUF_SIZE];
307 |
308 | char br[LEN_HP_SIZE];
309 | buflen = sprintf(br, ", %s: [", hp);
310 | output_samp(&defop, br, buflen);
311 |
312 | for (i = 0; i < ss->cpu.nrcpu; i++) {
313 | if (i > 0) {
314 | output_samp(&defop, ", ", 2);
315 | }
316 | cnt = ss->cpu.cpu[i].freqcnt.cnt;
317 | ticks = ss->cpu.cpu[i].freqcnt.ticks;
318 | maxfreq= ss->cpu.cpu[0].freqcnt.maxfreq;
319 |
320 | json_calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc);
321 |
322 | buflen = snprintf(buf, sizeof(buf), "{\"cpuid\": %d, "
323 | "\"stime\": %lld, "
324 | "\"utime\": %lld, "
325 | "\"ntime\": %lld, "
326 | "\"itime\": %lld, "
327 | "\"wtime\": %lld, "
328 | "\"Itime\": %lld, "
329 | "\"Stime\": %lld, "
330 | "\"steal\": %lld, "
331 | "\"guest\": %lld, "
332 | "\"freq\": %lld, "
333 | "\"freqperc\": %d, "
334 | "\"instr\": %lld, "
335 | "\"cycle\": %lld}",
336 | i,
337 | ss->cpu.cpu[i].stime,
338 | ss->cpu.cpu[i].utime,
339 | ss->cpu.cpu[i].ntime,
340 | ss->cpu.cpu[i].itime,
341 | ss->cpu.cpu[i].wtime,
342 | ss->cpu.cpu[i].Itime,
343 | ss->cpu.cpu[i].Stime,
344 | ss->cpu.cpu[i].steal,
345 | ss->cpu.cpu[i].guest,
346 | freq,
347 | freqperc,
348 | ss->cpu.cpu[i].instr,
349 | ss->cpu.cpu[i].cycle
350 | );
351 | output_samp(&defop, buf, buflen);
352 | }
353 |
354 | output_samp(&defop, "]", 1);
355 | }
356 |
357 | static void json_print_CPL(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
358 | {
359 | int buflen = 0;
360 | char buf[LINE_BUF_SIZE];
361 |
362 | buflen = snprintf(buf, sizeof(buf), ", %s: {"
363 | "\"lavg1\": %.2f, "
364 | "\"lavg5\": %.2f, "
365 | "\"lavg15\": %.2f, "
366 | "\"csw\": %lld, "
367 | "\"devint\": %lld}",
368 | hp,
369 | ss->cpu.lavg1,
370 | ss->cpu.lavg5,
371 | ss->cpu.lavg15,
372 | ss->cpu.csw,
373 | ss->cpu.devint);
374 |
375 | output_samp(&defop, buf, buflen);
376 | }
377 |
378 | static void json_print_GPU(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
379 | {
380 | int i;
381 | int buflen = 0;
382 | char buf[LINE_BUF_SIZE];
383 |
384 | char br[LEN_HP_SIZE];
385 | buflen = sprintf(br, ", %s: [", hp);
386 | output_samp(&defop, br, buflen);
387 |
388 | for (i = 0; i < ss->gpu.nrgpus; i++) {
389 | if (i > 0) {
390 | output_samp(&defop, ", ", 2);
391 | }
392 | buflen = snprintf(buf, sizeof(buf), "{\"gpuid\": %d, "
393 | "\"busid\": \"%.19s\", "
394 | "\"type\": \"%.19s\", "
395 | "\"gpupercnow\": %d, "
396 | "\"mempercnow\": %d, "
397 | "\"memtotnow\": %lld, "
398 | "\"memusenow\": %lld, "
399 | "\"samples\": %lld, "
400 | "\"gpuperccum\": %lld, "
401 | "\"memperccum\": %lld, "
402 | "\"memusecum\": %lld}",
403 | i,
404 | ss->gpu.gpu[i].busid,
405 | ss->gpu.gpu[i].type,
406 | ss->gpu.gpu[i].gpupercnow,
407 | ss->gpu.gpu[i].mempercnow,
408 | ss->gpu.gpu[i].memtotnow,
409 | ss->gpu.gpu[i].memusenow,
410 | ss->gpu.gpu[i].samples,
411 | ss->gpu.gpu[i].gpuperccum,
412 | ss->gpu.gpu[i].memperccum,
413 | ss->gpu.gpu[i].memusecum);
414 | output_samp(&defop, buf, buflen);
415 | }
416 |
417 | output_samp(&defop, "]", 1);
418 | }
419 |
420 | static void json_print_MEM(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
421 | {
422 | int buflen = 0;
423 | char buf[LINE_BUF_SIZE];
424 |
425 | buflen = snprintf(buf, sizeof(buf), ", %s: {"
426 | "\"physmem\": %lld, "
427 | "\"freemem\": %lld, "
428 | "\"cachemem\": %lld, "
429 | "\"buffermem\": %lld, "
430 | "\"slabmem\": %lld, "
431 | "\"cachedrt\": %lld, "
432 | "\"slabreclaim\": %lld, "
433 | "\"vmwballoon\": %lld, "
434 | "\"shmem\": %lld, "
435 | "\"shmrss\": %lld, "
436 | "\"shmswp\": %lld, "
437 | "\"pagetables\": %lld, "
438 | "\"hugepagesz\": %lld, "
439 | "\"tothugepage\": %lld, "
440 | "\"freehugepage\": %lld, "
441 | "\"tcpsk\": %lld, "
442 | "\"udpsk\": %lld}",
443 | hp,
444 | ss->mem.physmem * pagesize,
445 | ss->mem.freemem * pagesize,
446 | ss->mem.cachemem * pagesize,
447 | ss->mem.buffermem * pagesize,
448 | ss->mem.slabmem * pagesize,
449 | ss->mem.cachedrt * pagesize,
450 | ss->mem.slabreclaim * pagesize,
451 | ss->mem.vmwballoon * pagesize,
452 | ss->mem.shmem * pagesize,
453 | ss->mem.shmrss * pagesize,
454 | ss->mem.shmswp * pagesize,
455 | ss->mem.pagetables * pagesize,
456 | ss->mem.hugepagesz,
457 | ss->mem.tothugepage,
458 | ss->mem.freehugepage,
459 | ss->mem.tcpsock * pagesize,
460 | ss->mem.udpsock * pagesize);
461 |
462 | output_samp(&defop, buf, buflen);
463 | }
464 |
465 | static void json_print_SWP(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
466 | {
467 | int buflen = 0;
468 | char buf[LINE_BUF_SIZE];
469 |
470 | buflen = snprintf(buf, sizeof(buf), ", %s: {"
471 | "\"totswap\": %lld, "
472 | "\"freeswap\": %lld, "
473 | "\"swcac\": %lld, "
474 | "\"committed\": %lld, "
475 | "\"commitlim\": %lld}",
476 | hp,
477 | ss->mem.totswap * pagesize,
478 | ss->mem.freeswap * pagesize,
479 | ss->mem.swapcached * pagesize,
480 | ss->mem.committed * pagesize,
481 | ss->mem.commitlim * pagesize);
482 |
483 | output_samp(&defop, buf, buflen);
484 | }
485 |
486 | static void json_print_PAG(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
487 | {
488 | int buflen = 0;
489 | char buf[LINE_BUF_SIZE];
490 |
491 | buflen = snprintf(buf, sizeof(buf), ", %s: {"
492 | "\"stall\": %lld, "
493 | "\"compacts\": %lld, "
494 | "\"numamigs\": %lld, "
495 | "\"migrates\": %lld, "
496 | "\"pgscans\": %lld, "
497 | "\"pgsteal\": %lld,"
498 | "\"allocstall\": %lld, "
499 | "\"pgins\": %lld, "
500 | "\"pgouts\": %lld, "
501 | "\"swins\": %lld, "
502 | "\"swouts\": %lld, "
503 | "\"oomkills\": %lld}",
504 | hp,
505 | ss->mem.allocstall,
506 | ss->mem.compactstall,
507 | ss->mem.numamigrate,
508 | ss->mem.pgmigrate,
509 | ss->mem.pgscans,
510 | ss->mem.pgsteal,
511 | ss->mem.allocstall,
512 | ss->mem.pgins,
513 | ss->mem.pgouts,
514 | ss->mem.swins,
515 | ss->mem.swouts,
516 | ss->mem.oomkills);
517 |
518 | output_samp(&defop, buf, buflen);
519 | }
520 |
521 | static void json_print_PSI(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
522 | {
523 | if ( !(ss->psi.present) )
524 | return;
525 |
526 | int buflen = 0;
527 | char buf[LINE_BUF_SIZE];
528 |
529 | buflen = snprintf(buf, sizeof(buf), ", %s: {"
530 | "\"psi\": \"%c\", "
531 | "\"cs10\": %.1f, "
532 | "\"cs60\": %.1f, "
533 | "\"cs300\": %.1f, "
534 | "\"cstot\": %llu, "
535 | "\"ms10\": %.1f, "
536 | "\"ms60\": %.1f, "
537 | "\"ms300\": %.1f, "
538 | "\"mstot\": %llu, "
539 | "\"mf10\": %.1f, "
540 | "\"mf60\": %.1f, "
541 | "\"mf300\": %.1f, "
542 | "\"mftot\": %llu, "
543 | "\"ios10\": %.1f, "
544 | "\"ios60\": %.1f, "
545 | "\"ios300\": %.1f, "
546 | "\"iostot\": %llu, "
547 | "\"iof10\": %.1f, "
548 | "\"iof60\": %.1f, "
549 | "\"iof300\": %.1f, "
550 | "\"ioftot\": %llu}",
551 | hp, ss->psi.present ? 'y' : 'n',
552 | ss->psi.cpusome.avg10, ss->psi.cpusome.avg60,
553 | ss->psi.cpusome.avg300, ss->psi.cpusome.total,
554 | ss->psi.memsome.avg10, ss->psi.memsome.avg60,
555 | ss->psi.memsome.avg300, ss->psi.memsome.total,
556 | ss->psi.memfull.avg10, ss->psi.memfull.avg60,
557 | ss->psi.memfull.avg300, ss->psi.memfull.total,
558 | ss->psi.iosome.avg10, ss->psi.iosome.avg60,
559 | ss->psi.iosome.avg300, ss->psi.iosome.total,
560 | ss->psi.iofull.avg10, ss->psi.iofull.avg60,
561 | ss->psi.iofull.avg300, ss->psi.iofull.total);
562 |
563 | output_samp(&defop, buf, buflen);
564 | }
565 |
566 | static void json_print_LVM(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
567 | {
568 | int i;
569 | int buflen = 0;
570 | char buf[LINE_BUF_SIZE];
571 |
572 | char br[LEN_HP_SIZE];
573 | buflen = sprintf(br, ", %s: [", hp);
574 | output_samp(&defop, br, buflen);
575 |
576 | for (i = 0; ss->dsk.lvm[i].name[0]; i++) {
577 | if (i > 0) {
578 | output_samp(&defop, ", ", 2);
579 | }
580 | buflen = snprintf(buf, sizeof(buf), "{\"lvmname\": \"%.19s\", "
581 | "\"io_ms\": %lld, "
582 | "\"nread\": %lld, "
583 | "\"ndiscrd\": %lld, "
584 | "\"nrsect\": %lld, "
585 | "\"nwrite\": %lld, "
586 | "\"nwsect\": %lld, "
587 | "\"avque\": %lld, "
588 | "\"inflight\": %lld}",
589 | ss->dsk.lvm[i].name,
590 | ss->dsk.lvm[i].io_ms,
591 | ss->dsk.lvm[i].nread,
592 | ss->dsk.lvm[i].ndisc,
593 | ss->dsk.lvm[i].nrsect,
594 | ss->dsk.lvm[i].nwrite,
595 | ss->dsk.lvm[i].nwsect,
596 | ss->dsk.lvm[i].avque,
597 | ss->dsk.lvm[i].inflight);
598 | output_samp(&defop, buf, buflen);
599 | }
600 |
601 | output_samp(&defop, "]", 1);
602 | }
603 |
604 | static void json_print_MDD(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
605 | {
606 | int i;
607 | int buflen = 0;
608 | char buf[LINE_BUF_SIZE];
609 |
610 | char br[LEN_HP_SIZE];
611 | buflen = sprintf(br, ", %s: [", hp);
612 | output_samp(&defop, br, buflen);
613 |
614 | for (i = 0; ss->dsk.mdd[i].name[0]; i++) {
615 | if (i > 0) {
616 | output_samp(&defop, ", ", 2);
617 | }
618 | buflen = snprintf(buf, sizeof(buf), "{\"mddname\": \"%.19s\", "
619 | "\"io_ms\": %lld, "
620 | "\"nread\": %lld, "
621 | "\"nrsect\": %lld, "
622 | "\"nwrite\": %lld, "
623 | "\"nwsect\": %lld, "
624 | "\"avque\": %lld, "
625 | "\"inflight\": %lld}",
626 | ss->dsk.mdd[i].name,
627 | ss->dsk.mdd[i].io_ms,
628 | ss->dsk.mdd[i].nread,
629 | ss->dsk.mdd[i].nrsect,
630 | ss->dsk.mdd[i].nwrite,
631 | ss->dsk.mdd[i].nwsect,
632 | ss->dsk.mdd[i].avque,
633 | ss->dsk.mdd[i].inflight);
634 | output_samp(&defop, buf, buflen);
635 | }
636 |
637 | output_samp(&defop, "]", 1);
638 | }
639 |
640 | static void json_print_DSK(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
641 | {
642 | int i;
643 | int buflen = 0;
644 | char buf[LINE_BUF_SIZE];
645 |
646 | char br[LEN_HP_SIZE];
647 | buflen = sprintf(br, ", %s: [", hp);
648 | output_samp(&defop, br, buflen);
649 |
650 | for (i = 0; ss->dsk.dsk[i].name[0]; i++) {
651 | if (i > 0) {
652 | output_samp(&defop, ", ", 2);
653 | }
654 | buflen = snprintf(buf, sizeof(buf), "{\"dskname\": \"%.19s\", "
655 | "\"io_ms\": %lld, "
656 | "\"nread\": %lld, "
657 | "\"nrsect\": %lld, "
658 | "\"ndiscrd\": %lld, "
659 | "\"nwrite\": %lld, "
660 | "\"nwsect\": %lld, "
661 | "\"avque\": %lld, "
662 | "\"inflight\": %lld}",
663 | ss->dsk.dsk[i].name,
664 | ss->dsk.dsk[i].io_ms,
665 | ss->dsk.dsk[i].nread,
666 | ss->dsk.dsk[i].nrsect,
667 | ss->dsk.dsk[i].ndisc,
668 | ss->dsk.dsk[i].nwrite,
669 | ss->dsk.dsk[i].nwsect,
670 | ss->dsk.dsk[i].avque,
671 | ss->dsk.dsk[i].inflight);
672 | output_samp(&defop, buf, buflen);
673 | }
674 |
675 | output_samp(&defop, "]", 1);
676 | }
677 |
678 | static void json_print_NFM(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
679 | {
680 | int i;
681 | int buflen = 0;
682 | char buf[LINE_BUF_SIZE];
683 |
684 | char br[LEN_HP_SIZE];
685 | buflen = sprintf(br, ", %s: [", hp);
686 | output_samp(&defop, br, buflen);
687 |
688 | for (i = 0; i < ss->nfs.nfsmounts.nrmounts; i++) {
689 | if (i > 0) {
690 | output_samp(&defop, ", ", 2);
691 | }
692 | buflen = snprintf(buf, sizeof(buf), "{\"mountdev\": \"%.19s\", "
693 | "\"bytestotread\": %lld, "
694 | "\"bytestotwrite\": %lld, "
695 | "\"bytesread\": %lld, "
696 | "\"byteswrite\": %lld, "
697 | "\"bytesdread\": %lld, "
698 | "\"bytesdwrite\": %lld, "
699 | "\"pagesmread\": %lld, "
700 | "\"pagesmwrite\": %lld}",
701 | ss->nfs.nfsmounts.nfsmnt[i].mountdev,
702 | ss->nfs.nfsmounts.nfsmnt[i].bytestotread,
703 | ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite,
704 | ss->nfs.nfsmounts.nfsmnt[i].bytesread,
705 | ss->nfs.nfsmounts.nfsmnt[i].byteswrite,
706 | ss->nfs.nfsmounts.nfsmnt[i].bytesdread,
707 | ss->nfs.nfsmounts.nfsmnt[i].bytesdwrite,
708 | ss->nfs.nfsmounts.nfsmnt[i].pagesmread * pagesize,
709 | ss->nfs.nfsmounts.nfsmnt[i].pagesmwrite * pagesize);
710 | output_samp(&defop, buf, buflen);
711 | }
712 |
713 | output_samp(&defop, "]", 1);
714 | }
715 |
716 | static void json_print_NFC(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
717 | {
718 | int buflen = 0;
719 | char buf[LINE_BUF_SIZE];
720 |
721 | buflen = snprintf(buf, sizeof(buf), ", %s: {"
722 | "\"rpccnt\": %lld, "
723 | "\"rpcread\": %lld, "
724 | "\"rpcwrite\": %lld, "
725 | "\"rpcretrans\": %lld, "
726 | "\"rpcautrefresh\": %lld}",
727 | hp,
728 | ss->nfs.client.rpccnt,
729 | ss->nfs.client.rpcread,
730 | ss->nfs.client.rpcwrite,
731 | ss->nfs.client.rpcretrans,
732 | ss->nfs.client.rpcautrefresh);
733 |
734 | output_samp(&defop, buf, buflen);
735 | }
736 |
737 | static void json_print_NFS(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
738 | {
739 | int buflen = 0;
740 | char buf[LINE_BUF_SIZE];
741 |
742 | buflen = snprintf(buf, sizeof(buf), ", %s: {"
743 | "\"rpccnt\": %lld, "
744 | "\"rpcread\": %lld, "
745 | "\"rpcwrite\": %lld, "
746 | "\"nrbytes\": %lld, "
747 | "\"nwbytes\": %lld, "
748 | "\"rpcbadfmt\": %lld, "
749 | "\"rpcbadaut\": %lld, "
750 | "\"rpcbadcln\": %lld, "
751 | "\"netcnt\": %lld, "
752 | "\"nettcpcnt\": %lld, "
753 | "\"netudpcnt\": %lld, "
754 | "\"nettcpcon\": %lld, "
755 | "\"rchits\": %lld, "
756 | "\"rcmiss\": %lld, "
757 | "\"rcnocache\": %lld}",
758 | hp,
759 | ss->nfs.server.rpccnt,
760 | ss->nfs.server.rpcread,
761 | ss->nfs.server.rpcwrite,
762 | ss->nfs.server.nrbytes,
763 | ss->nfs.server.nwbytes,
764 | ss->nfs.server.rpcbadfmt,
765 | ss->nfs.server.rpcbadaut,
766 | ss->nfs.server.rpcbadcln,
767 | ss->nfs.server.netcnt,
768 | ss->nfs.server.nettcpcnt,
769 | ss->nfs.server.netudpcnt,
770 | ss->nfs.server.nettcpcon,
771 | ss->nfs.server.rchits,
772 | ss->nfs.server.rcmiss,
773 | ss->nfs.server.rcnoca);
774 |
775 | output_samp(&defop, buf, buflen);
776 | }
777 |
778 | static void json_print_NET(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
779 | {
780 | int i;
781 | int buflen = 0;
782 | char buf[LINE_BUF_SIZE];
783 |
784 | buflen = snprintf(buf, sizeof(buf), ", \"NET_GENERAL\": {"
785 | "\"rpacketsTCP\": %lld, "
786 | "\"spacketsTCP\": %lld, "
787 | "\"inerrTCP\": %lld, "
788 | "\"oresetTCP\": %lld, "
789 | "\"activeOpensTCP\": %lld, "
790 | "\"passiveOpensTCP\": %lld, "
791 | "\"retransSegsTCP\": %lld, "
792 | "\"noportUDP\": %lld, "
793 | "\"inerrUDP\": %lld, "
794 | "\"rpacketsUDP\": %lld, "
795 | "\"spacketsUDP\": %lld, "
796 | "\"rpacketsIP\": %lld, "
797 | "\"spacketsIP\": %lld, "
798 | "\"dpacketsIP\": %lld, "
799 | "\"fpacketsIP\": %lld, "
800 | "\"icmpi\" : %lld, "
801 | "\"icmpo\" : %lld}",
802 | ss->net.tcp.InSegs,
803 | ss->net.tcp.OutSegs,
804 | ss->net.tcp.InErrs,
805 | ss->net.tcp.OutRsts,
806 | ss->net.tcp.ActiveOpens,
807 | ss->net.tcp.PassiveOpens,
808 | ss->net.tcp.RetransSegs,
809 | ss->net.udpv4.NoPorts,
810 | ss->net.udpv4.InErrors,
811 | ss->net.udpv4.InDatagrams +
812 | ss->net.udpv6.Udp6InDatagrams,
813 | ss->net.udpv4.OutDatagrams +
814 | ss->net.udpv6.Udp6OutDatagrams,
815 | ss->net.ipv4.InReceives +
816 | ss->net.ipv6.Ip6InReceives,
817 | ss->net.ipv4.OutRequests +
818 | ss->net.ipv6.Ip6OutRequests,
819 | ss->net.ipv4.InDelivers +
820 | ss->net.ipv6.Ip6InDelivers,
821 | ss->net.ipv4.ForwDatagrams +
822 | ss->net.ipv6.Ip6OutForwDatagrams,
823 | ss->net.icmpv4.InMsgs +
824 | ss->net.icmpv6.Icmp6InMsgs,
825 | ss->net.icmpv4.OutMsgs +
826 | ss->net.icmpv6.Icmp6OutMsgs);
827 | output_samp(&defop, buf, buflen);
828 |
829 | char br[LEN_HP_SIZE];
830 | buflen = sprintf(br, ", %s: [", hp);
831 | output_samp(&defop, br, buflen);
832 |
833 | for (i = 0; ss->intf.intf[i].name[0]; i++) {
834 | if (i > 0) {
835 | output_samp(&defop, ", ", 2);
836 | }
837 | buflen = snprintf(buf, sizeof(buf), "{\"name\": \"%.19s\", "
838 | "\"rpack\": %lld, "
839 | "\"rbyte\": %lld, "
840 | "\"rerrs\": %lld, "
841 | "\"rdrops\": %lld, "
842 | "\"spack\": %lld, "
843 | "\"sbyte\": %lld, "
844 | "\"serrs\": %lld, "
845 | "\"sdrops\": %lld, "
846 | "\"speed\": \"%ld\", "
847 | "\"coll\": %lld, "
848 | "\"multi\": %lld, "
849 | "\"duplex\": %d}",
850 | ss->intf.intf[i].name,
851 | ss->intf.intf[i].rpack,
852 | ss->intf.intf[i].rbyte,
853 | ss->intf.intf[i].rerrs,
854 | ss->intf.intf[i].rdrop,
855 | ss->intf.intf[i].spack,
856 | ss->intf.intf[i].sbyte,
857 | ss->intf.intf[i].serrs,
858 | ss->intf.intf[i].sdrop,
859 | ss->intf.intf[i].speed,
860 | ss->intf.intf[i].scollis,
861 | ss->intf.intf[i].rmultic,
862 | ss->intf.intf[i].duplex);
863 | output_samp(&defop, buf, buflen);
864 | }
865 |
866 | output_samp(&defop, "]", 1);
867 | }
868 |
869 | static void json_print_IFB(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
870 | {
871 | int i;
872 | int buflen = 0;
873 | char buf[LINE_BUF_SIZE];
874 |
875 | char br[LEN_HP_SIZE];
876 | buflen = sprintf(br, ", %s: [", hp);
877 | output_samp(&defop, br, buflen);
878 |
879 | for (i = 0; i < ss->ifb.nrports; i++) {
880 | if (i > 0) {
881 | output_samp(&defop, ", ", 2);
882 | }
883 | buflen = snprintf(buf, sizeof(buf), "{\"ibname\": \"%.19s\", "
884 | "\"portnr\": \"%hd\", "
885 | "\"lanes\": \"%hd\", "
886 | "\"maxrate\": %lld, "
887 | "\"rcvb\": %lld, "
888 | "\"sndb\": %lld, "
889 | "\"rcvp\": %lld, "
890 | "\"sndp\": %lld}",
891 | ss->ifb.ifb[i].ibname,
892 | ss->ifb.ifb[i].portnr,
893 | ss->ifb.ifb[i].lanes,
894 | ss->ifb.ifb[i].rate,
895 | ss->ifb.ifb[i].rcvb,
896 | ss->ifb.ifb[i].sndb,
897 | ss->ifb.ifb[i].rcvp,
898 | ss->ifb.ifb[i].sndp);
899 | output_samp(&defop, buf, buflen);
900 | }
901 |
902 | output_samp(&defop, "]", 1);
903 | }
904 |
905 | static void json_print_NUM(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
906 | {
907 | int i;
908 | int buflen = 0;
909 | char buf[LINE_BUF_SIZE];
910 |
911 | char br[LEN_HP_SIZE];
912 | buflen = sprintf(br, ", %s: [", hp);
913 | output_samp(&defop, br, buflen);
914 |
915 | for (i = 0; i < ss->memnuma.nrnuma; i++) {
916 | if (i > 0) {
917 | output_samp(&defop, ", ", 2);
918 | }
919 | buflen = snprintf(buf, sizeof(buf), "{\"frag\": \"%f\", "
920 | "\"totmem\": %lld, "
921 | "\"freemem\": %lld, "
922 | "\"active\": %lld, "
923 | "\"inactive\": %lld, "
924 | "\"filepage\": %lld, "
925 | "\"dirtymem\": %lld, "
926 | "\"slabmem\": %lld, "
927 | "\"slabreclaim\": %lld, "
928 | "\"shmem\": %lld, "
929 | "\"tothp\": %lld}",
930 | ss->memnuma.numa[i].frag * 100.0,
931 | ss->memnuma.numa[i].totmem * pagesize,
932 | ss->memnuma.numa[i].freemem * pagesize,
933 | ss->memnuma.numa[i].active * pagesize,
934 | ss->memnuma.numa[i].inactive * pagesize,
935 | ss->memnuma.numa[i].filepage * pagesize,
936 | ss->memnuma.numa[i].dirtymem * pagesize,
937 | ss->memnuma.numa[i].slabmem * pagesize,
938 | ss->memnuma.numa[i].slabreclaim * pagesize,
939 | ss->memnuma.numa[i].shmem * pagesize,
940 | ss->memnuma.numa[i].tothp * ss->mem.hugepagesz);
941 | output_samp(&defop, buf, buflen);
942 | }
943 |
944 | output_samp(&defop, "]", 1);
945 | }
946 |
947 | static void json_print_NUC(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
948 | {
949 | int i;
950 | int buflen = 0;
951 | char buf[LINE_BUF_SIZE];
952 |
953 | char br[LEN_HP_SIZE];
954 | buflen = sprintf(br, ", %s: [", hp);
955 | output_samp(&defop, br, buflen);
956 |
957 | for (i = 0; i < ss->cpunuma.nrnuma; i++) {
958 | if (i > 0) {
959 | output_samp(&defop, ", ", 2);
960 | }
961 | buflen = snprintf(buf, sizeof(buf), "{\"numanr\": \"%d\", "
962 | "\"nrcpu\": %lld, "
963 | "\"stime\": %lld, "
964 | "\"utime\": %lld, "
965 | "\"ntime\": %lld, "
966 | "\"itime\": %lld, "
967 | "\"wtime\": %lld, "
968 | "\"Itime\": %lld, "
969 | "\"Stime\": %lld, "
970 | "\"steal\": %lld, "
971 | "\"guest\": %lld}",
972 | ss->cpunuma.numa[i].numanr,
973 | ss->cpunuma.numa[i].nrcpu,
974 | ss->cpunuma.numa[i].stime,
975 | ss->cpunuma.numa[i].utime,
976 | ss->cpunuma.numa[i].ntime,
977 | ss->cpunuma.numa[i].itime,
978 | ss->cpunuma.numa[i].wtime,
979 | ss->cpunuma.numa[i].Itime,
980 | ss->cpunuma.numa[i].Stime,
981 | ss->cpunuma.numa[i].steal,
982 | ss->cpunuma.numa[i].guest);
983 | output_samp(&defop, buf, buflen);
984 | }
985 |
986 | output_samp(&defop, "]", 1);
987 | }
988 |
989 | static void json_print_LLC(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
990 | {
991 | int i;
992 | int buflen = 0;
993 | char buf[LINE_BUF_SIZE];
994 |
995 | char br[LEN_HP_SIZE];
996 | buflen = sprintf(br, ", %s: [", hp);
997 | output_samp(&defop, br, buflen);
998 |
999 | for (i = 0; i < ss->llc.nrllcs; i++) {
1000 | if (i > 0) {
1001 | output_samp(&defop, ", ", 2);
1002 | }
1003 | buflen = snprintf(buf, sizeof(buf), "{\"LLC\": \"%3d\", "
1004 | "\"occupancy\": \"%3.1f\", "
1005 | "\"mbm_total\": \"%lld\", "
1006 | "\"mbm_local\": %lld}",
1007 | ss->llc.perllc[i].id,
1008 | ss->llc.perllc[i].occupancy * 100,
1009 | ss->llc.perllc[i].mbm_total,
1010 | ss->llc.perllc[i].mbm_local);
1011 | output_samp(&defop, buf, buflen);
1012 | }
1013 |
1014 | output_samp(&defop, "]", 1);
1015 | }
1016 |
1017 | /*
1018 | ** print functions for process-level statistics
1019 | */
1020 | static void json_print_PRG(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
1021 | {
1022 | int i, exitcode;
1023 | int buflen = 0;
1024 | char buf[LINE_BUF_SIZE];
1025 | char br[LEN_HP_SIZE];
1026 | buflen = sprintf(br, ", %s: [", hp);
1027 | output_samp(&defop, br, buflen);
1028 |
1029 | static char st[3];
1030 |
1031 | for (i = 0; i < nact; i++, ps++) {
1032 | /* For one thread whose pid==tgid and isproc=n, it has the same
1033 | value with pid==tgid and isproc=y, thus filter it out. */
1034 | if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc)
1035 | continue;
1036 |
1037 | if (ps->gen.excode & ~(INT_MAX))
1038 | st[0]='N';
1039 | else
1040 | st[0]='-';
1041 |
1042 | if (ps->gen.excode & 0xff) // killed by signal?
1043 | {
1044 | exitcode = (ps->gen.excode & 0x7f) + 256;
1045 | if (ps->gen.excode & 0x80)
1046 | st[1] = 'C';
1047 | else
1048 | st[1] = 'S';
1049 | }
1050 | else
1051 | {
1052 | exitcode = (ps->gen.excode >> 8) & 0xff;
1053 | st[1] = 'E';
1054 | }
1055 |
1056 | if (i > 0) {
1057 | output_samp(&defop, ", ", 2);
1058 | }
1059 |
1060 | /* using getpwuid() & getpwuid to convert ruid & euid to string seems better, but the two functions take a long time */
1061 | buflen = snprintf(buf, sizeof(buf), "{\"pid\": %d, "
1062 | "\"name\": \"(%.19s)\", "
1063 | "\"state\": \"%c\", "
1064 | "\"ruid\": %d, "
1065 | "\"rgid\": %d, "
1066 | "\"tgid\": %d, "
1067 | "\"nthr\": %d, "
1068 | "\"st\": \"%s\", "
1069 | "\"exitcode\": %d, "
1070 | "\"btime\": \"%ld\", "
1071 | "\"cmdline\": \"(%.130s)\", "
1072 | "\"ppid\": %d, "
1073 | "\"nthrrun\": %d, "
1074 | "\"nthrslpi\": %d, "
1075 | "\"nthrslpu\": %d, "
1076 | "\"euid\": %d, "
1077 | "\"egid\": %d, "
1078 | "\"elaps\": \"%ld\", "
1079 | "\"isproc\": %d, "
1080 | "\"cid\": \"%.19s\"}",
1081 | ps->gen.pid,
1082 | ps->gen.name,
1083 | ps->gen.state,
1084 | ps->gen.ruid,
1085 | ps->gen.rgid,
1086 | ps->gen.tgid,
1087 | ps->gen.nthr,
1088 | st,
1089 | exitcode,
1090 | ps->gen.btime,
1091 | ps->gen.cmdline,
1092 | ps->gen.ppid,
1093 | ps->gen.nthrrun,
1094 | ps->gen.nthrslpi,
1095 | ps->gen.nthrslpu,
1096 | ps->gen.euid,
1097 | ps->gen.egid,
1098 | ps->gen.elaps,
1099 | !!ps->gen.isproc, /* convert to boolean */
1100 | ps->gen.container[0] ? ps->gen.container:"-");
1101 | output_samp(&defop, buf, buflen);
1102 | }
1103 |
1104 | output_samp(&defop, "]", 1);
1105 | }
1106 |
1107 | static void json_print_PRC(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
1108 | {
1109 | int i;
1110 | int buflen = 0;
1111 | char buf[LINE_BUF_SIZE];
1112 |
1113 | char br[LEN_HP_SIZE];
1114 | buflen = sprintf(br, ", %s: [", hp);
1115 | output_samp(&defop, br, buflen);
1116 |
1117 | for (i = 0; i < nact; i++, ps++) {
1118 | if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc)
1119 | continue;
1120 | if (i > 0) {
1121 | output_samp(&defop, ", ", 2);
1122 | }
1123 | buflen = snprintf(buf, sizeof(buf), "{\"pid\": %d, "
1124 | "\"utime\": %lld, "
1125 | "\"stime\": %lld, "
1126 | "\"nice\": %d, "
1127 | "\"prio\": %d, "
1128 | "\"curcpu\": %d, "
1129 | "\"tgid\": %d, "
1130 | "\"isproc\": %d, "
1131 | "\"rundelay\": %lld, "
1132 | "\"blkdelay\": %lld, "
1133 | "\"sleepavg\": %d}",
1134 | ps->gen.pid,
1135 | ps->cpu.utime,
1136 | ps->cpu.stime,
1137 | ps->cpu.nice,
1138 | ps->cpu.prio,
1139 | ps->cpu.curcpu,
1140 | ps->gen.tgid,
1141 | !!ps->gen.isproc,
1142 | ps->cpu.rundelay/1000000,
1143 | ps->cpu.blkdelay*1000/hertz,
1144 | ps->cpu.sleepavg);
1145 | output_samp(&defop, buf, buflen);
1146 | }
1147 |
1148 | output_samp(&defop, "]", 1);
1149 | }
1150 |
1151 | static void json_print_PRM(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
1152 | {
1153 | int i;
1154 | int buflen = 0;
1155 | char buf[LINE_BUF_SIZE];
1156 |
1157 | char br[LEN_HP_SIZE];
1158 | buflen = sprintf(br, ", %s: [", hp);
1159 | output_samp(&defop, br, buflen);
1160 |
1161 | for (i = 0; i < nact; i++, ps++) {
1162 | if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc)
1163 | continue;
1164 | if (i > 0) {
1165 | output_samp(&defop, ", ", 2);
1166 | }
1167 | buflen = snprintf(buf, sizeof(buf), "{\"pid\": %d, "
1168 | "\"vmem\": %lld, "
1169 | "\"rmem\": %lld, "
1170 | "\"vexec\": %lld, "
1171 | "\"vgrow\": %lld, "
1172 | "\"rgrow\": %lld, "
1173 | "\"minflt\": %lld, "
1174 | "\"majflt\": %lld, "
1175 | "\"vlibs\": %lld, "
1176 | "\"vdata\": %lld, "
1177 | "\"vstack\": %lld, "
1178 | "\"vlock\": %lld, "
1179 | "\"vswap\": %lld, "
1180 | "\"pmem\": %lld}",
1181 | ps->gen.pid,
1182 | ps->mem.vmem,
1183 | ps->mem.rmem,
1184 | ps->mem.vexec,
1185 | ps->mem.vgrow,
1186 | ps->mem.rgrow,
1187 | ps->mem.minflt,
1188 | ps->mem.majflt,
1189 | ps->mem.vlibs,
1190 | ps->mem.vdata,
1191 | ps->mem.vstack,
1192 | ps->mem.vlock,
1193 | ps->mem.vswap,
1194 | ps->mem.pmem == (unsigned long long)-1LL ?
1195 | 0:ps->mem.pmem);
1196 | output_samp(&defop, buf, buflen);
1197 | }
1198 |
1199 | output_samp(&defop, "]", 1);
1200 | }
1201 |
1202 | static void json_print_PRD(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
1203 | {
1204 | int i;
1205 | int buflen = 0;
1206 | char buf[LINE_BUF_SIZE];
1207 |
1208 | char br[LEN_HP_SIZE];
1209 | buflen = sprintf(br, ", %s: [", hp);
1210 | output_samp(&defop, br, buflen);
1211 |
1212 | for (i = 0; i < nact; i++, ps++) {
1213 | if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc)
1214 | continue;
1215 | if (i > 0) {
1216 | output_samp(&defop, ", ", 2);
1217 | }
1218 | buflen = snprintf(buf, sizeof(buf), "{\"pid\": %d, "
1219 | "\"rio\": %lld, "
1220 | "\"rsz\": %lld, "
1221 | "\"wio\": %lld, "
1222 | "\"wsz\": %lld, "
1223 | "\"cwsz\": %lld}",
1224 | ps->gen.pid,
1225 | ps->dsk.rio, ps->dsk.rsz,
1226 | ps->dsk.wio, ps->dsk.wsz, ps->dsk.cwsz);
1227 | output_samp(&defop, buf, buflen);
1228 | }
1229 |
1230 | output_samp(&defop, "]", 1);
1231 | }
1232 |
1233 | static void json_print_PRN(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
1234 | {
1235 | if (!(flags & NETATOP))
1236 | return;
1237 |
1238 | int i;
1239 | int buflen = 0;
1240 | char buf[LINE_BUF_SIZE];
1241 |
1242 | char br[LEN_HP_SIZE];
1243 | buflen = sprintf(br, ", %s: [", hp);
1244 | output_samp(&defop, br, buflen);
1245 |
1246 | for (i = 0; i < nact; i++, ps++) {
1247 | if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc)
1248 | continue;
1249 | if (i > 0) {
1250 | output_samp(&defop, ", ", 2);
1251 | }
1252 | buflen = snprintf(buf, sizeof(buf), "{\"pid\": %d, "
1253 | "\"tcpsnd\": \"%lld\", "
1254 | "\"tcpssz\": \"%lld\", "
1255 | "\"tcprcv\": \"%lld\", "
1256 | "\"tcprsz\": \"%lld\", "
1257 | "\"udpsnd\": \"%lld\", "
1258 | "\"udpssz\": \"%lld\", "
1259 | "\"udprcv\": \"%lld\", "
1260 | "\"udprsz\": \"%lld\"}",
1261 | ps->gen.pid,
1262 | ps->net.tcpsnd, ps->net.tcpssz,
1263 | ps->net.tcprcv, ps->net.tcprsz,
1264 | ps->net.udpsnd, ps->net.udpssz,
1265 | ps->net.udprcv, ps->net.udprsz);
1266 | output_samp(&defop, buf, buflen);
1267 | }
1268 |
1269 | output_samp(&defop, "]", 1);
1270 | }
1271 |
1272 | static void json_print_PRE(int flags, char *hp, struct sstat *ss, struct tstat *ps, int nact)
1273 | {
1274 | if (!(flags & GPUSTAT) )
1275 | return;
1276 |
1277 | int i;
1278 | int buflen = 0;
1279 | char buf[LINE_BUF_SIZE];
1280 |
1281 | char br[LEN_HP_SIZE];
1282 | buflen = sprintf(br, ", %s: [", hp);
1283 | output_samp(&defop, br, buflen);
1284 |
1285 | for (i = 0; i < nact; i++, ps++) {
1286 | if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc)
1287 | continue;
1288 | if (i > 0) {
1289 | output_samp(&defop, ", ", 2);
1290 | }
1291 | buflen = snprintf(buf, sizeof(buf), "{\"pid\": %d, "
1292 | "\"gpustate\": \"%c\", "
1293 | "\"nrgpus\": %d, "
1294 | "\"gpulist\": \"%x\", "
1295 | "\"gpubusy\": %d, "
1296 | "\"membusy\": %d, "
1297 | "\"memnow\": %lld, "
1298 | "\"memcum\": %lld, "
1299 | "\"sample\": %lld}",
1300 | ps->gen.pid,
1301 | ps->gpu.state == '\0' ? 'N':ps->gpu.state,
1302 | ps->gpu.nrgpus,
1303 | ps->gpu.gpulist,
1304 | ps->gpu.gpubusy,
1305 | ps->gpu.membusy,
1306 | ps->gpu.memnow,
1307 | ps->gpu.memcum,
1308 | ps->gpu.sample);
1309 | output_samp(&defop, buf, buflen);
1310 | }
1311 |
1312 | output_samp(&defop, "]", 1);
1313 | }
1314 |
--------------------------------------------------------------------------------