intel_rdt: refactored proc utils to increase efficiency
[collectd.git] / src / utils_proc_pids_test.c
1 #include "testing.h"
2 #include "utils_proc_pids.c" /* sic */
3 #include <sys/stat.h>
4
5 /***************************************************************************
6  * helper functions
7  */
8
9 typedef struct stub_proc_pid {
10   proc_comm_t comm;
11   pid_t pid;
12 } stub_proc_pid_t;
13
14 static const char *proc_fs = "/tmp/procfs_stub";
15
16 /*
17  * NAME
18  *   stub_procfs_setup
19  *
20  * DESCRIPTION
21  *   Prepares testing environment by creating temporary
22  *   PID/comm file structure.
23  *
24  * PARAMETERS
25  *   `proc_pids_array'          Array of stub_proc_pid_t structs. Represents
26  *                              which PIDs should hold given process name.
27  *   `proc_pids_array_length'   Element count of input array.
28  *
29  * RETURN VALUE
30  *   0 on success.
31  *   -1 on base dir creation error.
32  *   -2 on comm file creation error.
33  *   -3 on comm file write error.
34  */
35 int stub_procfs_setup(const stub_proc_pid_t *proc_pids_array,
36                       const size_t proc_pids_array_length) {
37   if (mkdir(proc_fs, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
38     return -1;
39   char path[256];
40
41   for (size_t i = 0; i < proc_pids_array_length; ++i) {
42     memset(path, 0, sizeof(path));
43     snprintf(path, STATIC_ARRAY_SIZE(path), "%s/%d", proc_fs,
44              proc_pids_array[i].pid);
45     mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
46     strncat(path, "/comm", STATIC_ARRAY_SIZE(path) - strlen(path) - 1);
47
48     FILE *fp = fopen(path, "w");
49     if (!fp)
50       return -2;
51
52     size_t slen = strlen(proc_pids_array[i].comm);
53     size_t wlen = fwrite(proc_pids_array[i].comm, sizeof(char), slen, fp);
54     fclose(fp);
55
56     if (slen != wlen)
57       return -3;
58   }
59   return 0;
60 }
61
62 /*
63  * NAME
64  *   stub_procfs_teardown
65  *
66  * DESCRIPTION
67  *   Clears testing environment: removes stub proc files.
68  *   NOTE - This function could be implemented by usage of nftw, but this
69  *   would require #define _XOPEN_SOURCE 500, which
70  *   messes up intel_rdt includes.
71  *
72  * RETURN VALUE
73  *   system command result
74  */
75 int stub_procfs_teardown() {
76   char cmd[256];
77   sstrncpy(cmd, "rm -rf ", STATIC_ARRAY_SIZE(cmd));
78   strncat(cmd, proc_fs, STATIC_ARRAY_SIZE(cmd) - strlen(cmd) - 1);
79   return system(cmd);
80 }
81
82 /* Max PID value. More info:
83  * http://web.archive.org/web/20111209081734/http://research.cs.wisc.edu/condor/condorg/linux_scalability.html
84  */
85 #define MAX_PID 4194304
86 #define MAX_PID_STR "4194304"
87
88 /***************************************************************************
89  * tests
90  */
91 DEF_TEST(initialize_proc_pids__on_nullptr) {
92   /* setup */
93   const char *procs_names_array[] = {"proc1", "proc2", "proc3"};
94   const size_t procs_names_array_size = STATIC_ARRAY_SIZE(procs_names_array);
95   proc_pids_t **proc_pids_array = NULL;
96
97   /* check */
98   int result = initialize_proc_pids(procs_names_array, procs_names_array_size,
99                                     &proc_pids_array);
100   EXPECT_EQ_INT(0, result);
101   for (size_t i = 0; i < procs_names_array_size; ++i)
102     EXPECT_EQ_STR(procs_names_array[i], proc_pids_array[i]->process_name);
103
104   /* cleanup */
105   proc_pids_free(proc_pids_array, procs_names_array_size);
106   return 0;
107 }
108
109 DEF_TEST(pid_list_add_pid__empty_list) {
110   /* setup */
111   pids_list_t *proc_pids_instance = calloc(1, sizeof(*proc_pids_instance));
112   pid_t pid = 1234;
113
114   /* check */
115   pids_list_add_pid(proc_pids_instance, pid);
116   EXPECT_EQ_INT(pid, proc_pids_instance->pids[0]);
117
118   /* cleanup */
119   pids_list_free(proc_pids_instance);
120   return 0;
121 }
122
123 DEF_TEST(pid_list_add_pid__non_empty_list) {
124   /* setup */
125   pids_list_t *proc_pids_instance = calloc(1, sizeof(*proc_pids_instance));
126   pid_t pids[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
127
128   /* check */
129   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids); ++i)
130     pids_list_add_pid(proc_pids_instance, pids[i]);
131
132   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids); ++i) {
133     EXPECT_EQ_INT(pids[i], proc_pids_instance->pids[i]);
134   }
135
136   /* cleanup */
137   pids_list_free(proc_pids_instance);
138   return 0;
139 }
140
141 DEF_TEST(pids_list_add_pids_list__non_empty_lists) {
142   /* setup */
143   pid_t pids_array_1[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
144   pid_t pids_array_2[] = {2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007};
145   pids_list_t *pids_list_1 = calloc(1, sizeof(*pids_list_1));
146   pids_list_t *pids_list_2 = calloc(1, sizeof(*pids_list_2));
147   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array_1); ++i) {
148     pids_list_add_pid(pids_list_1, pids_array_1[i]);
149     pids_list_add_pid(pids_list_2, pids_array_2[i]);
150   }
151
152   /* check */
153   int result = pids_list_add_list(pids_list_1, pids_list_2);
154   EXPECT_EQ_INT(0, result);
155   EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array_2) +
156                     STATIC_ARRAY_SIZE(pids_array_1),
157                 pids_list_1->size);
158
159   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array_1); ++i) {
160     EXPECT_EQ_INT(1, pids_list_contains_pid(pids_list_1, pids_array_1[i]));
161     EXPECT_EQ_INT(1, pids_list_contains_pid(pids_list_1, pids_array_2[i]));
162   }
163
164   /* setup */
165   pids_list_free(pids_list_1);
166   pids_list_free(pids_list_2);
167   return 0;
168 }
169
170 DEF_TEST(pids_list_add_pids_list__add_to_empty) {
171   /* setup */
172   pid_t pids_array[] = {2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007};
173   pids_list_t *pids_list_1 = calloc(1, sizeof(*pids_list_1));
174   pids_list_t *pids_list_2 = calloc(1, sizeof(*pids_list_2));
175   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array); ++i)
176     pids_list_add_pid(pids_list_2, pids_array[i]);
177
178   /* check */
179   int result = pids_list_add_list(pids_list_1, pids_list_2);
180   EXPECT_EQ_INT(0, result);
181   EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array), pids_list_1->size);
182
183   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array); ++i)
184     EXPECT_EQ_INT(1, pids_list_contains_pid(pids_list_1, pids_array[i]));
185
186   /* setup */
187   pids_list_free(pids_list_1);
188   pids_list_free(pids_list_2);
189   return 0;
190 }
191
192 DEF_TEST(get_pid_number__valid_dir) {
193   /* setup */
194   struct dirent d;
195   sstrncpy(d.d_name, MAX_PID_STR, STATIC_ARRAY_SIZE(d.d_name));
196   d.d_type = DT_DIR;
197   pid_t pid = 0;
198
199   /* check */
200   int pid_conversion = get_pid_number(&d, &pid);
201
202   EXPECT_EQ_INT(0, pid_conversion);
203   EXPECT_EQ_INT(MAX_PID, pid);
204
205   /* cleanup */
206   return 0;
207 }
208
209 DEF_TEST(get_pid_number__invalid_dir_name) {
210   /* setup */
211   struct dirent d;
212   sstrncpy(d.d_name, "invalid", STATIC_ARRAY_SIZE(d.d_name));
213   d.d_type = DT_DIR;
214   pid_t pid = 0;
215
216   /* check */
217   int pid_conversion = get_pid_number(&d, &pid);
218
219   EXPECT_EQ_INT(-1, pid_conversion);
220   EXPECT_EQ_INT(0, pid);
221
222   /* cleanup */
223   return 0;
224 }
225
226 DEF_TEST(read_proc_name__valid_name) {
227   /* setup */
228   stub_proc_pid_t pp_stubs[] = {{"proc1", MAX_PID}};
229   stub_procfs_setup(pp_stubs, STATIC_ARRAY_SIZE(pp_stubs));
230   struct dirent d;
231   sstrncpy(d.d_name, MAX_PID_STR, STATIC_ARRAY_SIZE(d.d_name));
232   d.d_type = DT_DIR;
233
234   /* check */
235   proc_comm_t comm;
236   int read_result = read_proc_name(proc_fs, &d, comm, STATIC_ARRAY_SIZE(comm));
237
238   EXPECT_EQ_INT(strlen(pp_stubs[0].comm), read_result);
239   EXPECT_EQ_STR(pp_stubs[0].comm, comm);
240
241   /* cleanup */
242   stub_procfs_teardown();
243   return 0;
244 }
245
246 DEF_TEST(read_proc_name__invalid_name) {
247   /* setup */
248   struct dirent d;
249   sstrncpy(d.d_name, MAX_PID_STR, STATIC_ARRAY_SIZE(d.d_name));
250   d.d_type = DT_DIR;
251
252   /* check */
253   proc_comm_t comm;
254   int read_result = read_proc_name(proc_fs, &d, comm, STATIC_ARRAY_SIZE(comm));
255
256   EXPECT_EQ_INT(-1, read_result);
257
258   /* cleanup */
259   return 0;
260 }
261
262 DEF_TEST(update_proc_pids__one_proc_many_pid) {
263   /* setup */
264   const char *proc_names[] = {"proc1"};
265   stub_proc_pid_t pp_stubs[] = {{"proc1", 1007},
266                                 {"proc1", 1008},
267                                 {"proc1", 1009},
268                                 {"proc2", 1010},
269                                 {"proc3", 1011}};
270   proc_pids_t **proc_pids = NULL;
271   int result;
272   stub_procfs_setup(pp_stubs, STATIC_ARRAY_SIZE(pp_stubs));
273
274   result = initialize_proc_pids(proc_names, STATIC_ARRAY_SIZE(proc_names),
275                                 &proc_pids);
276   EXPECT_EQ_INT(0, result);
277
278   /* check */
279   result = update_proc_pids(proc_fs, proc_pids, STATIC_ARRAY_SIZE(proc_names));
280   EXPECT_EQ_INT(0, result);
281
282   /* proc name check */
283   EXPECT_EQ_STR(proc_names[0], proc_pids[0]->process_name);
284
285   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pp_stubs); ++i) {
286     if (0 == strcmp(pp_stubs[i].comm, proc_names[0]))
287       /* check if proc struct has correct pids */
288       EXPECT_EQ_INT(pids_list_contains_pid(proc_pids[0]->curr, pp_stubs[i].pid),
289                     1);
290     else
291       /* check if proc struct has no incorrect pids */
292       EXPECT_EQ_INT(pids_list_contains_pid(proc_pids[0]->curr, pp_stubs[i].pid),
293                     0);
294   }
295
296   /* cleanup */
297   proc_pids_free(proc_pids, STATIC_ARRAY_SIZE(proc_names));
298   stub_procfs_teardown();
299   return 0;
300 }
301
302 DEF_TEST(update_proc_pids__many_proc_many_pid) {
303   /* setup */
304   const char *proc_names[] = {"proc1", "proc2", "proc3"};
305   stub_proc_pid_t pp_stubs[] = {
306       {"proc1", 1007}, {"proc1", 1008}, {"proc1", 1009}, {"proc2", 2007},
307       {"proc2", 2008}, {"proc2", 2009}, {"proc3", 3007}, {"proc3", 3008},
308       {"proc3", 3009}, {"proc4", 4007}, {"proc4", 4008}, {"proc4", 4009},
309       {"proc5", 5007}, {"proc5", 5008}, {"proc5", 5009}};
310   proc_pids_t **proc_pids = NULL;
311   int result;
312   stub_procfs_setup(pp_stubs, STATIC_ARRAY_SIZE(pp_stubs));
313
314   result = initialize_proc_pids(proc_names, STATIC_ARRAY_SIZE(proc_names),
315                                 &proc_pids);
316   EXPECT_EQ_INT(0, result);
317
318   /* check */
319   result = update_proc_pids(proc_fs, proc_pids, STATIC_ARRAY_SIZE(proc_names));
320   EXPECT_EQ_INT(0, result);
321
322   for (size_t i = 0; i < STATIC_ARRAY_SIZE(proc_names); ++i) {
323
324     /* proc name check */
325     EXPECT_EQ_STR(proc_names[i], proc_pids[i]->process_name);
326
327     for (size_t j = 0; j < STATIC_ARRAY_SIZE(pp_stubs); ++j) {
328       if (0 == strcmp(pp_stubs[j].comm, proc_names[i]))
329         /* check if proc struct has correct pids */
330         EXPECT_EQ_INT(
331             pids_list_contains_pid(proc_pids[i]->curr, pp_stubs[j].pid), 1);
332       else
333         /* check if proc struct has no incorrect pids */
334         EXPECT_EQ_INT(
335             pids_list_contains_pid(proc_pids[i]->curr, pp_stubs[j].pid), 0);
336     }
337   }
338
339   /* cleanup */
340   proc_pids_free(proc_pids, STATIC_ARRAY_SIZE(proc_names));
341   stub_procfs_teardown();
342   return 0;
343 }
344
345 DEF_TEST(pids_list_diff__all_changed) {
346   /* setup */
347   pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
348   pid_t pids_array_after[] = {2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007};
349   proc_pids_t proc_pids;
350   pids_list_t curr;
351   pids_list_t prev;
352
353   prev.pids = pids_array_before;
354   prev.size = STATIC_ARRAY_SIZE(pids_array_before);
355   prev.allocated = prev.size;
356   curr.pids = pids_array_after;
357   curr.size = STATIC_ARRAY_SIZE(pids_array_after);
358   curr.allocated = curr.size;
359   proc_pids.curr = &curr;
360   proc_pids.prev = &prev;
361
362   pids_list_t *new_pids = calloc(1, sizeof(*new_pids));
363   pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids));
364
365   /* check */
366   int result = pids_list_diff(&proc_pids, new_pids, lost_pids);
367   EXPECT_EQ_INT(0, result);
368   EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array_before), lost_pids->size);
369   EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array_after), new_pids->size);
370
371   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array_before); ++i) {
372     EXPECT_EQ_INT(1, pids_list_contains_pid(new_pids, pids_array_after[i]));
373     EXPECT_EQ_INT(1, pids_list_contains_pid(lost_pids, pids_array_before[i]));
374   }
375
376   /* cleanup */
377   pids_list_free(new_pids);
378   pids_list_free(lost_pids);
379
380   return 0;
381 }
382
383 DEF_TEST(pids_list_diff__nothing_changed) {
384   /* setup */
385   pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
386   proc_pids_t proc_pids;
387   pids_list_t curr;
388   pids_list_t prev;
389
390   prev.pids = pids_array_before;
391   prev.size = STATIC_ARRAY_SIZE(pids_array_before);
392   prev.allocated = prev.size;
393   curr.pids = pids_array_before;
394   curr.size = STATIC_ARRAY_SIZE(pids_array_before);
395   curr.allocated = curr.size;
396   proc_pids.curr = &curr;
397   proc_pids.prev = &prev;
398
399   pids_list_t *new_pids = calloc(1, sizeof(*new_pids));
400   pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids));
401
402   /* check */
403   int result = pids_list_diff(&proc_pids, new_pids, lost_pids);
404   EXPECT_EQ_INT(0, result);
405   EXPECT_EQ_INT(0, lost_pids->size);
406   EXPECT_EQ_INT(0, new_pids->size);
407
408   /* cleanup */
409   pids_list_free(lost_pids);
410   pids_list_free(new_pids);
411
412   return 0;
413 }
414
415 DEF_TEST(pids_list_diff__one_added) {
416   /* setup */
417   pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
418   pid_t pids_array_after[] = {1000, 1001, 1002, 1003, 1004,
419                               1005, 1006, 1007, 1008};
420   proc_pids_t proc_pids;
421   pids_list_t curr;
422   pids_list_t prev;
423
424   prev.pids = pids_array_before;
425   prev.size = STATIC_ARRAY_SIZE(pids_array_before);
426   prev.allocated = prev.size;
427   curr.pids = pids_array_after;
428   curr.size = STATIC_ARRAY_SIZE(pids_array_after);
429   curr.allocated = curr.size;
430   proc_pids.curr = &curr;
431   proc_pids.prev = &prev;
432
433   pids_list_t *new_pids = calloc(1, sizeof(*new_pids));
434   pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids));
435
436   /* check */
437   int result = pids_list_diff(&proc_pids, new_pids, lost_pids);
438   EXPECT_EQ_INT(0, result);
439   EXPECT_EQ_INT(0, lost_pids->size);
440   EXPECT_EQ_INT(1, new_pids->size);
441   EXPECT_EQ_INT(1008, new_pids->pids[0]);
442
443   /* cleanup */
444   pids_list_free(lost_pids);
445   pids_list_free(new_pids);
446
447   return 0;
448 }
449
450 DEF_TEST(pids_list_diff__one_removed) {
451   /* setup */
452   pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004,
453                                1005, 1006, 1007, 1008};
454   pid_t pids_array_after[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
455
456   proc_pids_t proc_pids;
457   pids_list_t curr;
458   pids_list_t prev;
459
460   prev.pids = pids_array_before;
461   prev.size = STATIC_ARRAY_SIZE(pids_array_before);
462   prev.allocated = prev.size;
463   curr.pids = pids_array_after;
464   curr.size = STATIC_ARRAY_SIZE(pids_array_after);
465   curr.allocated = curr.size;
466   proc_pids.curr = &curr;
467   proc_pids.prev = &prev;
468
469   pids_list_t *new_pids = calloc(1, sizeof(*new_pids));
470   pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids));
471
472   /* check */
473   int result = pids_list_diff(&proc_pids, new_pids, lost_pids);
474   EXPECT_EQ_INT(0, result);
475   EXPECT_EQ_INT(0, new_pids->size);
476   EXPECT_EQ_INT(1, lost_pids->size);
477   EXPECT_EQ_INT(1008, lost_pids->pids[0]);
478
479   /* cleanup */
480   pids_list_free(lost_pids);
481   pids_list_free(new_pids);
482
483   return 0;
484 }
485
486 int main(void) {
487   stub_procfs_teardown();
488   RUN_TEST(initialize_proc_pids__on_nullptr);
489   RUN_TEST(pid_list_add_pid__empty_list);
490   RUN_TEST(pid_list_add_pid__non_empty_list);
491   RUN_TEST(pids_list_add_pids_list__non_empty_lists);
492   RUN_TEST(pids_list_add_pids_list__add_to_empty);
493   RUN_TEST(get_pid_number__valid_dir);
494   RUN_TEST(get_pid_number__invalid_dir_name);
495   RUN_TEST(read_proc_name__valid_name);
496   RUN_TEST(read_proc_name__invalid_name);
497   RUN_TEST(update_proc_pids__one_proc_many_pid);
498   RUN_TEST(update_proc_pids__many_proc_many_pid);
499   RUN_TEST(pids_list_diff__all_changed);
500   RUN_TEST(pids_list_diff__nothing_changed);
501   RUN_TEST(pids_list_diff__one_added);
502   RUN_TEST(pids_list_diff__one_removed);
503   stub_procfs_teardown();
504   END_TEST;
505 }