blob: 60a3c58d80afc65d2600f960562a17c60d36c1af [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* testutils.c
* Implementation of testing utilities.
*
*/
#include <sys/time.h>
#include <sys/resource.h>
#include "postgres.h"
#include "storage/lock.h"
#include "storage/lwlock.h"
#include "utils/elog.h"
#include "utils/testutils.h"
#ifdef USE_TEST_UTILS
#define MAX_FRAME_DEPTH 64
#define USECS_IN_MSEC 1000
#define MSECS_IN_SEC 1000
/* holds return addresses of frames in stack */
static void *stackAddressesLastCFI[MAX_FRAME_DEPTH];
/* stack depth */
static uint32 stackDepthLastCFI = 0;
/* elapsed user time */
static struct timeval userTimeLastCFI;
/* static functions */
static int32 timeElapsedMs(struct timeval *tvLast, struct timeval *tvFirst);
static bool IsValidReportLevel(int level);
/*
* Reset time slice
*/
void
TimeSliceReset()
{
struct rusage ru;
if (0 != getrusage(RUSAGE_SELF, &ru))
{
elog(ERROR, "Time slicer: Failed to retrieve user time");
}
userTimeLastCFI = ru.ru_utime;
stackDepthLastCFI = gp_backtrace(stackAddressesLastCFI, MAX_FRAME_DEPTH);
}
/*
* Check if time slice since last check-for-interrupts (CFI) has been exceeded
*/
void
TimeSliceCheck(const char *file, int line)
{
Assert(gp_test_time_slice);
Assert(IsValidReportLevel(gp_test_time_slice_report_level));
struct rusage ru;
int32 elapsedMs = 0;
/* CFI is disabled inside critical sections */
if (0 != InterruptHoldoffCount || 0 != CritSectionCount)
{
return;
}
/* get current user time */
if (getrusage(RUSAGE_SELF, &ru) != 0)
{
elog(ERROR, "Time slicer: Failed to retrieve user time");
}
elapsedMs = timeElapsedMs(&ru.ru_utime, &userTimeLastCFI);
Assert(0 <= elapsedMs);
/* check elapsed time since last CFI */
if (gp_test_time_slice_interval < elapsedMs)
{
void *stackAddressesCurrent[MAX_FRAME_DEPTH];
uint32 stackDepthCurrent = gp_backtrace(stackAddressesCurrent, MAX_FRAME_DEPTH);
char *stackTraceLastCFI = gp_stacktrace(stackAddressesLastCFI, stackDepthLastCFI);
char *stackTraceCurrent = gp_stacktrace(stackAddressesCurrent, stackDepthCurrent);
/* report time slice violation error */
ereport(gp_test_time_slice_report_level,
(errmsg("Time slice of %d ms exceeded at (%s:%d), last CFI before %d ms.\n"
"Stack trace of last CFI:\n%s\n"
"Current stack trace:\n%s\n",
gp_test_time_slice_interval,
file,
line,
elapsedMs,
stackTraceLastCFI,
stackTraceCurrent)));
}
/* reset time slice */
userTimeLastCFI = ru.ru_utime;
stackDepthLastCFI = gp_backtrace(stackAddressesLastCFI, MAX_FRAME_DEPTH);
}
/*
* Get elapsed time between two timestamps in ms
*/
static int32
timeElapsedMs(struct timeval *tvLast, struct timeval *tvFirst)
{
return (tvLast->tv_sec - tvFirst->tv_sec) * MSECS_IN_SEC +
(tvLast->tv_usec - tvFirst->tv_usec) / USECS_IN_MSEC;
}
/*
* Check if report level is valid
*/
static bool
IsValidReportLevel(int level)
{
return (level >= NOTICE && level <= PANIC);
}
#ifdef USE_TEST_UTILS_X86
/*
* check if lightweight lock(s) are held;
* print stack trace where lock(s) got acquired and error out;
*/
void
LWLockHeldDetect(const void *pv, int lockmode)
{
Assert(gp_test_deadlock_hazard);
Assert(IsValidReportLevel(gp_test_deadlock_hazard_report_level));
const LOCKTAG *locktag = (const LOCKTAG *) pv;
if (0 < LWLocksHeld())
{
void *stackAddressesCurrent[MAX_FRAME_DEPTH];
uint32 stackDepthCurrent = gp_backtrace(stackAddressesCurrent, MAX_FRAME_DEPTH);
char *stackTraceCurrent = gp_stacktrace(stackAddressesCurrent, stackDepthCurrent);
const char *stackTraces = LWLocksHeldStackTraces();
Assert(NULL != stackTraces);
/* report time slice violation error */
ereport(gp_test_deadlock_hazard_report_level,
(errmsg("Attempting to acquire database lock (%s:%d:%d:%d:%d) while holding lightweight lock (%d:%p).\n"
"Stack trace(s) where lightweight lock(s) got acquired:\n%s\n"
"Current stack trace:\n%s\n",
GetLockmodeName(locktag->locktag_lockmethodid, lockmode),
locktag->locktag_field1,
locktag->locktag_field2,
locktag->locktag_field3,
locktag->locktag_field4,
LWLockHeldLatestId(),
LWLockHeldLatestCaller(),
stackTraces,
stackTraceCurrent)));
}
}
#endif /* USE_TEST_UTILS_X86 */
#endif /* USE_TEST_UTILS */
/* EOF */