// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// All functions here are thread-hostile due to file caching unless
// commented otherwise.

#ifndef _SYSINFO_H_
#define _SYSINFO_H_

#include <config.h>

#include <time.h>
#if (defined(_WIN32) || defined(__MINGW32__)) && (!defined(__CYGWIN__) && !defined(__CYGWIN32__))
#include <windows.h>   // for DWORD
#include <tlhelp32.h>  // for CreateToolhelp32Snapshot
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>    // for pid_t
#endif
#include <stddef.h>    // for size_t
#include <limits.h>    // for PATH_MAX
#include "base/basictypes.h"
#include "base/logging.h"   // for RawFD

// This getenv function is safe to call before the C runtime is initialized.
// On Windows, it utilizes GetEnvironmentVariable() and on unix it uses
// /proc/self/environ instead calling getenv().  It's intended to be used in
// routines that run before main(), when the state required for getenv() may
// not be set up yet.  In particular, errno isn't set up until relatively late
// (after the pthreads library has a chance to make it threadsafe), and
// getenv() doesn't work until then.
// On some platforms, this call will utilize the same, static buffer for
// repeated GetenvBeforeMain() calls. Callers should not expect pointers from
// this routine to be long lived.
// Note that on unix, /proc only has the environment at the time the
// application was started, so this routine ignores setenv() calls/etc.  Also
// note it only reads the first 16K of the environment.
extern const char* GetenvBeforeMain(const char* name);

// This takes as an argument an environment-variable name (like
// CPUPROFILE) whose value is supposed to be a file-path, and sets
// path to that path, and returns true.  Non-trivial for surprising
// reasons, as documented in sysinfo.cc.  path must have space PATH_MAX.
extern bool GetUniquePathFromEnv(const char* env_name, char* path);

extern int GetSystemCPUsCount();

//  Return true if we're running POSIX (e.g., NPTL on Linux) threads,
//  as opposed to a non-POSIX thread library.  The thing that we care
//  about is whether a thread's pid is the same as the thread that
//  spawned it.  If so, this function returns true.
//  Thread-safe.
//  Note: We consider false negatives to be OK.
bool HasPosixThreads();

#ifndef SWIG  // SWIG doesn't like struct Buffer and variable arguments.

// A ProcMapsIterator abstracts access to /proc/maps for a given
// process. Needs to be stack-allocatable and avoid using stdio/malloc
// so it can be used in the google stack dumper, heap-profiler, etc.
//
// On Windows and Mac OS X, this iterator iterates *only* over DLLs
// mapped into this process space.  For Linux, FreeBSD, and Solaris,
// it iterates over *all* mapped memory regions, including anonymous
// mmaps.  For other O/Ss, it is unlikely to work at all, and Valid()
// will always return false.  Also note: this routine only works on
// FreeBSD if procfs is mounted: make sure this is in your /etc/fstab:
//    proc            /proc   procfs  rw 0 0
class ProcMapsIterator {
 public:
  struct Buffer {
#ifdef __FreeBSD__
    // FreeBSD requires us to read all of the maps file at once, so
    // we have to make a buffer that's "always" big enough
    static const size_t kBufSize = 102400;
#else   // a one-line buffer is good enough
    static const size_t kBufSize = PATH_MAX + 1024;
#endif
    char buf_[kBufSize];
  };


  // Create a new iterator for the specified pid.  pid can be 0 for "self".
  explicit ProcMapsIterator(pid_t pid);

  // Create an iterator with specified storage (for use in signal
  // handler). "buffer" should point to a ProcMapsIterator::Buffer
  // buffer can be NULL in which case a bufer will be allocated.
  ProcMapsIterator(pid_t pid, Buffer *buffer);

  // Iterate through maps_backing instead of maps if use_maps_backing
  // is true.  Otherwise the same as above.  buffer can be NULL and
  // it will allocate a buffer itself.
  ProcMapsIterator(pid_t pid, Buffer *buffer,
                   bool use_maps_backing);

  // Returns true if the iterator successfully initialized;
  bool Valid() const;

  // Returns a pointer to the most recently parsed line. Only valid
  // after Next() returns true, and until the iterator is destroyed or
  // Next() is called again.  This may give strange results on non-Linux
  // systems.  Prefer FormatLine() if that may be a concern.
  const char *CurrentLine() const { return stext_; }

  // Writes the "canonical" form of the /proc/xxx/maps info for a single
  // line to the passed-in buffer. Returns the number of bytes written,
  // or 0 if it was not able to write the complete line.  (To guarantee
  // success, buffer should have size at least Buffer::kBufSize.)
  // Takes as arguments values set via a call to Next().  The
  // "canonical" form of the line (taken from linux's /proc/xxx/maps):
  //    <start_addr(hex)>-<end_addr(hex)> <perms(rwxp)> <offset(hex)>   +
  //    <major_dev(hex)>:<minor_dev(hex)> <inode> <filename> Note: the
  // eg
  //    08048000-0804c000 r-xp 00000000 03:01 3793678    /bin/cat
  // If you don't have the dev_t (dev), feel free to pass in 0.
  // (Next() doesn't return a dev_t, though NextExt does.)
  //
  // Note: if filename and flags were obtained via a call to Next(),
  // then the output of this function is only valid if Next() returned
  // true, and only until the iterator is destroyed or Next() is
  // called again.  (Since filename, at least, points into CurrentLine.)
  static int FormatLine(char* buffer, int bufsize,
                        uint64 start, uint64 end, const char *flags,
                        uint64 offset, int64 inode, const char *filename,
                        dev_t dev);

  // Find the next entry in /proc/maps; return true if found or false
  // if at the end of the file.
  //
  // Any of the result pointers can be NULL if you're not interested
  // in those values.
  //
  // If "flags" and "filename" are passed, they end up pointing to
  // storage within the ProcMapsIterator that is valid only until the
  // iterator is destroyed or Next() is called again. The caller may
  // modify the contents of these strings (up as far as the first NUL,
  // and only until the subsequent call to Next()) if desired.

  // The offsets are all uint64 in order to handle the case of a
  // 32-bit process running on a 64-bit kernel
  //
  // IMPORTANT NOTE: see top-of-class notes for details about what
  // mapped regions Next() iterates over, depending on O/S.
  // TODO(csilvers): make flags and filename const.
  bool Next(uint64 *start, uint64 *end, char **flags,
            uint64 *offset, int64 *inode, char **filename);

  bool NextExt(uint64 *start, uint64 *end, char **flags,
               uint64 *offset, int64 *inode, char **filename,
               uint64 *file_mapping, uint64 *file_pages,
               uint64 *anon_mapping, uint64 *anon_pages,
               dev_t *dev);

  ~ProcMapsIterator();

 private:
  void Init(pid_t pid, Buffer *buffer, bool use_maps_backing);

  char *ibuf_;        // input buffer
  char *stext_;       // start of text
  char *etext_;       // end of text
  char *nextline_;    // start of next line
  char *ebuf_;        // end of buffer (1 char for a nul)
#if (defined(_WIN32) || defined(__MINGW32__)) && (!defined(__CYGWIN__) && !defined(__CYGWIN32__))
  HANDLE snapshot_;   // filehandle on dll info
  // In a change from the usual W-A pattern, there is no A variant of
  // MODULEENTRY32.  Tlhelp32.h #defines the W variant, but not the A.
  // We want the original A variants, and this #undef is the only
  // way I see to get them.  Redefining it when we're done prevents us
  // from affecting other .cc files.
# ifdef MODULEENTRY32  // Alias of W
#   undef MODULEENTRY32
  MODULEENTRY32 module_;   // info about current dll (and dll iterator)
#   define MODULEENTRY32 MODULEENTRY32W
# else  // It's the ascii, the one we want.
  MODULEENTRY32 module_;   // info about current dll (and dll iterator)
# endif
#elif defined(__MACH__)
  int current_image_; // dll's are called "images" in macos parlance
  int current_load_cmd_;   // the segment of this dll we're examining
#elif defined(__sun__)     // Solaris
  int fd_;
  char current_filename_[PATH_MAX];
#else
  int fd_;            // filehandle on /proc/*/maps
#endif
  pid_t pid_;
  char flags_[10];
  Buffer* dynamic_buffer_;  // dynamically-allocated Buffer
  bool using_maps_backing_; // true if we are looking at maps_backing instead of maps.
};

#endif  /* #ifndef SWIG */

// Helper routines

namespace tcmalloc {
int FillProcSelfMaps(char buf[], int size, bool* wrote_all);
void DumpProcSelfMaps(RawFD fd);
}

#endif   /* #ifndef _SYSINFO_H_ */
