LibNl.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /*
  2. htop - linux/LibNl.c
  3. (C) 2024 htop dev team
  4. Released under the GNU GPLv2+, see the COPYING file
  5. in the source distribution for its full text.
  6. */
  7. #include "config.h" // IWYU pragma: keep
  8. #ifndef HAVE_DELAYACCT
  9. #error Compiling this file requires HAVE_DELAYACCT
  10. #endif
  11. #include "linux/LibNl.h"
  12. #include <dlfcn.h>
  13. #include <linux/netlink.h>
  14. #include <linux/taskstats.h>
  15. #include <netlink/attr.h>
  16. #include <netlink/handlers.h>
  17. #include <netlink/msg.h>
  18. static void* libnlHandle;
  19. static void* libnlGenlHandle;
  20. static void (*sym_nl_close)(struct nl_sock*);
  21. static int (*sym_nl_connect)(struct nl_sock*, int);
  22. static int (*sym_nl_recvmsgs_default)(struct nl_sock*);
  23. static int (*sym_nl_send_sync)(struct nl_sock*, struct nl_msg*);
  24. static struct nl_sock* (*sym_nl_socket_alloc)(void);
  25. static void (*sym_nl_socket_free)(struct nl_sock*);
  26. static int (*sym_nl_socket_modify_cb)(struct nl_sock*, enum nl_cb_type, enum nl_cb_kind, nl_recvmsg_msg_cb_t, void*);
  27. static void* (*sym_nla_data)(const struct nlattr*);
  28. static struct nlattr* (*sym_nla_next)(const struct nlattr*, int*);
  29. static int (*sym_nla_put_u32)(struct nl_msg*, int, uint32_t);
  30. static struct nl_msg* (*sym_nlmsg_alloc)(void);
  31. static struct nlmsghdr* (*sym_nlmsg_hdr)(struct nl_msg*);
  32. static void (*sym_nlmsg_free)(struct nl_msg*);
  33. static int (*sym_genl_ctrl_resolve)(struct nl_sock*, const char*);
  34. static int (*sym_genlmsg_parse)(struct nlmsghdr*, int, struct nlattr**, int, const struct nla_policy*);
  35. static void* (*sym_genlmsg_put)(struct nl_msg*, uint32_t, uint32_t, int, int, int, uint8_t, uint8_t);
  36. static void unload_libnl(void) {
  37. sym_nl_close = NULL;
  38. sym_nl_connect = NULL;
  39. sym_nl_recvmsgs_default = NULL;
  40. sym_nl_send_sync = NULL;
  41. sym_nl_socket_alloc = NULL;
  42. sym_nl_socket_free = NULL;
  43. sym_nl_socket_modify_cb = NULL;
  44. sym_nla_data = NULL;
  45. sym_nla_next = NULL;
  46. sym_nla_put_u32 = NULL;
  47. sym_nlmsg_alloc = NULL;
  48. sym_nlmsg_free = NULL;
  49. sym_nlmsg_hdr = NULL;
  50. sym_genl_ctrl_resolve = NULL;
  51. sym_genlmsg_parse = NULL;
  52. sym_genlmsg_put = NULL;
  53. if (libnlGenlHandle) {
  54. dlclose(libnlGenlHandle);
  55. libnlGenlHandle = NULL;
  56. }
  57. if (libnlHandle) {
  58. dlclose(libnlHandle);
  59. libnlHandle = NULL;
  60. }
  61. }
  62. static int load_libnl(void) {
  63. if (libnlHandle && libnlGenlHandle)
  64. return 0;
  65. libnlHandle = dlopen("libnl-3.so", RTLD_LAZY);
  66. if (!libnlHandle) {
  67. libnlHandle = dlopen("libnl-3.so.200", RTLD_LAZY);
  68. if (!libnlHandle) {
  69. goto dlfailure;
  70. }
  71. }
  72. libnlGenlHandle = dlopen("libnl-genl-3.so", RTLD_LAZY);
  73. if (!libnlGenlHandle) {
  74. libnlGenlHandle = dlopen("libnl-genl-3.so.200", RTLD_LAZY);
  75. if (!libnlGenlHandle) {
  76. goto dlfailure;
  77. }
  78. }
  79. /* Clear any errors */
  80. dlerror();
  81. #define resolve(handle, symbolname) do { \
  82. *(void **)(&sym_##symbolname) = dlsym(handle, #symbolname); \
  83. if (!sym_##symbolname || dlerror() != NULL) { \
  84. goto dlfailure; \
  85. } \
  86. } while(0)
  87. resolve(libnlHandle, nl_close);
  88. resolve(libnlHandle, nl_connect);
  89. resolve(libnlHandle, nl_recvmsgs_default);
  90. resolve(libnlHandle, nl_send_sync);
  91. resolve(libnlHandle, nl_socket_alloc);
  92. resolve(libnlHandle, nl_socket_free);
  93. resolve(libnlHandle, nl_socket_modify_cb);
  94. resolve(libnlHandle, nla_data);
  95. resolve(libnlHandle, nla_next);
  96. resolve(libnlHandle, nla_put_u32);
  97. resolve(libnlHandle, nlmsg_alloc);
  98. resolve(libnlHandle, nlmsg_free);
  99. resolve(libnlHandle, nlmsg_hdr);
  100. resolve(libnlGenlHandle, genl_ctrl_resolve);
  101. resolve(libnlGenlHandle, genlmsg_parse);
  102. resolve(libnlGenlHandle, genlmsg_put);
  103. #undef resolve
  104. return 0;
  105. dlfailure:
  106. unload_libnl();
  107. return -1;
  108. }
  109. static void initNetlinkSocket(LinuxProcessTable* this) {
  110. if (load_libnl() < 0) {
  111. return;
  112. }
  113. this->netlink_socket = sym_nl_socket_alloc();
  114. if (this->netlink_socket == NULL) {
  115. return;
  116. }
  117. if (sym_nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) {
  118. return;
  119. }
  120. this->netlink_family = sym_genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME);
  121. }
  122. void LibNl_destroyNetlinkSocket(LinuxProcessTable* this) {
  123. if (this->netlink_socket) {
  124. assert(libnlHandle);
  125. sym_nl_close(this->netlink_socket);
  126. sym_nl_socket_free(this->netlink_socket);
  127. this->netlink_socket = NULL;
  128. }
  129. unload_libnl();
  130. }
  131. static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) {
  132. struct nlmsghdr* nlhdr;
  133. struct nlattr* nlattrs[TASKSTATS_TYPE_MAX + 1];
  134. const struct nlattr* nlattr;
  135. struct taskstats stats;
  136. int rem;
  137. LinuxProcess* lp = (LinuxProcess*) linuxProcess;
  138. nlhdr = sym_nlmsg_hdr(nlmsg);
  139. if (sym_genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) {
  140. return NL_SKIP;
  141. }
  142. if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) {
  143. memcpy(&stats, sym_nla_data(sym_nla_next(sym_nla_data(nlattr), &rem)), sizeof(stats));
  144. assert(Process_getPid(&lp->super) == (pid_t)stats.ac_pid);
  145. // The xxx_delay_total values wrap around on overflow.
  146. // (Linux Kernel "Documentation/accounting/taskstats-struct.rst")
  147. unsigned long long int timeDelta = stats.ac_etime * 1000 - lp->delay_read_time;
  148. #define DELTAPERC(x, y) (timeDelta ? MINIMUM((float)((x) - (y)) / timeDelta * 100.0F, 100.0F) : NAN)
  149. lp->cpu_delay_percent = DELTAPERC(stats.cpu_delay_total, lp->cpu_delay_total);
  150. lp->blkio_delay_percent = DELTAPERC(stats.blkio_delay_total, lp->blkio_delay_total);
  151. lp->swapin_delay_percent = DELTAPERC(stats.swapin_delay_total, lp->swapin_delay_total);
  152. #undef DELTAPERC
  153. lp->swapin_delay_total = stats.swapin_delay_total;
  154. lp->blkio_delay_total = stats.blkio_delay_total;
  155. lp->cpu_delay_total = stats.cpu_delay_total;
  156. lp->delay_read_time = stats.ac_etime * 1000;
  157. }
  158. return NL_OK;
  159. }
  160. /*
  161. * Gather delay-accounting information (thread-specific data)
  162. */
  163. void LibNl_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process) {
  164. struct nl_msg* msg;
  165. if (!this->netlink_socket) {
  166. initNetlinkSocket(this);
  167. if (!this->netlink_socket) {
  168. goto delayacct_failure;
  169. }
  170. }
  171. if (sym_nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
  172. goto delayacct_failure;
  173. }
  174. if (! (msg = sym_nlmsg_alloc())) {
  175. goto delayacct_failure;
  176. }
  177. if (! sym_genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) {
  178. sym_nlmsg_free(msg);
  179. }
  180. if (sym_nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, Process_getPid(&process->super)) < 0) {
  181. sym_nlmsg_free(msg);
  182. }
  183. if (sym_nl_send_sync(this->netlink_socket, msg) < 0) {
  184. goto delayacct_failure;
  185. }
  186. if (sym_nl_recvmsgs_default(this->netlink_socket) < 0) {
  187. goto delayacct_failure;
  188. }
  189. return;
  190. delayacct_failure:
  191. process->swapin_delay_percent = NAN;
  192. process->blkio_delay_percent = NAN;
  193. process->cpu_delay_percent = NAN;
  194. }