blob: c45aa808ce5d25429bd56f35e0546e7290d3fd80 [file] [log] [blame]
Kostya Serebryany1e172b42011-11-30 01:07:02 +00001// Copyright (c) 2006, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include <assert.h>
31#include <stdlib.h> // for getenv()
32#include <stdio.h> // for snprintf(), sscanf()
33#include <string.h> // for memmove(), memchr(), etc.
34#include <fcntl.h> // for open()
35#include <errno.h> // for errno
36#include <unistd.h> // for read()
37#if defined __MACH__ // Mac OS X, almost certainly
38#include <mach-o/dyld.h> // for iterating over dll's in ProcMapsIter
39#include <mach-o/loader.h> // for iterating over dll's in ProcMapsIter
40#include <sys/types.h>
41#include <sys/sysctl.h> // how we figure out numcpu's on OS X
42#elif defined __FreeBSD__
43#include <sys/sysctl.h>
44#elif defined __sun__ // Solaris
45#include <procfs.h> // for, e.g., prmap_t
46#elif defined(PLATFORM_WINDOWS)
47#include <process.h> // for getpid() (actually, _getpid())
48#include <shlwapi.h> // for SHGetValueA()
49#include <tlhelp32.h> // for Module32First()
50#endif
51#include "sysinfo.h"
52
53#ifdef PLATFORM_WINDOWS
54#ifdef MODULEENTRY32
55// In a change from the usual W-A pattern, there is no A variant of
56// MODULEENTRY32. Tlhelp32.h #defines the W variant, but not the A.
57// In unicode mode, tlhelp32.h #defines MODULEENTRY32 to be
58// MODULEENTRY32W. These #undefs are the only way I see to get back
59// access to the original, ascii struct (and related functions).
60#undef MODULEENTRY32
61#undef Module32First
62#undef Module32Next
63#undef PMODULEENTRY32
64#undef LPMODULEENTRY32
65#endif /* MODULEENTRY32 */
66// MinGW doesn't seem to define this, perhaps some windowsen don't either.
67#ifndef TH32CS_SNAPMODULE32
68#define TH32CS_SNAPMODULE32 0
69#endif /* TH32CS_SNAPMODULE32 */
70#endif /* PLATFORM_WINDOWS */
71
72// Re-run fn until it doesn't cause EINTR.
73#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
74
75// open/read/close can set errno, which may be illegal at this
76// time, so prefer making the syscalls directly if we can.
77#ifdef HAVE_SYS_SYSCALL_H
78# include <sys/syscall.h>
79# define safeopen(filename, mode) syscall(SYS_open, filename, mode)
80# define saferead(fd, buffer, size) syscall(SYS_read, fd, buffer, size)
81# define safeclose(fd) syscall(SYS_close, fd)
82#else
83# define safeopen(filename, mode) open(filename, mode)
84# define saferead(fd, buffer, size) read(fd, buffer, size)
85# define safeclose(fd) close(fd)
86#endif
87
88
89// ----------------------------------------------------------------------
90// HasPosixThreads()
91// Return true if we're running POSIX (e.g., NPTL on Linux)
92// threads, as opposed to a non-POSIX thread libary. The thing
93// that we care about is whether a thread's pid is the same as
94// the thread that spawned it. If so, this function returns
95// true.
96// ----------------------------------------------------------------------
97bool HasPosixThreads() {
98#if defined(__linux__) and !defined(ANDROID)
99#ifndef _CS_GNU_LIBPTHREAD_VERSION
100#define _CS_GNU_LIBPTHREAD_VERSION 3
101#endif
102 char buf[32];
103 // We assume that, if confstr() doesn't know about this name, then
104 // the same glibc is providing LinuxThreads.
105 if (confstr(_CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf)) == 0)
106 return false;
107 return strncmp(buf, "NPTL", 4) == 0;
108#elif defined(PLATFORM_WINDOWS) || defined(__CYGWIN__) || defined(__CYGWIN32__)
109 return false;
110#else // other OS
111 return true; // Assume that everything else has Posix
112#endif // else OS_LINUX
113}
114
115// ----------------------------------------------------------------------
116
117#define CHECK_LT(x, y) do { assert((x) < (y)); } while (0)
118
119#if defined __linux__ || defined __FreeBSD__ || defined __sun__ || defined __CYGWIN__ || defined __CYGWIN32__
120static void ConstructFilename(const char* spec, pid_t pid,
121 char* buf, int buf_size) {
122 CHECK_LT(snprintf(buf, buf_size,
123 spec,
124 static_cast<int>(pid ? pid : getpid())), buf_size);
125}
126#endif
127
128// A templatized helper function instantiated for Mach (OS X) only.
129// It can handle finding info for both 32 bits and 64 bits.
130// Returns true if it successfully handled the hdr, false else.
131#ifdef __MACH__ // Mac OS X, almost certainly
132template<uint32_t kMagic, uint32_t kLCSegment,
133 typename MachHeader, typename SegmentCommand>
134static bool NextExtMachHelper(const mach_header* hdr,
135 int current_image, int current_load_cmd,
136 uint64 *start, uint64 *end, char **flags,
137 uint64 *offset, int64 *inode, char **filename,
138 uint64 *file_mapping, uint64 *file_pages,
139 uint64 *anon_mapping, uint64 *anon_pages,
140 dev_t *dev) {
141 static char kDefaultPerms[5] = "r-xp";
142 if (hdr->magic != kMagic)
143 return false;
144 const char* lc = (const char *)hdr + sizeof(MachHeader);
145 // TODO(csilvers): make this not-quadradic (increment and hold state)
146 for (int j = 0; j < current_load_cmd; j++) // advance to *our* load_cmd
147 lc += ((const load_command *)lc)->cmdsize;
148 if (((const load_command *)lc)->cmd == kLCSegment) {
149 const intptr_t dlloff = _dyld_get_image_vmaddr_slide(current_image);
150 const SegmentCommand* sc = (const SegmentCommand *)lc;
151 if (start) *start = sc->vmaddr + dlloff;
152 if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
153 if (flags) *flags = kDefaultPerms; // can we do better?
154 if (offset) *offset = sc->fileoff;
155 if (inode) *inode = 0;
156 if (filename)
157 *filename = const_cast<char*>(_dyld_get_image_name(current_image));
158 if (file_mapping) *file_mapping = 0;
159 if (file_pages) *file_pages = 0; // could we use sc->filesize?
160 if (anon_mapping) *anon_mapping = 0;
161 if (anon_pages) *anon_pages = 0;
162 if (dev) *dev = 0;
163 return true;
164 }
165
166 return false;
167}
168#endif
169
170ProcMapsIterator::ProcMapsIterator(pid_t pid) {
171 Init(pid, NULL, false);
172}
173
174ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer) {
175 Init(pid, buffer, false);
176}
177
178ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer,
179 bool use_maps_backing) {
180 Init(pid, buffer, use_maps_backing);
181}
182
183void ProcMapsIterator::Init(pid_t pid, Buffer *buffer,
184 bool use_maps_backing) {
185 pid_ = pid;
186 using_maps_backing_ = use_maps_backing;
187 dynamic_buffer_ = NULL;
188 if (!buffer) {
189 // If the user didn't pass in any buffer storage, allocate it
190 // now. This is the normal case; the signal handler passes in a
191 // static buffer.
192 buffer = dynamic_buffer_ = new Buffer;
193 } else {
194 dynamic_buffer_ = NULL;
195 }
196
197 ibuf_ = buffer->buf_;
198
199 stext_ = etext_ = nextline_ = ibuf_;
200 ebuf_ = ibuf_ + Buffer::kBufSize - 1;
201 nextline_ = ibuf_;
202
203#if defined(__linux__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
204 if (use_maps_backing) { // don't bother with clever "self" stuff in this case
205 ConstructFilename("/proc/%d/maps_backing", pid, ibuf_, Buffer::kBufSize);
206 } else if (pid == 0) {
207 // We have to kludge a bit to deal with the args ConstructFilename
208 // expects. The 1 is never used -- it's only impt. that it's not 0.
209 ConstructFilename("/proc/self/maps", 1, ibuf_, Buffer::kBufSize);
210 } else {
211 ConstructFilename("/proc/%d/maps", pid, ibuf_, Buffer::kBufSize);
212 }
213 // No error logging since this can be called from the crash dump
214 // handler at awkward moments. Users should call Valid() before
215 // using.
216 NO_INTR(fd_ = open(ibuf_, O_RDONLY));
217#elif defined(__FreeBSD__)
218 // We don't support maps_backing on freebsd
219 if (pid == 0) {
220 ConstructFilename("/proc/curproc/map", 1, ibuf_, Buffer::kBufSize);
221 } else {
222 ConstructFilename("/proc/%d/map", pid, ibuf_, Buffer::kBufSize);
223 }
224 NO_INTR(fd_ = open(ibuf_, O_RDONLY));
225#elif defined(__sun__)
226 if (pid == 0) {
227 ConstructFilename("/proc/self/map", 1, ibuf_, Buffer::kBufSize);
228 } else {
229 ConstructFilename("/proc/%d/map", pid, ibuf_, Buffer::kBufSize);
230 }
231 NO_INTR(fd_ = open(ibuf_, O_RDONLY));
232#elif defined(__MACH__)
233 current_image_ = _dyld_image_count(); // count down from the top
234 current_load_cmd_ = -1;
235#elif defined(PLATFORM_WINDOWS)
236 snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE |
237 TH32CS_SNAPMODULE32,
238 GetCurrentProcessId());
239 memset(&module_, 0, sizeof(module_));
240#else
241 fd_ = -1; // so Valid() is always false
242#endif
243
244}
245
246ProcMapsIterator::~ProcMapsIterator() {
247#if defined(PLATFORM_WINDOWS)
248 if (snapshot_ != INVALID_HANDLE_VALUE) CloseHandle(snapshot_);
249#elif defined(__MACH__)
250 // no cleanup necessary!
251#else
252 if (fd_ >= 0) NO_INTR(close(fd_));
253#endif
254 delete dynamic_buffer_;
255}
256
257bool ProcMapsIterator::Valid() const {
258#if defined(PLATFORM_WINDOWS)
259 return snapshot_ != INVALID_HANDLE_VALUE;
260#elif defined(__MACH__)
261 return 1;
262#else
263 return fd_ != -1;
264#endif
265}
266
267bool ProcMapsIterator::Next(uint64 *start, uint64 *end, char **flags,
268 uint64 *offset, int64 *inode, char **filename) {
269 return NextExt(start, end, flags, offset, inode, filename, NULL, NULL,
270 NULL, NULL, NULL);
271}
272
273// This has too many arguments. It should really be building
274// a map object and returning it. The problem is that this is called
275// when the memory allocator state is undefined, hence the arguments.
276bool ProcMapsIterator::NextExt(uint64 *start, uint64 *end, char **flags,
277 uint64 *offset, int64 *inode, char **filename,
278 uint64 *file_mapping, uint64 *file_pages,
279 uint64 *anon_mapping, uint64 *anon_pages,
280 dev_t *dev) {
281
282#if defined(__linux__) || defined(__FreeBSD__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
283 do {
284 // Advance to the start of the next line
285 stext_ = nextline_;
286
287 // See if we have a complete line in the buffer already
288 nextline_ = static_cast<char *>(memchr (stext_, '\n', etext_ - stext_));
289 if (!nextline_) {
290 // Shift/fill the buffer so we do have a line
291 int count = etext_ - stext_;
292
293 // Move the current text to the start of the buffer
294 memmove(ibuf_, stext_, count);
295 stext_ = ibuf_;
296 etext_ = ibuf_ + count;
297
298 int nread = 0; // fill up buffer with text
299 while (etext_ < ebuf_) {
300 NO_INTR(nread = read(fd_, etext_, ebuf_ - etext_));
301 if (nread > 0)
302 etext_ += nread;
303 else
304 break;
305 }
306
307 // Zero out remaining characters in buffer at EOF to avoid returning
308 // garbage from subsequent calls.
309 if (etext_ != ebuf_ && nread == 0) {
310 memset(etext_, 0, ebuf_ - etext_);
311 }
312 *etext_ = '\n'; // sentinel; safe because ibuf extends 1 char beyond ebuf
313 nextline_ = static_cast<char *>(memchr (stext_, '\n', etext_ + 1 - stext_));
314 }
315 *nextline_ = 0; // turn newline into nul
316 nextline_ += ((nextline_ < etext_)? 1 : 0); // skip nul if not end of text
317 // stext_ now points at a nul-terminated line
318 uint64 tmpstart, tmpend, tmpoffset;
319 int64 tmpinode;
320 int major, minor;
321 unsigned filename_offset = 0;
322#if defined(__linux__)
323 // for now, assume all linuxes have the same format
324 if (sscanf(stext_, "%"SCNx64"-%"SCNx64" %4s %"SCNx64" %x:%x %"SCNd64" %n",
325 start ? start : &tmpstart,
326 end ? end : &tmpend,
327 flags_,
328 offset ? offset : &tmpoffset,
329 &major, &minor,
330 inode ? inode : &tmpinode, &filename_offset) != 7) continue;
331#elif defined(__CYGWIN__) || defined(__CYGWIN32__)
332 // cygwin is like linux, except the third field is the "entry point"
333 // rather than the offset (see format_process_maps at
334 // http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/fhandler_process.cc?rev=1.89&content-type=text/x-cvsweb-markup&cvsroot=src
335 // Offset is always be 0 on cygwin: cygwin implements an mmap
336 // by loading the whole file and then calling NtMapViewOfSection.
337 // Cygwin also seems to set its flags kinda randomly; use windows default.
338 char tmpflags[5];
339 if (offset)
340 *offset = 0;
341 strcpy(flags_, "r-xp");
342 if (sscanf(stext_, "%llx-%llx %4s %llx %x:%x %lld %n",
343 start ? start : &tmpstart,
344 end ? end : &tmpend,
345 tmpflags,
346 &tmpoffset,
347 &major, &minor,
348 inode ? inode : &tmpinode, &filename_offset) != 7) continue;
349#elif defined(__FreeBSD__)
350 // For the format, see http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/fs/procfs/procfs_map.c?rev=1.31&content-type=text/x-cvsweb-markup
351 tmpstart = tmpend = tmpoffset = 0;
352 tmpinode = 0;
353 major = minor = 0; // can't get this info in freebsd
354 if (inode)
355 *inode = 0; // nor this
356 if (offset)
357 *offset = 0; // seems like this should be in there, but maybe not
358 // start end resident privateresident obj(?) prot refcnt shadowcnt
359 // flags copy_on_write needs_copy type filename:
360 // 0x8048000 0x804a000 2 0 0xc104ce70 r-x 1 0 0x0 COW NC vnode /bin/cat
361 if (sscanf(stext_, "0x%"SCNx64" 0x%"SCNx64" %*d %*d %*p %3s %*d %*d 0x%*x %*s %*s %*s %n",
362 start ? start : &tmpstart,
363 end ? end : &tmpend,
364 flags_,
365 &filename_offset) != 3) continue;
366#endif
367
368 // Depending on the Linux kernel being used, there may or may not be a space
369 // after the inode if there is no filename. sscanf will in such situations
370 // nondeterministically either fill in filename_offset or not (the results
371 // differ on multiple calls in the same run even with identical arguments).
372 // We don't want to wander off somewhere beyond the end of the string.
373 size_t stext_length = strlen(stext_);
374 if (filename_offset == 0 || filename_offset > stext_length)
375 filename_offset = stext_length;
376
377 // We found an entry
378 if (flags) *flags = flags_;
379 if (filename) *filename = stext_ + filename_offset;
380 if (dev) *dev = minor | (major << 8);
381
382 if (using_maps_backing_) {
383 // Extract and parse physical page backing info.
384 char *backing_ptr = stext_ + filename_offset +
385 strlen(stext_+filename_offset);
386
387 // find the second '('
388 int paren_count = 0;
389 while (--backing_ptr > stext_) {
390 if (*backing_ptr == '(') {
391 ++paren_count;
392 if (paren_count >= 2) {
393 uint64 tmp_file_mapping;
394 uint64 tmp_file_pages;
395 uint64 tmp_anon_mapping;
396 uint64 tmp_anon_pages;
397
398 sscanf(backing_ptr+1, "F %"SCNx64" %"SCNd64") (A %"SCNx64" %"SCNd64")",
399 file_mapping ? file_mapping : &tmp_file_mapping,
400 file_pages ? file_pages : &tmp_file_pages,
401 anon_mapping ? anon_mapping : &tmp_anon_mapping,
402 anon_pages ? anon_pages : &tmp_anon_pages);
403 // null terminate the file name (there is a space
404 // before the first (.
405 backing_ptr[-1] = 0;
406 break;
407 }
408 }
409 }
410 }
411
412 return true;
413 } while (etext_ > ibuf_);
414#elif defined(__sun__)
415 // This is based on MA_READ == 4, MA_WRITE == 2, MA_EXEC == 1
416 static char kPerms[8][4] = { "---", "--x", "-w-", "-wx",
417 "r--", "r-x", "rw-", "rwx" };
418 COMPILE_ASSERT(MA_READ == 4, solaris_ma_read_must_equal_4);
419 COMPILE_ASSERT(MA_WRITE == 2, solaris_ma_write_must_equal_2);
420 COMPILE_ASSERT(MA_EXEC == 1, solaris_ma_exec_must_equal_1);
421 Buffer object_path;
422 int nread = 0; // fill up buffer with text
423 NO_INTR(nread = read(fd_, ibuf_, sizeof(prmap_t)));
424 if (nread == sizeof(prmap_t)) {
425 long inode_from_mapname = 0;
426 prmap_t* mapinfo = reinterpret_cast<prmap_t*>(ibuf_);
427 // Best-effort attempt to get the inode from the filename. I think the
428 // two middle ints are major and minor device numbers, but I'm not sure.
429 sscanf(mapinfo->pr_mapname, "ufs.%*d.%*d.%ld", &inode_from_mapname);
430
431 if (pid_ == 0) {
432 CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize,
433 "/proc/self/path/%s", mapinfo->pr_mapname),
434 Buffer::kBufSize);
435 } else {
436 CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize,
437 "/proc/%d/path/%s",
438 static_cast<int>(pid_), mapinfo->pr_mapname),
439 Buffer::kBufSize);
440 }
441 ssize_t len = readlink(object_path.buf_, current_filename_, PATH_MAX);
442 CHECK_LT(len, PATH_MAX);
443 if (len < 0)
444 len = 0;
445 current_filename_[len] = '\0';
446
447 if (start) *start = mapinfo->pr_vaddr;
448 if (end) *end = mapinfo->pr_vaddr + mapinfo->pr_size;
449 if (flags) *flags = kPerms[mapinfo->pr_mflags & 7];
450 if (offset) *offset = mapinfo->pr_offset;
451 if (inode) *inode = inode_from_mapname;
452 if (filename) *filename = current_filename_;
453 if (file_mapping) *file_mapping = 0;
454 if (file_pages) *file_pages = 0;
455 if (anon_mapping) *anon_mapping = 0;
456 if (anon_pages) *anon_pages = 0;
457 if (dev) *dev = 0;
458 return true;
459 }
460#elif defined(__MACH__)
461 // We return a separate entry for each segment in the DLL. (TODO(csilvers):
462 // can we do better?) A DLL ("image") has load-commands, some of which
463 // talk about segment boundaries.
464 // cf image_for_address from http://svn.digium.com/view/asterisk/team/oej/minivoicemail/dlfcn.c?revision=53912
465 for (; current_image_ >= 0; current_image_--) {
466 const mach_header* hdr = _dyld_get_image_header(current_image_);
467 if (!hdr) continue;
468 if (current_load_cmd_ < 0) // set up for this image
469 current_load_cmd_ = hdr->ncmds; // again, go from the top down
470
471 // We start with the next load command (we've already looked at this one).
472 for (current_load_cmd_--; current_load_cmd_ >= 0; current_load_cmd_--) {
473#ifdef MH_MAGIC_64
474 if (NextExtMachHelper<MH_MAGIC_64, LC_SEGMENT_64,
475 struct mach_header_64, struct segment_command_64>(
476 hdr, current_image_, current_load_cmd_,
477 start, end, flags, offset, inode, filename,
478 file_mapping, file_pages, anon_mapping,
479 anon_pages, dev)) {
480 return true;
481 }
482#endif
483 if (NextExtMachHelper<MH_MAGIC, LC_SEGMENT,
484 struct mach_header, struct segment_command>(
485 hdr, current_image_, current_load_cmd_,
486 start, end, flags, offset, inode, filename,
487 file_mapping, file_pages, anon_mapping,
488 anon_pages, dev)) {
489 return true;
490 }
491 }
492 // If we get here, no more load_cmd's in this image talk about
493 // segments. Go on to the next image.
494 }
495#elif defined(PLATFORM_WINDOWS)
496 static char kDefaultPerms[5] = "r-xp";
497 BOOL ok;
498 if (module_.dwSize == 0) { // only possible before first call
499 module_.dwSize = sizeof(module_);
500 ok = Module32First(snapshot_, &module_);
501 } else {
502 ok = Module32Next(snapshot_, &module_);
503 }
504 if (ok) {
505 uint64 base_addr = reinterpret_cast<DWORD_PTR>(module_.modBaseAddr);
506 if (start) *start = base_addr;
507 if (end) *end = base_addr + module_.modBaseSize;
508 if (flags) *flags = kDefaultPerms;
509 if (offset) *offset = 0;
510 if (inode) *inode = 0;
511 if (filename) *filename = module_.szExePath;
512 if (file_mapping) *file_mapping = 0;
513 if (file_pages) *file_pages = 0;
514 if (anon_mapping) *anon_mapping = 0;
515 if (anon_pages) *anon_pages = 0;
516 if (dev) *dev = 0;
517 return true;
518 }
519#endif
520
521 // We didn't find anything
522 return false;
523}
524
525int ProcMapsIterator::FormatLine(char* buffer, int bufsize,
526 uint64 start, uint64 end, const char *flags,
527 uint64 offset, int64 inode,
528 const char *filename, dev_t dev) {
529 // We assume 'flags' looks like 'rwxp' or 'rwx'.
530 char r = (flags && flags[0] == 'r') ? 'r' : '-';
531 char w = (flags && flags[0] && flags[1] == 'w') ? 'w' : '-';
532 char x = (flags && flags[0] && flags[1] && flags[2] == 'x') ? 'x' : '-';
533 // p always seems set on linux, so we set the default to 'p', not '-'
534 char p = (flags && flags[0] && flags[1] && flags[2] && flags[3] != 'p')
535 ? '-' : 'p';
536
537 const int rc = snprintf(buffer, bufsize,
538 "%08"PRIx64"-%08"PRIx64" %c%c%c%c %08"PRIx64" %02x:%02x %-11"PRId64" %s\n",
539 start, end, r,w,x,p, offset,
540 static_cast<int>(dev/256), static_cast<int>(dev%256),
541 inode, filename);
542 return (rc < 0 || rc >= bufsize) ? 0 : rc;
543}
544
545// Helper to add the list of mapped shared libraries to a profile.
546// Fill formatted "/proc/self/maps" contents into buffer 'buf' of size 'size'
547// and return the actual size occupied in 'buf'. We fill wrote_all to true
548// if we successfully wrote all proc lines to buf, false else.
549// We do not provision for 0-terminating 'buf'.
550int FillProcSelfMaps(char buf[], int size, bool* wrote_all) {
551 ProcMapsIterator::Buffer iterbuf;
552 ProcMapsIterator it(0, &iterbuf); // 0 means "current pid"
553
554 uint64 start, end, offset;
555 int64 inode;
556 char *flags, *filename;
557 int bytes_written = 0;
558 *wrote_all = true;
559 while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
560 const int line_length = it.FormatLine(buf + bytes_written,
561 size - bytes_written,
562 start, end, flags, offset,
563 inode, filename, 0);
564 if (line_length == 0)
565 *wrote_all = false; // failed to write this line out
566 else
567 bytes_written += line_length;
568
569 }
570 return bytes_written;
571}
572
573// Dump the same data as FillProcSelfMaps reads to fd.
574// It seems easier to repeat parts of FillProcSelfMaps here than to
575// reuse it via a call.
576void DumpProcSelfMaps(RawFD fd) {
577 ProcMapsIterator::Buffer iterbuf;
578 ProcMapsIterator it(0, &iterbuf); // 0 means "current pid"
579
580 uint64 start, end, offset;
581 int64 inode;
582 char *flags, *filename;
583 ProcMapsIterator::Buffer linebuf;
584 while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
585 int written = it.FormatLine(linebuf.buf_, sizeof(linebuf.buf_),
586 start, end, flags, offset, inode, filename,
587 0);
588 RawWrite(fd, linebuf.buf_, written);
589 }
590}
591
592// Re-run fn until it doesn't cause EINTR.
593#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
594
595RawFD RawOpenForWriting(const char* filename) {
596 return open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664);
597}
598
599void RawWrite(RawFD fd, const char* buf, size_t len) {
600 while (len > 0) {
601 ssize_t r;
602 NO_INTR(r = write(fd, buf, len));
603 if (r <= 0) break;
604 buf += r;
605 len -= r;
606 }
607}
608
609void RawClose(RawFD fd) {
610 NO_INTR(close(fd));
611}
612