ipc.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  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. 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. 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. 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. 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. 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. error("Cannot read %s. Expected 2 or more lines, read %zu.", ff->filename, 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. error("Cannot read %s line. Expected 14 params, read %zu.", ff->filename, 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));
  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));
  192. msq->bytes = str2ull(procfile_lineword(ff, l, 3));
  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. error("Cannot read %s. Expected 2 or more lines, read %zu.", ff->filename, 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. error("Cannot read %s line. Expected 16 params, read %zu.", ff->filename, words);
  220. continue;
  221. }
  222. shm->segments++;
  223. shm->bytes += str2ull(procfile_lineword(ff, l, 3));
  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 RRDVAR *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. error("unable to fetch semaphore limits");
  253. do_sem = CONFIG_BOOLEAN_NO;
  254. }
  255. else if(ipc_sem_get_status(&status) == -1) {
  256. 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_create(localhost, "ipc_semaphores_max");
  297. arrays_max = rrdvar_custom_host_variable_create(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. 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. 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. error("Unable to get semaphore statistics");
  325. return 0;
  326. }
  327. if(st_semaphores->counter_done) rrdset_next(st_semaphores);
  328. rrddim_set_by_pointer(st_semaphores, rd_semaphores, status.semaem);
  329. rrdset_done(st_semaphores);
  330. if(st_arrays->counter_done) rrdset_next(st_arrays);
  331. rrddim_set_by_pointer(st_arrays, rd_arrays, status.semusz);
  332. rrdset_done(st_arrays);
  333. }
  334. // --------------------------------------------------------------------
  335. if(likely(do_msg != CONFIG_BOOLEAN_NO)) {
  336. static RRDSET *st_msq_messages = NULL, *st_msq_bytes = NULL;
  337. int ret = ipc_msq_get_info(msg_filename, &message_queue_root);
  338. if(!ret && message_queue_root) {
  339. if(unlikely(!st_msq_messages))
  340. st_msq_messages = rrdset_create_localhost(
  341. "system"
  342. , "message_queue_messages"
  343. , NULL
  344. , "ipc message queues"
  345. , NULL
  346. , "IPC Message Queue Number of Messages"
  347. , "messages"
  348. , PLUGIN_PROC_NAME
  349. , "ipc"
  350. , NETDATA_CHART_PRIO_SYSTEM_IPC_MSQ_MESSAGES
  351. , update_every
  352. , RRDSET_TYPE_STACKED
  353. );
  354. else
  355. rrdset_next(st_msq_messages);
  356. if(unlikely(!st_msq_bytes))
  357. st_msq_bytes = rrdset_create_localhost(
  358. "system"
  359. , "message_queue_bytes"
  360. , NULL
  361. , "ipc message queues"
  362. , NULL
  363. , "IPC Message Queue Used Bytes"
  364. , "bytes"
  365. , PLUGIN_PROC_NAME
  366. , "ipc"
  367. , NETDATA_CHART_PRIO_SYSTEM_IPC_MSQ_SIZE
  368. , update_every
  369. , RRDSET_TYPE_STACKED
  370. );
  371. else
  372. rrdset_next(st_msq_bytes);
  373. struct message_queue *msq = message_queue_root, *msq_prev = NULL;
  374. while(likely(msq)){
  375. if(likely(msq->found)) {
  376. if(unlikely(!msq->rd_messages || !msq->rd_bytes)) {
  377. char id[RRD_ID_LENGTH_MAX + 1];
  378. snprintfz(id, RRD_ID_LENGTH_MAX, "%llu", msq->id);
  379. if(likely(!msq->rd_messages)) msq->rd_messages = rrddim_add(st_msq_messages, id, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  380. if(likely(!msq->rd_bytes)) msq->rd_bytes = rrddim_add(st_msq_bytes, id, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  381. }
  382. rrddim_set_by_pointer(st_msq_messages, msq->rd_messages, msq->messages);
  383. rrddim_set_by_pointer(st_msq_bytes, msq->rd_bytes, msq->bytes);
  384. msq->found = 0;
  385. }
  386. else {
  387. rrddim_is_obsolete(st_msq_messages, msq->rd_messages);
  388. rrddim_is_obsolete(st_msq_bytes, msq->rd_bytes);
  389. // remove message queue from the linked list
  390. if(!msq_prev)
  391. message_queue_root = msq->next;
  392. else
  393. msq_prev->next = msq->next;
  394. freez(msq);
  395. msq = NULL;
  396. }
  397. if(likely(msq)) {
  398. msq_prev = msq;
  399. msq = msq->next;
  400. }
  401. else if(!msq_prev)
  402. msq = message_queue_root;
  403. else
  404. msq = msq_prev->next;
  405. }
  406. rrdset_done(st_msq_messages);
  407. rrdset_done(st_msq_bytes);
  408. long long dimensions_num = 0;
  409. RRDDIM *rd;
  410. rrdset_rdlock(st_msq_messages);
  411. rrddim_foreach_read(rd, st_msq_messages) dimensions_num++;
  412. rrdset_unlock(st_msq_messages);
  413. if(unlikely(dimensions_num > dimensions_limit)) {
  414. info("Message queue statistics has been disabled");
  415. info("There are %lld dimensions in memory but limit was set to %lld", dimensions_num, dimensions_limit);
  416. rrdset_is_obsolete(st_msq_messages);
  417. rrdset_is_obsolete(st_msq_bytes);
  418. st_msq_messages = NULL;
  419. st_msq_bytes = NULL;
  420. do_msg = CONFIG_BOOLEAN_NO;
  421. }
  422. else if(unlikely(!message_queue_root)) {
  423. info("Making chart %s (%s) obsolete since it does not have any dimensions", st_msq_messages->name, st_msq_messages->id);
  424. rrdset_is_obsolete(st_msq_messages);
  425. st_msq_messages = NULL;
  426. info("Making chart %s (%s) obsolete since it does not have any dimensions", st_msq_bytes->name, st_msq_bytes->id);
  427. rrdset_is_obsolete(st_msq_bytes);
  428. st_msq_bytes = NULL;
  429. }
  430. }
  431. }
  432. // --------------------------------------------------------------------
  433. if(likely(do_shm != CONFIG_BOOLEAN_NO)) {
  434. static RRDSET *st_shm_segments = NULL, *st_shm_bytes = NULL;
  435. static RRDDIM *rd_shm_segments = NULL, *rd_shm_bytes = NULL;
  436. struct shm_stats shm;
  437. if(!ipc_shm_get_info(shm_filename, &shm)) {
  438. if(unlikely(!st_shm_segments)) {
  439. st_shm_segments = rrdset_create_localhost(
  440. "system"
  441. , "shared_memory_segments"
  442. , NULL
  443. , "ipc shared memory"
  444. , NULL
  445. , "IPC Shared Memory Number of Segments"
  446. , "segments"
  447. , PLUGIN_PROC_NAME
  448. , "ipc"
  449. , NETDATA_CHART_PRIO_SYSTEM_IPC_SHARED_MEM_SEGS
  450. , update_every
  451. , RRDSET_TYPE_STACKED
  452. );
  453. rd_shm_segments = rrddim_add(st_shm_segments, "segments", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  454. }
  455. else
  456. rrdset_next(st_shm_segments);
  457. rrddim_set_by_pointer(st_shm_segments, rd_shm_segments, shm.segments);
  458. rrdset_done(st_shm_segments);
  459. // --------------------------------------------------------------------
  460. if(unlikely(!st_shm_bytes)) {
  461. st_shm_bytes = rrdset_create_localhost(
  462. "system"
  463. , "shared_memory_bytes"
  464. , NULL
  465. , "ipc shared memory"
  466. , NULL
  467. , "IPC Shared Memory Used Bytes"
  468. , "bytes"
  469. , PLUGIN_PROC_NAME
  470. , "ipc"
  471. , NETDATA_CHART_PRIO_SYSTEM_IPC_SHARED_MEM_SIZE
  472. , update_every
  473. , RRDSET_TYPE_STACKED
  474. );
  475. rd_shm_bytes = rrddim_add(st_shm_bytes, "bytes", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  476. }
  477. else
  478. rrdset_next(st_shm_bytes);
  479. rrddim_set_by_pointer(st_shm_bytes, rd_shm_bytes, shm.bytes);
  480. rrdset_done(st_shm_bytes);
  481. }
  482. }
  483. return 0;
  484. }