os-linux.h revision 6c61288b9c5b3c967937d9ab12113bc7e368e433
1/* libunwind - a platform-independent unwind library 2 Copyright (C) 2003-2004 Hewlett-Packard Co 3 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 { 31 off_t offset; 32 int fd; 33 size_t buf_size; 34 char *buf; 35 char *buf_end; 36 }; 37 38static 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 63static inline void 64maps_init (struct map_iterator *mi, pid_t pid) 65{ 66 char path[PATH_MAX], *cp; 67 68 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; 74 75 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 } 88 mi->buf = mi->buf_end = cp; 89} 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 { 142 digit = *cp; 143 if ((digit - '0') <= 9) 144 { 145 digit -= '0'; 146 ++cp; 147 } 148 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 165 *valp = *cp; 166 167 /* don't step over NUL terminator */ 168 if (*cp) 169 ++cp; 170 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; 193} 194 195static inline int 196maps_next (struct map_iterator *mi, 197 unsigned long *low, unsigned long *high, unsigned long *offset, 198 char *path, size_t path_size) 199{ 200 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; 204 205 if (mi->fd < 0) 206 return 0; 207 208 while (1) 209 { 210 if (mi->buf) 211 { 212 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'; 249 } 250 else 251 { 252 /* 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 } 265 else 266 { 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; 274 } 275 276 /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */ 277 cp = scan_hex (cp, low); 278 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; 290 } 291 return 0; 292} 293 294static inline void 295maps_close (struct map_iterator *mi) 296{ 297 if (mi->fd < 0) 298 return; 299 close (mi->fd); 300 mi->fd = -1; 301 if (mi->buf) 302 { 303 munmap (mi->buf_end - mi->buf_size, mi->buf_size); 304 mi->buf = mi->buf_end = 0; 305 } 306} 307 308#endif /* os_linux_h */ 309