/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
 * Copyright (c) 2023, gperftools Contributors
 * 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.
 */

// mmap_hook.h holds strictly non-public API for hooking mmap/sbrk
// events as well invoking mmap/munmap with ability to bypass hooks
// (i.e. for low_level_alloc).
#ifndef MMAP_HOOK_H
#define MMAP_HOOK_H

#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include "base/basictypes.h"

namespace tcmalloc {

struct DirectAnonMMapResult {
  void* addr;
  bool success;
};

// DirectAnonMMap does mmap of r+w anonymous memory. Optionally
// bypassing or not mmap hooks.
ATTRIBUTE_VISIBILITY_HIDDEN DirectAnonMMapResult DirectAnonMMap(bool invoke_hooks, size_t length);
// DirectMUnMap does munmap of given region optionally bypassing mmap hooks.
ATTRIBUTE_VISIBILITY_HIDDEN int DirectMUnMap(bool invoke_hooks, void* start, size_t length);

// We use those by tests to see what parts we think should work.
extern ATTRIBUTE_VISIBILITY_HIDDEN const bool mmap_hook_works;
extern ATTRIBUTE_VISIBILITY_HIDDEN const bool sbrk_hook_works;

// MMapEventFn gets this struct with all the details of
// mmap/munmap/mremap/sbrk event.
struct MappingEvent {
  MappingEvent() {
    memset(this, 0, sizeof(*this));
  }

  // before_XXX fields describe address space chunk that was removed
  // from address space (say via munmap or mremap)
  void* before_address;
  size_t before_length;

  // after_XXX fields describe address space chunk that was added to
  // address space.
  void* after_address;
  size_t after_length;

  // This group of fields gets populated from mmap file, flags, prot
  // fields.
  int prot;
  int flags;
  int file_fd;
  int64_t file_off;

  unsigned after_valid:1;
  unsigned before_valid:1;
  unsigned file_valid:1;
  unsigned is_sbrk:1;

  // NOTE, in order to get mapping event backtrace you need to request
  // it via need_backtrace callback.
  int stack_depth;
  void** stack;
};

// Pass this to Hook/Unhook function below. Note, nature of
// implementation requires that this chunk of memory must be valid
// even after unhook. So typical use-case is to use global variable
// storage.
//
// All fields are private.
class MappingHookSpace {
public:
  constexpr MappingHookSpace() = default;

  bool initialized = false;

  static constexpr size_t kSize = sizeof(void*) * 4;
  alignas(alignof(void*)) char storage[kSize] = {};
};

using MMapEventFn = void (*)(const MappingEvent& evt);
using MMapEventNeedBacktraceFn = int (*)(const MappingEvent& evt);

// HookMMapEvents address hook for mmap events, using given place to
// store relevant metadata (linked list membership etc).
//
// It does no memory allocation and is safe to be called from hooks of all kinds.
ATTRIBUTE_VISIBILITY_HIDDEN void HookMMapEvents(MappingHookSpace* place, MMapEventFn callback);

ATTRIBUTE_VISIBILITY_HIDDEN void HookMMapEventsWithBacktrace(MappingHookSpace* place, MMapEventFn callback,
                                                             MMapEventNeedBacktraceFn need_backtrace);

// UnHookMMapEvents undoes effect of HookMMapEvents. This one is also
// entirely safe to be called from out of anywhere. Including from
// inside MMapEventFn invokations.
//
// As noted on MappingHookSpace the place ***must not** be deallocated or
// reused for anything even after unhook. This requirement makes
// implementation simple enough and fits our internal usage use-case
// fine.
ATTRIBUTE_VISIBILITY_HIDDEN void UnHookMMapEvents(MappingHookSpace* place);

}  // namespace tcmalloc


#endif  // MMAP_HOOK_H
