blob: b0e58667d7ee3bdd401d8f8aa9aeb425038e62dc [file] [log] [blame]
mostang.com!davidm824d6612003-02-08 10:10:59 +00001/* libunwind - a platform-independent unwind library
mostang.com!davidm64c702c2004-03-31 07:38:06 +00002 Copyright (C) 2003-2004 Hewlett-Packard Co
mostang.com!davidm824d6612003-02-08 10:10:59 +00003 Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4
5This file is part of libunwind.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
25
26#ifndef os_linux_h
27#define os_linux_h
28
29struct map_iterator
30 {
mostang.com!davidm64c702c2004-03-31 07:38:06 +000031 off_t offset;
32 int fd;
mostang.com!davidm5de4b352004-04-21 03:56:06 +000033 size_t buf_size;
34 char *buf;
35 char *buf_end;
mostang.com!davidm824d6612003-02-08 10:10:59 +000036 };
37
mostang.com!davidm64c702c2004-03-31 07:38:06 +000038static inline char *
39ltoa (char *buf, long val)
40{
41 char *cp = buf, tmp;
42 ssize_t i, len;
43
44 do
45 {
46 *cp++ = '0' + (val % 10);
47 val /= 10;
48 }
49 while (val);
50
51 /* reverse the order of the digits: */
52 len = cp - buf;
53 --cp;
54 for (i = 0; i < len / 2; ++i)
55 {
56 tmp = buf[i];
57 buf[i] = cp[-i];
58 cp[-i] = tmp;
59 }
60 return buf + len;
61}
62
mostang.com!davidm824d6612003-02-08 10:10:59 +000063static inline void
64maps_init (struct map_iterator *mi, pid_t pid)
65{
mostang.com!davidm64c702c2004-03-31 07:38:06 +000066 char path[PATH_MAX], *cp;
mostang.com!davidm824d6612003-02-08 10:10:59 +000067
mostang.com!davidm64c702c2004-03-31 07:38:06 +000068 memcpy (path, "/proc/", 6);
69 cp = ltoa (path + 6, pid);
70 memcpy (cp, "/maps", 6);
71
72 mi->fd = open (path, O_RDONLY);
73 mi->offset = 0;
mostang.com!davidm5de4b352004-04-21 03:56:06 +000074
mostang.com!davidm2f210752004-04-21 07:24:35 +000075 cp = NULL;
76 if (mi->fd >= 0)
77 {
78 /* Try to allocate a page-sized buffer. If that fails, we'll
79 fall back on reading one line at a time. */
80 mi->buf_size = getpagesize ();
81 cp = mmap (0, mi->buf_size, PROT_READ | PROT_WRITE,
82 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
83 if (cp == MAP_FAILED)
84 cp = NULL;
85 else
86 cp += mi->buf_size;
87 }
mostang.com!davidm5de4b352004-04-21 03:56:06 +000088 mi->buf = mi->buf_end = cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +000089}
90
91static inline char *
92skip_whitespace (char *cp)
93{
94 if (!cp)
95 return NULL;
96
97 while (*cp == ' ' || *cp == '\t')
98 ++cp;
99 return cp;
100}
101
102static inline char *
103scan_hex (char *cp, unsigned long *valp)
104{
105 unsigned long num_digits = 0, digit, val = 0;
106
107 cp = skip_whitespace (cp);
108 if (!cp)
109 return NULL;
110
111 while (1)
112 {
113 digit = *cp;
114 if ((digit - '0') <= 9)
115 digit -= '0';
116 else if ((digit - 'a') < 6)
117 digit -= 'a' - 10;
118 else if ((digit - 'A') < 6)
119 digit -= 'A' - 10;
120 else
121 break;
122 val = (val << 4) | digit;
123 ++num_digits;
124 ++cp;
125 }
126 if (!num_digits)
127 return NULL;
128 *valp = val;
129 return cp;
130}
131
132static inline char *
133scan_dec (char *cp, unsigned long *valp)
134{
135 unsigned long num_digits = 0, digit, val = 0;
136
137 if (!(cp = skip_whitespace (cp)))
138 return NULL;
139
140 while (1)
141 {
hp.com!davidm6c612882004-06-29 05:59:35 +0000142 digit = *cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000143 if ((digit - '0') <= 9)
hp.com!davidm6c612882004-06-29 05:59:35 +0000144 {
145 digit -= '0';
146 ++cp;
147 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000148 else
149 break;
150 val = (10 * val) + digit;
151 ++num_digits;
152 }
153 if (!num_digits)
154 return NULL;
155 *valp = val;
156 return cp;
157}
158
159static inline char *
160scan_char (char *cp, char *valp)
161{
162 if (!cp)
163 return NULL;
164
hp.com!davidm6c612882004-06-29 05:59:35 +0000165 *valp = *cp;
166
167 /* don't step over NUL terminator */
168 if (*cp)
169 ++cp;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000170 return cp;
171}
172
173/* Scan a string delimited by white-space. Fails on empty string or
174 if string is doesn't fit in the specified buffer. */
175static inline char *
176scan_string (char *cp, char *valp, size_t buf_size)
177{
178 size_t i = 0;
179
180 if (!(cp = skip_whitespace (cp)))
181 return NULL;
182
183 while (*cp != ' ' && *cp != '\t' && *cp != '\0')
184 {
185 if (i < buf_size - 1)
186 valp[i++] = *cp;
187 ++cp;
188 }
189 if (i == 0 || i >= buf_size)
190 return NULL;
191 valp[i] = '\0';
192 return cp;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000193}
194
195static inline int
196maps_next (struct map_iterator *mi,
197 unsigned long *low, unsigned long *high, unsigned long *offset,
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000198 char *path, size_t path_size)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000199{
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000200 char line[256 + PATH_MAX], perm[16], dash, colon, *cp;
201 unsigned long major, minor, inum;
202 size_t to_read = 256; /* most lines fit in 256 characters easy */
203 ssize_t i, nread;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000204
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000205 if (mi->fd < 0)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000206 return 0;
207
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000208 while (1)
mostang.com!davidm824d6612003-02-08 10:10:59 +0000209 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000210 if (mi->buf)
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000211 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000212 ssize_t bytes_left = mi->buf_end - mi->buf;
213 char *eol = NULL;
214
215 for (i = 0; i < bytes_left; ++i)
216 {
217 if (mi->buf[i] == '\n')
218 {
219 eol = mi->buf + i;
220 break;
221 }
222 else if (mi->buf[i] == '\0')
223 break;
224 }
225 if (!eol)
226 {
227 /* copy down the remaining bytes, if any */
228 if (bytes_left > 0)
229 memcpy (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
230
231 mi->buf = mi->buf_end - mi->buf_size;
232 nread = read (mi->fd, mi->buf + bytes_left,
233 mi->buf_size - bytes_left);
234 if (nread <= 0)
235 return 0;
236
237 eol = mi->buf + bytes_left + nread - 1;
238
239 for (i = bytes_left; i < bytes_left + nread; ++i)
240 if (mi->buf[i] == '\n')
241 {
242 eol = mi->buf + i;
243 break;
244 }
245 }
246 cp = mi->buf;
247 mi->buf = eol + 1;
248 *eol = '\0';
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000249 }
250 else
251 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000252 /* maps_init() wasn't able to allocate a buffer; do it the
253 slow way. */
254 lseek (mi->fd, mi->offset, SEEK_SET);
255
256 if ((nread = read (mi->fd, line, to_read)) <= 0)
257 return 0;
258 for (i = 0; i < nread && line[i] != '\n'; ++i)
259 /* skip */;
260 if (i < nread)
261 {
262 line[i] = '\0';
263 mi->offset += i + 1;
264 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000265 else
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000266 {
267 if (to_read < sizeof (line))
268 to_read = sizeof (line) - 1;
269 else
270 mi->offset += nread; /* not supposed to happen... */
271 continue; /* duh, no newline found */
272 }
273 cp = line;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000274 }
275
276 /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000277 cp = scan_hex (cp, low);
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000278 cp = scan_char (cp, &dash);
279 cp = scan_hex (cp, high);
280 cp = scan_string (cp, perm, sizeof (perm));
281 cp = scan_hex (cp, offset);
282 cp = scan_hex (cp, &major);
283 cp = scan_char (cp, &colon);
284 cp = scan_hex (cp, &minor);
285 cp = scan_dec (cp, &inum);
286 cp = scan_string (cp, path, path_size);
287 if (!cp || dash != '-' || colon != ':')
288 continue; /* skip line with unknown or bad format */
289 return 1;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000290 }
291 return 0;
292}
293
294static inline void
295maps_close (struct map_iterator *mi)
296{
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000297 if (mi->fd < 0)
hp.com!davidmcfded022003-03-29 07:32:50 +0000298 return;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000299 close (mi->fd);
300 mi->fd = -1;
mostang.com!davidm2f210752004-04-21 07:24:35 +0000301 if (mi->buf)
302 {
303 munmap (mi->buf_end - mi->buf_size, mi->buf_size);
304 mi->buf = mi->buf_end = 0;
305 }
mostang.com!davidm824d6612003-02-08 10:10:59 +0000306}
307
308#endif /* os_linux_h */