os-linux.h revision af2503e2230f59a8b87ae5b71ef57e7e18b24a98
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 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 } 245 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'; 258 } 259 else 260 { 261 /* 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 } 274 else 275 { 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; 283 } 284 285 /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */ 286 cp = scan_hex (cp, low); 287 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; 299 } 300 return 0; 301} 302 303static inline void 304maps_close (struct map_iterator *mi) 305{ 306 if (mi->fd < 0) 307 return; 308 close (mi->fd); 309 mi->fd = -1; 310 if (mi->buf) 311 { 312 munmap (mi->buf_end - mi->buf_size, mi->buf_size); 313 mi->buf = mi->buf_end = 0; 314 } 315} 316 317#endif /* os_linux_h */ 318