1*15858da5SNam Cao // SPDX-License-Identifier: GPL-2.0
2*15858da5SNam Cao
3*15858da5SNam Cao #include <fcntl.h>
4*15858da5SNam Cao #include <libgen.h>
5*15858da5SNam Cao #include <linux/limits.h>
6*15858da5SNam Cao #include <pthread.h>
7*15858da5SNam Cao #include <string.h>
8*15858da5SNam Cao #include <sys/resource.h>
9*15858da5SNam Cao #include <unistd.h>
10*15858da5SNam Cao
11*15858da5SNam Cao #include "../kselftest_harness.h"
12*15858da5SNam Cao
13*15858da5SNam Cao #define STACKDUMP_FILE "stack_values"
14*15858da5SNam Cao #define STACKDUMP_SCRIPT "stackdump"
15*15858da5SNam Cao #define NUM_THREAD_SPAWN 128
16*15858da5SNam Cao
do_nothing(void *)17*15858da5SNam Cao static void *do_nothing(void *)
18*15858da5SNam Cao {
19*15858da5SNam Cao while (1)
20*15858da5SNam Cao pause();
21*15858da5SNam Cao }
22*15858da5SNam Cao
crashing_child(void)23*15858da5SNam Cao static void crashing_child(void)
24*15858da5SNam Cao {
25*15858da5SNam Cao pthread_t thread;
26*15858da5SNam Cao int i;
27*15858da5SNam Cao
28*15858da5SNam Cao for (i = 0; i < NUM_THREAD_SPAWN; ++i)
29*15858da5SNam Cao pthread_create(&thread, NULL, do_nothing, NULL);
30*15858da5SNam Cao
31*15858da5SNam Cao /* crash on purpose */
32*15858da5SNam Cao i = *(int *)NULL;
33*15858da5SNam Cao }
34*15858da5SNam Cao
FIXTURE(coredump)35*15858da5SNam Cao FIXTURE(coredump)
36*15858da5SNam Cao {
37*15858da5SNam Cao char original_core_pattern[256];
38*15858da5SNam Cao };
39*15858da5SNam Cao
FIXTURE_SETUP(coredump)40*15858da5SNam Cao FIXTURE_SETUP(coredump)
41*15858da5SNam Cao {
42*15858da5SNam Cao char buf[PATH_MAX];
43*15858da5SNam Cao FILE *file;
44*15858da5SNam Cao char *dir;
45*15858da5SNam Cao int ret;
46*15858da5SNam Cao
47*15858da5SNam Cao file = fopen("/proc/sys/kernel/core_pattern", "r");
48*15858da5SNam Cao ASSERT_NE(NULL, file);
49*15858da5SNam Cao
50*15858da5SNam Cao ret = fread(self->original_core_pattern, 1, sizeof(self->original_core_pattern), file);
51*15858da5SNam Cao ASSERT_TRUE(ret || feof(file));
52*15858da5SNam Cao ASSERT_LT(ret, sizeof(self->original_core_pattern));
53*15858da5SNam Cao
54*15858da5SNam Cao self->original_core_pattern[ret] = '\0';
55*15858da5SNam Cao
56*15858da5SNam Cao ret = fclose(file);
57*15858da5SNam Cao ASSERT_EQ(0, ret);
58*15858da5SNam Cao }
59*15858da5SNam Cao
FIXTURE_TEARDOWN(coredump)60*15858da5SNam Cao FIXTURE_TEARDOWN(coredump)
61*15858da5SNam Cao {
62*15858da5SNam Cao const char *reason;
63*15858da5SNam Cao FILE *file;
64*15858da5SNam Cao int ret;
65*15858da5SNam Cao
66*15858da5SNam Cao unlink(STACKDUMP_FILE);
67*15858da5SNam Cao
68*15858da5SNam Cao file = fopen("/proc/sys/kernel/core_pattern", "w");
69*15858da5SNam Cao if (!file) {
70*15858da5SNam Cao reason = "Unable to open core_pattern";
71*15858da5SNam Cao goto fail;
72*15858da5SNam Cao }
73*15858da5SNam Cao
74*15858da5SNam Cao ret = fprintf(file, "%s", self->original_core_pattern);
75*15858da5SNam Cao if (ret < 0) {
76*15858da5SNam Cao reason = "Unable to write to core_pattern";
77*15858da5SNam Cao goto fail;
78*15858da5SNam Cao }
79*15858da5SNam Cao
80*15858da5SNam Cao ret = fclose(file);
81*15858da5SNam Cao if (ret) {
82*15858da5SNam Cao reason = "Unable to close core_pattern";
83*15858da5SNam Cao goto fail;
84*15858da5SNam Cao }
85*15858da5SNam Cao
86*15858da5SNam Cao return;
87*15858da5SNam Cao fail:
88*15858da5SNam Cao /* This should never happen */
89*15858da5SNam Cao fprintf(stderr, "Failed to cleanup stackdump test: %s\n", reason);
90*15858da5SNam Cao }
91*15858da5SNam Cao
TEST_F(coredump,stackdump)92*15858da5SNam Cao TEST_F(coredump, stackdump)
93*15858da5SNam Cao {
94*15858da5SNam Cao struct sigaction action = {};
95*15858da5SNam Cao unsigned long long stack;
96*15858da5SNam Cao char *test_dir, *line;
97*15858da5SNam Cao size_t line_length;
98*15858da5SNam Cao char buf[PATH_MAX];
99*15858da5SNam Cao int ret, i;
100*15858da5SNam Cao FILE *file;
101*15858da5SNam Cao pid_t pid;
102*15858da5SNam Cao
103*15858da5SNam Cao /*
104*15858da5SNam Cao * Step 1: Setup core_pattern so that the stackdump script is executed when the child
105*15858da5SNam Cao * process crashes
106*15858da5SNam Cao */
107*15858da5SNam Cao ret = readlink("/proc/self/exe", buf, sizeof(buf));
108*15858da5SNam Cao ASSERT_NE(-1, ret);
109*15858da5SNam Cao ASSERT_LT(ret, sizeof(buf));
110*15858da5SNam Cao buf[ret] = '\0';
111*15858da5SNam Cao
112*15858da5SNam Cao test_dir = dirname(buf);
113*15858da5SNam Cao
114*15858da5SNam Cao file = fopen("/proc/sys/kernel/core_pattern", "w");
115*15858da5SNam Cao ASSERT_NE(NULL, file);
116*15858da5SNam Cao
117*15858da5SNam Cao ret = fprintf(file, "|%1$s/%2$s %%P %1$s/%3$s", test_dir, STACKDUMP_SCRIPT, STACKDUMP_FILE);
118*15858da5SNam Cao ASSERT_LT(0, ret);
119*15858da5SNam Cao
120*15858da5SNam Cao ret = fclose(file);
121*15858da5SNam Cao ASSERT_EQ(0, ret);
122*15858da5SNam Cao
123*15858da5SNam Cao /* Step 2: Create a process who spawns some threads then crashes */
124*15858da5SNam Cao pid = fork();
125*15858da5SNam Cao ASSERT_TRUE(pid >= 0);
126*15858da5SNam Cao if (pid == 0)
127*15858da5SNam Cao crashing_child();
128*15858da5SNam Cao
129*15858da5SNam Cao /*
130*15858da5SNam Cao * Step 3: Wait for the stackdump script to write the stack pointers to the stackdump file
131*15858da5SNam Cao */
132*15858da5SNam Cao for (i = 0; i < 10; ++i) {
133*15858da5SNam Cao file = fopen(STACKDUMP_FILE, "r");
134*15858da5SNam Cao if (file)
135*15858da5SNam Cao break;
136*15858da5SNam Cao sleep(1);
137*15858da5SNam Cao }
138*15858da5SNam Cao ASSERT_NE(file, NULL);
139*15858da5SNam Cao
140*15858da5SNam Cao /* Step 4: Make sure all stack pointer values are non-zero */
141*15858da5SNam Cao for (i = 0; -1 != getline(&line, &line_length, file); ++i) {
142*15858da5SNam Cao stack = strtoull(line, NULL, 10);
143*15858da5SNam Cao ASSERT_NE(stack, 0);
144*15858da5SNam Cao }
145*15858da5SNam Cao
146*15858da5SNam Cao ASSERT_EQ(i, 1 + NUM_THREAD_SPAWN);
147*15858da5SNam Cao
148*15858da5SNam Cao fclose(file);
149*15858da5SNam Cao }
150*15858da5SNam Cao
151*15858da5SNam Cao TEST_HARNESS_MAIN
152