improve behaviour under VPC, fixes from nicolas tittley.
[minix.git] / commands / simple / dumpcore.c
blobf032141225dcfd9d4280403a0c604013e98587b8
1 /* dumpcore - create core file of running process */
3 #include <fcntl.h>
4 #include <unistd.h>
5 #include <minix/config.h>
6 #include <minix/type.h>
7 #include <minix/ipc.h>
8 #include <minix/const.h>
9 #include <sys/ptrace.h>
10 #include <sys/wait.h>
11 #include <signal.h>
12 #include <timers.h>
13 #include <errno.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdlib.h>
18 #include "../../kernel/arch/i386/include/archtypes.h"
19 #include "../../kernel/proc.h"
21 #define CLICK_WORDS (CLICK_SIZE / sizeof(unsigned long))
23 int adjust_stack(pid_t pid, struct mem_map *seg)
25 static unsigned long buf[CLICK_WORDS];
26 struct ptrace_range pr;
27 size_t off, top, bottom;
28 int i;
30 /* FIXME: kernel/VM strangeness */
31 seg->mem_vir -= seg->mem_len - 1;
33 /* Scan the stack, top to bottom, to find the lowest accessible region.
34 * In practice that will be at 64MB, so we also scan for the lowest non-zero
35 * region in order to keep the core file size managable.
36 * Portability note: this code assumes that the stack grows down.
38 top = seg->mem_vir + seg->mem_len;
40 pr.pr_space = TS_DATA;
41 pr.pr_addr = (top - 1) << CLICK_SHIFT;
42 pr.pr_size = sizeof(buf);
43 pr.pr_ptr = buf;
45 for (off = top - 1; off >= seg->mem_vir; off--) {
46 if (ptrace(T_GETRANGE, pid, (long) &pr, 0)) {
47 if (errno == EFAULT)
48 break;
50 perror("ptrace(T_GETRANGE)");
51 return 1;
54 for (i = 0; i < CLICK_WORDS; i += sizeof(buf[0]))
55 if (buf[i] != 0)
56 bottom = off;
58 pr.pr_addr -= sizeof(buf);
61 /* Add one extra zero page as margin. */
62 if (bottom > off && bottom > seg->mem_vir)
63 bottom--;
65 seg->mem_len -= bottom - seg->mem_vir;
66 seg->mem_vir = bottom;
68 return 0;
71 int write_seg(int fd, pid_t pid, int seg, off_t seg_off, phys_bytes seg_bytes)
73 int r;
74 off_t off;
75 ssize_t w;
76 static char buf[CLICK_SIZE];
77 struct ptrace_range pr;
79 pr.pr_space = (seg == T) ? TS_INS : TS_DATA;
80 pr.pr_addr = seg_off;
81 pr.pr_size = sizeof(buf);
82 pr.pr_ptr = buf;
84 for ( ; pr.pr_addr < seg_off + seg_bytes; pr.pr_addr += sizeof(buf))
86 /* Copy a chunk from user space to the block buffer. */
87 if (ptrace(T_GETRANGE, pid, (long) &pr, 0)) {
88 /* Create holes for inaccessible areas. */
89 if (errno == EFAULT) {
90 lseek(fd, sizeof(buf), SEEK_CUR);
91 continue;
94 perror("ptrace(T_GETRANGE)");
95 return 1;
98 if((w=write(fd, buf, sizeof(buf))) != sizeof(buf)) {
99 if(w < 0) printf("write error: %s\n", strerror(errno));
100 printf("write_seg: write failed: %d/%d\n", w, sizeof(buf));
101 return 1;
105 return 0;
108 int dumpcore(pid_t pid)
110 int r, seg, fd;
111 vir_bytes len;
112 off_t off, seg_off;
113 long data;
114 struct mem_map segs[NR_LOCAL_SEGS];
115 struct proc procentry;
116 ssize_t w;
117 char core_name[PATH_MAX];
119 /* Get the process table entry for this process. */
120 len = sizeof(struct proc) / sizeof(long);
121 for (off = 0; off < len; off++)
123 errno = 0;
124 data = ptrace(T_GETUSER, pid, off * sizeof(long), 0);
125 if (data == -1 && errno != 0)
127 perror("ptrace(T_GETUSER)");
128 return 1;
131 ((long *) &procentry)[off] = data;
134 memcpy(segs, procentry.p_memmap, sizeof(segs));
136 /* Correct and reduce the stack segment. */
137 r = adjust_stack(pid, &segs[S]);
138 if (r != 0)
139 goto error;
141 /* Create a core file with a temporary, unique name. */
142 sprintf(core_name, "core.%d", pid);
144 if((fd = open(core_name, O_CREAT|O_EXCL|O_WRONLY, 0600)) < 0) {
145 fprintf(stderr, "couldn't open %s (%s)\n", core_name,
146 strerror(errno));
147 return 1;
150 /* Write out the process's segments. */
151 if((w=write(fd, segs, sizeof(segs))) != sizeof(segs)) {
152 if(w < 0) printf("write error: %s\n", strerror(errno));
153 printf( "segs write failed: %d/%d\n", w, sizeof(segs));
154 goto error;
157 /* Write out the whole kernel process table entry to get the regs. */
158 if((w=write(fd, &procentry, sizeof(procentry))) != sizeof(procentry)) {
159 if(w < 0) printf("write error: %s\n", strerror(errno));
160 printf( "proc write failed: %d/%d\n", w, sizeof(procentry));
161 goto error;
164 /* Loop through segments and write the segments themselves out. */
165 for (seg = 0; seg < NR_LOCAL_SEGS; seg++) {
166 len= segs[seg].mem_len << CLICK_SHIFT;
167 seg_off= segs[seg].mem_vir << CLICK_SHIFT;
168 r= write_seg(fd, pid, seg, seg_off, len);
169 if (r != 0)
170 goto error;
173 /* Give the core file its final name. */
174 if (rename(core_name, "core")) {
175 perror("rename");
176 goto error;
179 close(fd);
181 return 0;
183 error:
184 close(fd);
186 unlink(core_name);
188 return 1;
191 int main(int argc, char *argv[])
193 pid_t pid;
194 int r, status;
196 if(argc != 2) {
197 printf("usage: %s <pid>\n", argv[0]);
198 return 1;
201 pid = atoi(argv[1]);
203 if (ptrace(T_ATTACH, pid, 0, 0) != 0) {
204 perror("ptrace(T_ATTACH)");
205 return 1;
208 if (waitpid(pid, &status, 0) != pid) {
209 perror("waitpid");
210 return 1;
213 while (WIFSTOPPED(status) && WSTOPSIG(status) != SIGSTOP) {
214 /* whatever happens here is fine */
215 ptrace(T_RESUME, pid, 0, WSTOPSIG(status));
217 if (waitpid(pid, &status, 0) != pid) {
218 perror("waitpid");
219 return 1;
223 if (!WIFSTOPPED(status)) {
224 fprintf(stderr, "process died while attaching\n");
225 return 1;
228 r = dumpcore(pid);
230 if (ptrace(T_DETACH, pid, 0, 0)) {
231 fprintf(stderr, "warning, detaching failed (%s)\n",
232 strerror(errno));
235 return r;