ipc.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "plugin_proc.h"
  3. #include <sys/sem.h>
  4. #include <sys/msg.h>
  5. #include <sys/shm.h>
  6. #ifndef SEMVMX
  7. #define SEMVMX 32767 /* <= 32767 semaphore maximum value */
  8. #endif
  9. /* Some versions of libc only define IPC_INFO when __USE_GNU is defined. */
  10. #ifndef IPC_INFO
  11. #define IPC_INFO 3
  12. #endif
  13. struct ipc_limits {
  14. uint64_t shmmni; /* max number of segments */
  15. uint64_t shmmax; /* max segment size */
  16. uint64_t shmall; /* max total shared memory */
  17. uint64_t shmmin; /* min segment size */
  18. int semmni; /* max number of arrays */
  19. int semmsl; /* max semaphores per array */
  20. int semmns; /* max semaphores system wide */
  21. int semopm; /* max ops per semop call */
  22. unsigned int semvmx; /* semaphore max value (constant) */
  23. int msgmni; /* max queues system wide */
  24. size_t msgmax; /* max size of message */
  25. int msgmnb; /* default max size of queue */
  26. };
  27. struct ipc_status {
  28. int semusz; /* current number of arrays */
  29. int semaem; /* current semaphores system wide */
  30. };
  31. /*
  32. * The last arg of semctl is a union semun, but where is it defined? X/OPEN
  33. * tells us to define it ourselves, but until recently Linux include files
  34. * would also define it.
  35. */
  36. #ifndef HAVE_UNION_SEMUN
  37. /* according to X/OPEN we have to define it ourselves */
  38. union semun {
  39. int val;
  40. struct semid_ds *buf;
  41. unsigned short int *array;
  42. struct seminfo *__buf;
  43. };
  44. #endif
  45. struct message_queue {
  46. unsigned long long id;
  47. int found;
  48. RRDDIM *rd_messages;
  49. RRDDIM *rd_bytes;
  50. unsigned long long messages;
  51. unsigned long long bytes;
  52. struct message_queue * next;
  53. };
  54. struct shm_stats {
  55. unsigned long long segments;
  56. unsigned long long bytes;
  57. };
  58. static inline int ipc_sem_get_limits(struct ipc_limits *lim) {
  59. static procfile *ff = NULL;
  60. static int error_shown = 0;
  61. static char filename[FILENAME_MAX + 1] = "";
  62. if(unlikely(!filename[0]))
  63. snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/sem", netdata_configured_host_prefix);
  64. if(unlikely(!ff)) {
  65. ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
  66. if(unlikely(!ff)) {
  67. if(unlikely(!error_shown)) {
  68. collector_error("IPC: Cannot open file '%s'.", filename);
  69. error_shown = 1;
  70. }
  71. goto ipc;
  72. }
  73. }
  74. ff = procfile_readall(ff);
  75. if(unlikely(!ff)) {
  76. if(unlikely(!error_shown)) {
  77. collector_error("IPC: Cannot read file '%s'.", filename);
  78. error_shown = 1;
  79. }
  80. goto ipc;
  81. }
  82. if(procfile_lines(ff) >= 1 && procfile_linewords(ff, 0) >= 4) {
  83. lim->semvmx = SEMVMX;
  84. lim->semmsl = str2i(procfile_lineword(ff, 0, 0));
  85. lim->semmns = str2i(procfile_lineword(ff, 0, 1));
  86. lim->semopm = str2i(procfile_lineword(ff, 0, 2));
  87. lim->semmni = str2i(procfile_lineword(ff, 0, 3));
  88. return 0;
  89. }
  90. else {
  91. if(unlikely(!error_shown)) {
  92. collector_error("IPC: Invalid content in file '%s'.", filename);
  93. error_shown = 1;
  94. }
  95. goto ipc;
  96. }
  97. ipc:
  98. // cannot do it from the file
  99. // query IPC
  100. {
  101. struct seminfo seminfo = {.semmni = 0};
  102. union semun arg = {.array = (ushort *) &seminfo};
  103. if(unlikely(semctl(0, 0, IPC_INFO, arg) < 0)) {
  104. collector_error("IPC: Failed to read '%s' and request IPC_INFO with semctl().", filename);
  105. goto error;
  106. }
  107. lim->semvmx = SEMVMX;
  108. lim->semmni = seminfo.semmni;
  109. lim->semmsl = seminfo.semmsl;
  110. lim->semmns = seminfo.semmns;
  111. lim->semopm = seminfo.semopm;
  112. return 0;
  113. }
  114. error:
  115. lim->semvmx = 0;
  116. lim->semmni = 0;
  117. lim->semmsl = 0;
  118. lim->semmns = 0;
  119. lim->semopm = 0;
  120. return -1;
  121. }
  122. /*
  123. printf ("------ Semaphore Limits --------\n");
  124. printf ("max number of arrays = %d\n", limits.semmni);
  125. printf ("max semaphores per array = %d\n", limits.semmsl);
  126. printf ("max semaphores system wide = %d\n", limits.semmns);
  127. printf ("max ops per semop call = %d\n", limits.semopm);
  128. printf ("semaphore max value = %u\n", limits.semvmx);
  129. printf ("------ Semaphore Status --------\n");
  130. printf ("used arrays = %d\n", status.semusz);
  131. printf ("allocated semaphores = %d\n", status.semaem);
  132. */
  133. static inline int ipc_sem_get_status(struct ipc_status *st) {
  134. struct seminfo seminfo;
  135. union semun arg;
  136. arg.array = (ushort *) (void *) &seminfo;
  137. if(unlikely(semctl (0, 0, SEM_INFO, arg) < 0)) {
  138. /* kernel not configured for semaphores */
  139. static int error_shown = 0;
  140. if(unlikely(!error_shown)) {
  141. collector_error("IPC: kernel is not configured for semaphores");
  142. error_shown = 1;
  143. }
  144. st->semusz = 0;
  145. st->semaem = 0;
  146. return -1;
  147. }
  148. st->semusz = seminfo.semusz;
  149. st->semaem = seminfo.semaem;
  150. return 0;
  151. }
  152. int ipc_msq_get_info(char *msg_filename, struct message_queue **message_queue_root) {
  153. static procfile *ff;
  154. struct message_queue *msq;
  155. if(unlikely(!ff)) {
  156. ff = procfile_open(msg_filename, " \t:", PROCFILE_FLAG_DEFAULT);
  157. if(unlikely(!ff)) return 1;
  158. }
  159. ff = procfile_readall(ff);
  160. if(unlikely(!ff)) return 1;
  161. size_t lines = procfile_lines(ff);
  162. size_t words = 0;
  163. if(unlikely(lines < 2)) {
  164. collector_error("Cannot read %s. Expected 2 or more lines, read %zu.", procfile_filename(ff), lines);
  165. return 1;
  166. }
  167. // loop through all lines except the first and the last ones
  168. size_t l;
  169. for(l = 1; l < lines - 1; l++) {
  170. words = procfile_linewords(ff, l);
  171. if(unlikely(words < 2)) continue;
  172. if(unlikely(words < 14)) {
  173. collector_error("Cannot read %s line. Expected 14 params, read %zu.", procfile_filename(ff), words);
  174. continue;
  175. }
  176. // find the id in the linked list or create a new structure
  177. int found = 0;
  178. unsigned long long id = str2ull(procfile_lineword(ff, l, 1), NULL);
  179. for(msq = *message_queue_root; msq ; msq = msq->next) {
  180. if(unlikely(id == msq->id)) {
  181. found = 1;
  182. break;
  183. }
  184. }
  185. if(unlikely(!found)) {
  186. msq = callocz(1, sizeof(struct message_queue));
  187. msq->next = *message_queue_root;
  188. *message_queue_root = msq;
  189. msq->id = id;
  190. }
  191. msq->messages = str2ull(procfile_lineword(ff, l, 4), NULL);
  192. msq->bytes = str2ull(procfile_lineword(ff, l, 3), NULL);
  193. msq->found = 1;
  194. }
  195. return 0;
  196. }
  197. int ipc_shm_get_info(char *shm_filename, struct shm_stats *shm) {
  198. static procfile *ff;
  199. if(unlikely(!ff)) {
  200. ff = procfile_open(shm_filename, " \t:", PROCFILE_FLAG_DEFAULT);
  201. if(unlikely(!ff)) return 1;
  202. }
  203. ff = procfile_readall(ff);
  204. if(unlikely(!ff)) return 1;
  205. size_t lines = procfile_lines(ff);
  206. size_t words = 0;
  207. if(unlikely(lines < 2)) {
  208. collector_error("Cannot read %s. Expected 2 or more lines, read %zu.", procfile_filename(ff), lines);
  209. return 1;
  210. }
  211. shm->segments = 0;
  212. shm->bytes = 0;
  213. // loop through all lines except the first and the last ones
  214. size_t l;
  215. for(l = 1; l < lines - 1; l++) {
  216. words = procfile_linewords(ff, l);
  217. if(unlikely(words < 2)) continue;
  218. if(unlikely(words < 16)) {
  219. collector_error("Cannot read %s line. Expected 16 params, read %zu.", procfile_filename(ff), words);
  220. continue;
  221. }
  222. shm->segments++;
  223. shm->bytes += str2ull(procfile_lineword(ff, l, 3), NULL);
  224. }
  225. return 0;
  226. }
  227. int do_ipc(int update_every, usec_t dt) {
  228. (void)dt;
  229. static int do_sem = -1, do_msg = -1, do_shm = -1;
  230. static int read_limits_next = -1;
  231. static struct ipc_limits limits;
  232. static struct ipc_status status;
  233. static const RRDVAR_ACQUIRED *arrays_max = NULL, *semaphores_max = NULL;
  234. static RRDSET *st_semaphores = NULL, *st_arrays = NULL;
  235. static RRDDIM *rd_semaphores = NULL, *rd_arrays = NULL;
  236. static char *msg_filename = NULL;
  237. static struct message_queue *message_queue_root = NULL;
  238. static long long dimensions_limit;
  239. static char *shm_filename = NULL;
  240. if(unlikely(do_sem == -1)) {
  241. do_msg = config_get_boolean("plugin:proc:ipc", "message queues", CONFIG_BOOLEAN_YES);
  242. do_sem = config_get_boolean("plugin:proc:ipc", "semaphore totals", CONFIG_BOOLEAN_YES);
  243. do_shm = config_get_boolean("plugin:proc:ipc", "shared memory totals", CONFIG_BOOLEAN_YES);
  244. char filename[FILENAME_MAX + 1];
  245. snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/sysvipc/msg");
  246. msg_filename = config_get("plugin:proc:ipc", "msg filename to monitor", filename);
  247. snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/sysvipc/shm");
  248. shm_filename = config_get("plugin:proc:ipc", "shm filename to monitor", filename);
  249. dimensions_limit = config_get_number("plugin:proc:ipc", "max dimensions in memory allowed", 50);
  250. // make sure it works
  251. if(ipc_sem_get_limits(&limits) == -1) {
  252. collector_error("unable to fetch semaphore limits");
  253. do_sem = CONFIG_BOOLEAN_NO;
  254. }
  255. else if(ipc_sem_get_status(&status) == -1) {
  256. collector_error("unable to fetch semaphore statistics");
  257. do_sem = CONFIG_BOOLEAN_NO;
  258. }
  259. else {
  260. // create the charts
  261. if(unlikely(!st_semaphores)) {
  262. st_semaphores = rrdset_create_localhost(
  263. "system"
  264. , "ipc_semaphores"
  265. , NULL
  266. , "ipc semaphores"
  267. , NULL
  268. , "IPC Semaphores"
  269. , "semaphores"
  270. , PLUGIN_PROC_NAME
  271. , "ipc"
  272. , NETDATA_CHART_PRIO_SYSTEM_IPC_SEMAPHORES
  273. , localhost->rrd_update_every
  274. , RRDSET_TYPE_AREA
  275. );
  276. rd_semaphores = rrddim_add(st_semaphores, "semaphores", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  277. }
  278. if(unlikely(!st_arrays)) {
  279. st_arrays = rrdset_create_localhost(
  280. "system"
  281. , "ipc_semaphore_arrays"
  282. , NULL
  283. , "ipc semaphores"
  284. , NULL
  285. , "IPC Semaphore Arrays"
  286. , "arrays"
  287. , PLUGIN_PROC_NAME
  288. , "ipc"
  289. , NETDATA_CHART_PRIO_SYSTEM_IPC_SEM_ARRAYS
  290. , localhost->rrd_update_every
  291. , RRDSET_TYPE_AREA
  292. );
  293. rd_arrays = rrddim_add(st_arrays, "arrays", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  294. }
  295. // variables
  296. semaphores_max = rrdvar_custom_host_variable_add_and_acquire(localhost, "ipc_semaphores_max");
  297. arrays_max = rrdvar_custom_host_variable_add_and_acquire(localhost, "ipc_semaphores_arrays_max");
  298. }
  299. struct stat stbuf;
  300. if (stat(msg_filename, &stbuf)) {
  301. do_msg = CONFIG_BOOLEAN_NO;
  302. }
  303. if(unlikely(do_sem == CONFIG_BOOLEAN_NO && do_msg == CONFIG_BOOLEAN_NO)) {
  304. collector_error("ipc module disabled");
  305. return 1;
  306. }
  307. }
  308. if(likely(do_sem != CONFIG_BOOLEAN_NO)) {
  309. if(unlikely(read_limits_next < 0)) {
  310. if(unlikely(ipc_sem_get_limits(&limits) == -1)) {
  311. collector_error("Unable to fetch semaphore limits.");
  312. }
  313. else {
  314. if(semaphores_max) rrdvar_custom_host_variable_set(localhost, semaphores_max, limits.semmns);
  315. if(arrays_max) rrdvar_custom_host_variable_set(localhost, arrays_max, limits.semmni);
  316. st_arrays->red = limits.semmni;
  317. st_semaphores->red = limits.semmns;
  318. read_limits_next = 60 / update_every;
  319. }
  320. }
  321. else
  322. read_limits_next--;
  323. if(unlikely(ipc_sem_get_status(&status) == -1)) {
  324. collector_error("Unable to get semaphore statistics");
  325. return 0;
  326. }
  327. rrddim_set_by_pointer(st_semaphores, rd_semaphores, status.semaem);
  328. rrdset_done(st_semaphores);
  329. rrddim_set_by_pointer(st_arrays, rd_arrays, status.semusz);
  330. rrdset_done(st_arrays);
  331. }
  332. if(likely(do_msg != CONFIG_BOOLEAN_NO)) {
  333. static RRDSET *st_msq_messages = NULL, *st_msq_bytes = NULL;
  334. int ret = ipc_msq_get_info(msg_filename, &message_queue_root);
  335. if(!ret && message_queue_root) {
  336. if(unlikely(!st_msq_messages))
  337. st_msq_messages = rrdset_create_localhost(
  338. "system"
  339. , "message_queue_messages"
  340. , NULL
  341. , "ipc message queues"
  342. , NULL
  343. , "IPC Message Queue Number of Messages"
  344. , "messages"
  345. , PLUGIN_PROC_NAME
  346. , "ipc"
  347. , NETDATA_CHART_PRIO_SYSTEM_IPC_MSQ_MESSAGES
  348. , update_every
  349. , RRDSET_TYPE_STACKED
  350. );
  351. if(unlikely(!st_msq_bytes))
  352. st_msq_bytes = rrdset_create_localhost(
  353. "system"
  354. , "message_queue_bytes"
  355. , NULL
  356. , "ipc message queues"
  357. , NULL
  358. , "IPC Message Queue Used Bytes"
  359. , "bytes"
  360. , PLUGIN_PROC_NAME
  361. , "ipc"
  362. , NETDATA_CHART_PRIO_SYSTEM_IPC_MSQ_SIZE
  363. , update_every
  364. , RRDSET_TYPE_STACKED
  365. );
  366. struct message_queue *msq = message_queue_root, *msq_prev = NULL;
  367. while(likely(msq)){
  368. if(likely(msq->found)) {
  369. if(unlikely(!msq->rd_messages || !msq->rd_bytes)) {
  370. char id[RRD_ID_LENGTH_MAX + 1];
  371. snprintfz(id, RRD_ID_LENGTH_MAX, "%llu", msq->id);
  372. if(likely(!msq->rd_messages)) msq->rd_messages = rrddim_add(st_msq_messages, id, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  373. if(likely(!msq->rd_bytes)) msq->rd_bytes = rrddim_add(st_msq_bytes, id, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  374. }
  375. rrddim_set_by_pointer(st_msq_messages, msq->rd_messages, msq->messages);
  376. rrddim_set_by_pointer(st_msq_bytes, msq->rd_bytes, msq->bytes);
  377. msq->found = 0;
  378. }
  379. else {
  380. rrddim_is_obsolete___safe_from_collector_thread(st_msq_messages, msq->rd_messages);
  381. rrddim_is_obsolete___safe_from_collector_thread(st_msq_bytes, msq->rd_bytes);
  382. // remove message queue from the linked list
  383. if(!msq_prev)
  384. message_queue_root = msq->next;
  385. else
  386. msq_prev->next = msq->next;
  387. freez(msq);
  388. msq = NULL;
  389. }
  390. if(likely(msq)) {
  391. msq_prev = msq;
  392. msq = msq->next;
  393. }
  394. else if(!msq_prev)
  395. msq = message_queue_root;
  396. else
  397. msq = msq_prev->next;
  398. }
  399. rrdset_done(st_msq_messages);
  400. rrdset_done(st_msq_bytes);
  401. long long dimensions_num = rrdset_number_of_dimensions(st_msq_messages);
  402. if(unlikely(dimensions_num > dimensions_limit)) {
  403. collector_info("Message queue statistics has been disabled");
  404. collector_info("There are %lld dimensions in memory but limit was set to %lld", dimensions_num, dimensions_limit);
  405. rrdset_is_obsolete___safe_from_collector_thread(st_msq_messages);
  406. rrdset_is_obsolete___safe_from_collector_thread(st_msq_bytes);
  407. st_msq_messages = NULL;
  408. st_msq_bytes = NULL;
  409. do_msg = CONFIG_BOOLEAN_NO;
  410. }
  411. else if(unlikely(!message_queue_root)) {
  412. collector_info("Making chart %s (%s) obsolete since it does not have any dimensions", rrdset_name(st_msq_messages), rrdset_id(st_msq_messages));
  413. rrdset_is_obsolete___safe_from_collector_thread(st_msq_messages);
  414. st_msq_messages = NULL;
  415. collector_info("Making chart %s (%s) obsolete since it does not have any dimensions", rrdset_name(st_msq_bytes), rrdset_id(st_msq_bytes));
  416. rrdset_is_obsolete___safe_from_collector_thread(st_msq_bytes);
  417. st_msq_bytes = NULL;
  418. }
  419. }
  420. }
  421. if(likely(do_shm != CONFIG_BOOLEAN_NO)) {
  422. static RRDSET *st_shm_segments = NULL, *st_shm_bytes = NULL;
  423. static RRDDIM *rd_shm_segments = NULL, *rd_shm_bytes = NULL;
  424. struct shm_stats shm;
  425. if(!ipc_shm_get_info(shm_filename, &shm)) {
  426. if(unlikely(!st_shm_segments)) {
  427. st_shm_segments = rrdset_create_localhost(
  428. "system"
  429. , "shared_memory_segments"
  430. , NULL
  431. , "ipc shared memory"
  432. , NULL
  433. , "IPC Shared Memory Number of Segments"
  434. , "segments"
  435. , PLUGIN_PROC_NAME
  436. , "ipc"
  437. , NETDATA_CHART_PRIO_SYSTEM_IPC_SHARED_MEM_SEGS
  438. , update_every
  439. , RRDSET_TYPE_STACKED
  440. );
  441. rd_shm_segments = rrddim_add(st_shm_segments, "segments", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  442. }
  443. rrddim_set_by_pointer(st_shm_segments, rd_shm_segments, shm.segments);
  444. rrdset_done(st_shm_segments);
  445. if(unlikely(!st_shm_bytes)) {
  446. st_shm_bytes = rrdset_create_localhost(
  447. "system"
  448. , "shared_memory_bytes"
  449. , NULL
  450. , "ipc shared memory"
  451. , NULL
  452. , "IPC Shared Memory Used Bytes"
  453. , "bytes"
  454. , PLUGIN_PROC_NAME
  455. , "ipc"
  456. , NETDATA_CHART_PRIO_SYSTEM_IPC_SHARED_MEM_SIZE
  457. , update_every
  458. , RRDSET_TYPE_STACKED
  459. );
  460. rd_shm_bytes = rrddim_add(st_shm_bytes, "bytes", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  461. }
  462. rrddim_set_by_pointer(st_shm_bytes, rd_shm_bytes, shm.bytes);
  463. rrdset_done(st_shm_bytes);
  464. }
  465. }
  466. return 0;
  467. }