blob: 6a2b7dd7d5f6d6111b00e990cec02494feecb2b5 [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;
David Mosberger-Tangaf2503e2006-07-25 21:35:30 -0600236 else if (nread + bytes_left < mi->buf_size)
237 {
238 /* Move contents to the end of the buffer so we
239 maintain the invariant that all bytes between
240 mi->buf and mi->buf_end are valid. */
241 memcpy (mi->buf_end - nread - bytes_left, mi->buf,
242 nread + bytes_left);
243 mi->buf = mi->buf_end - nread - bytes_left;
244 }
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000245
246 eol = mi->buf + bytes_left + nread - 1;
247
248 for (i = bytes_left; i < bytes_left + nread; ++i)
249 if (mi->buf[i] == '\n')
250 {
251 eol = mi->buf + i;
252 break;
253 }
254 }
255 cp = mi->buf;
256 mi->buf = eol + 1;
257 *eol = '\0';
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000258 }
259 else
260 {
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000261 /* maps_init() wasn't able to allocate a buffer; do it the
262 slow way. */
263 lseek (mi->fd, mi->offset, SEEK_SET);
264
265 if ((nread = read (mi->fd, line, to_read)) <= 0)
266 return 0;
267 for (i = 0; i < nread && line[i] != '\n'; ++i)
268 /* skip */;
269 if (i < nread)
270 {
271 line[i] = '\0';
272 mi->offset += i + 1;
273 }
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000274 else
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000275 {
276 if (to_read < sizeof (line))
277 to_read = sizeof (line) - 1;
278 else
279 mi->offset += nread; /* not supposed to happen... */
280 continue; /* duh, no newline found */
281 }
282 cp = line;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000283 }
284
285 /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
mostang.com!davidm5de4b352004-04-21 03:56:06 +0000286 cp = scan_hex (cp, low);
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000287 cp = scan_char (cp, &dash);
288 cp = scan_hex (cp, high);
289 cp = scan_string (cp, perm, sizeof (perm));
290 cp = scan_hex (cp, offset);
291 cp = scan_hex (cp, &major);
292 cp = scan_char (cp, &colon);
293 cp = scan_hex (cp, &minor);
294 cp = scan_dec (cp, &inum);
295 cp = scan_string (cp, path, path_size);
296 if (!cp || dash != '-' || colon != ':')
297 continue; /* skip line with unknown or bad format */
298 return 1;
mostang.com!davidm824d6612003-02-08 10:10:59 +0000299 }
300 return 0;
301}
302
303static inline void
304maps_close (struct map_iterator *mi)
305{
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000306 if (mi->fd < 0)
hp.com!davidmcfded022003-03-29 07:32:50 +0000307 return;
mostang.com!davidm64c702c2004-03-31 07:38:06 +0000308 close (mi->fd);
309 mi->fd = -1;
mostang.com!davidm2f210752004-04-21 07:24:35 +0000310 if (mi->buf)
311 {
312 munmap (mi->buf_end - mi->buf_size, mi->buf_size);
313 mi->buf = mi->buf_end = 0;
314 }
mostang.com!davidm824d6612003-02-08 10:10:59 +0000315}
316
317#endif /* os_linux_h */