runtime/dyninst/shm.c - systemtap
Global variables defined
Functions defined
Macros defined
Source code
/* -*- linux-c -*-
* Shared Memory Functions
* Copyright (C) 2012 Red Hat Inc.
*
* This file is part of systemtap, and is free software. You can
* redistribute it and/or modify it under the terms of the GNU General
* Public License (GPL); either version 2, or (at your option) any
* later version.
*/
#ifndef _STAPDYN_SHM_H_
#define _STAPDYN_SHM_H_
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static char _stp_shm_name[NAME_MAX] = { '\0' };
static int _stp_shm_fd = -1;
static int _stp_shm_page_size = -1;
static off_t _stp_shm_size = 0;
static off_t _stp_shm_allocated = 0;
static void *_stp_shm_base = NULL;
static const char *_stp_shm_init(void);
static int _stp_shm_connect(const char *name);
static void *_stp_shm_alloc(size_t size);
static void *_stp_shm_zalloc(size_t size);
static void _stp_shm_free(void *ptr);
static void _stp_shm_finalize(void);
static void _stp_shm_destroy(void);
#ifdef DEBUG_SHM
#define shm_dbug(fmt, args...) _stp_dbug(__FUNCTION__, __LINE__, fmt, ##args)
#else
#define shm_dbug(fmt, args...)
#endif
// Create and initialize the shared memory for this module.
static const char *_stp_shm_init(void)
{
char name[NAME_MAX];
long page_size;
int fd;
void *base;
// If we already have shared memory, carry on...
if (_stp_shm_base)
return _stp_shm_name;
// Find the increment to use in growing our memory size. Since this is
// used in mmap, only multiples of the page size make sense.
page_size = sysconf(_SC_PAGESIZE);
if (page_size < 1)
return NULL;
// Create a unique name for our shared memory. It need not be anything
// secret because shm_open's flags will ensure security.
(void) snprintf(name, NAME_MAX, "/stapdyn.%d", getpid());
// Create the actual share memory. The O_EXCL saves us from the
// possible mktemp race. Only USR file mode bits are added, because
// that's all that ptrace will allow to attach anyway. (Unless you're
// root, but that deserves much larger consideration.)
fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fd < 0)
return NULL;
// Start the shared memory sized with just one unit.
if (ftruncate(fd, page_size) < 0)
goto err_fd;
// Now finally map it into this process.
base = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (base == MAP_FAILED)
goto err_fd;
// We're done; set globals and go home!
strlcpy(_stp_shm_name, name, sizeof(_stp_shm_name));
_stp_shm_fd = fd;
_stp_shm_page_size = page_size;
_stp_shm_size = page_size;
_stp_shm_allocated = 0;
_stp_shm_base = base;
shm_dbug("initialized %s @ %p", _stp_shm_name, _stp_shm_base);
return _stp_shm_name;
err_fd:
close(fd);
shm_unlink(_stp_shm_name);
return NULL;
}
static int _stp_shm_connect(const char *name)
{
int rc = 0, fd = -1;
struct stat st;
void *base;
// NB: since we're just connecting, we won't set _stp_shm_name,
// so _stp_shm_destroy won't try to unlink it from this process.
// Open the pre-existing shared memory
fd = shm_open(name, O_RDWR, 0);
if (fd < 0)
rc = fd;
// Find out the shared memory's size.
// (This implies that the main process is done allocating.)
if (rc == 0)
rc = fstat(fd, &st);
// Map it into this process.
if (rc == 0) {
base = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (base == MAP_FAILED)
rc = -1;
else
_stp_shm_base = base;
}
if (fd >= 0)
close(fd);
if (rc == 0)
shm_dbug("connected pid %d to %s @ %p", getpid(), name, _stp_shm_base);
return rc;
}
// Allocate space from shared memory
static void *_stp_shm_alloc(size_t size)
{
void *alloc;
// We're not initialized or already finalized.
// Either way, we're not taking new requests.
if (_stp_shm_fd < 0)
return NULL;
// Round up to 8-byte aligned sizes, just to be a little safer
size = (size + 7) & ~7;
if (size <= 0)
return NULL; // either 0 requested or overflow
// Check if more memory is needed.
if (_stp_shm_size - _stp_shm_allocated < size) {
void *new_base;
// Round up to the nearest page size
off_t new_size = _stp_shm_allocated + size;
new_size += _stp_shm_page_size - 1;
new_size /= _stp_shm_page_size;
new_size *= _stp_shm_page_size;
if (new_size < _stp_shm_allocated ||
new_size - _stp_shm_allocated < size)
return NULL; // math overflow?
// Try to resize the underlying file.
if (ftruncate(_stp_shm_fd, new_size) < 0)
return NULL;
// Try to remap the address in memory.
new_base = mremap(_stp_shm_base, _stp_shm_size,
new_size, MREMAP_MAYMOVE);
if (new_base == MAP_FAILED)
return NULL;
// Update globals
_stp_shm_size = new_size;
_stp_shm_base = new_base;
}
// Finally return some memory.
alloc = _stp_shm_base + _stp_shm_allocated;
_stp_shm_allocated += size;
return alloc;
}
// Allocate zeroed space from shared memory
static void *_stp_shm_zalloc(size_t size)
{
void *ptr = _stp_shm_alloc(size);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
static void _stp_shm_free(void *ptr __attribute__((unused)))
{
// NO-OP; we don't try to reclaim individual pieces.
}
// Signal that we're done allocating in shared memory.
// It won't be allowed to grow or move from here on out.
static void _stp_shm_finalize(void)
{
if (_stp_shm_fd >= 0) {
close(_stp_shm_fd);
_stp_shm_fd = -1;
}
shm_dbug("mapped %" PRIi64 " bytes @ %p, used %" PRIi64, _stp_shm_size,
_stp_shm_base, _stp_shm_allocated);
}
// Tear down everything we created... *sniff*
// NB: Make sure not to reference any memory within after this!
// (Other processes may still have their own mmap reference though.)
// Don't destroy things in shm that may still be used by other processes!
static void _stp_shm_destroy(void)
{
if (_stp_shm_base) {
munmap(_stp_shm_base, _stp_shm_size);
_stp_shm_base = NULL;
_stp_shm_size = 0;
_stp_shm_allocated = 0;
}
if (_stp_shm_fd >= 0) {
close(_stp_shm_fd);
_stp_shm_fd = -1;
}
if (_stp_shm_name[0]) {
shm_unlink(_stp_shm_name);
_stp_shm_name[0] = '\0';
}
}
#endif /* _STAPDYN_SHM_H_ */