blob: 210a642b674ebb379bae0324b683acf4ec61fb26 [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.
#include "common/init.h"
#include <gperftools/heap-profiler.h>
#include <gperftools/malloc_extension.h>
#include "common/logging.h"
#include "common/status.h"
#include "exec/kudu-util.h"
#include "exprs/expr.h"
#include "exprs/timezone_db.h"
#include "gutil/atomicops.h"
#include "rpc/authentication.h"
#include "rpc/thrift-util.h"
#include "runtime/decimal-value.h"
#include "runtime/exec-env.h"
#include "runtime/hdfs-fs-cache.h"
#include "runtime/lib-cache.h"
#include "runtime/mem-tracker.h"
#include "runtime/timestamp-parse-util.h"
#include "util/cpu-info.h"
#include "util/debug-util.h"
#include "util/decimal-util.h"
#include "util/disk-info.h"
#include "util/logging-support.h"
#include "util/mem-info.h"
#include "util/minidump.h"
#include "util/network-util.h"
#include "util/openssl-util.h"
#include "util/os-info.h"
#include "util/pretty-printer.h"
#include "util/redactor.h"
#include "util/test-info.h"
#include "util/thread.h"
#include "util/time.h"
#include "common/names.h"
using namespace impala;
DECLARE_string(hostname);
DECLARE_string(redaction_rules_file);
// TODO: renamed this to be more generic when we have a good CM release to do so.
DECLARE_int32(logbufsecs);
DECLARE_string(heap_profile_dir);
DECLARE_bool(enable_process_lifetime_heap_profiling);
DEFINE_int32(max_log_files, 10, "Maximum number of log files to retain per severity "
"level. The most recent log files are retained. If set to 0, all log files are "
"retained.");
DEFINE_int64(pause_monitor_sleep_time_ms, 500, "Sleep time in milliseconds for "
"pause monitor thread.");
DEFINE_int64(pause_monitor_warn_threshold_ms, 10000, "If the pause monitor sleeps "
"more than this time period, a warning is logged. If set to 0 or less, pause monitor"
" is disabled.");
// Defined by glog. This allows users to specify the log level using a glob. For
// example -vmodule=*scanner*=3 would enable full logging for scanners. If redaction
// is enabled, this option won't be allowed because some logging dumps table data
// in ways the authors of redaction rules can't anticipate.
DECLARE_string(vmodule);
// tcmalloc will hold on to freed memory. We will periodically release the memory back
// to the OS if the extra memory is too high. If the memory used by the application
// is less than this fraction of the total reserved memory, free it back to the OS.
static const float TCMALLOC_RELEASE_FREE_MEMORY_FRACTION = 0.5f;
using std::string;
// Maintenance thread that runs periodically. It does a few things:
// 1) flushes glog every logbufsecs sec. glog flushes the log file only if
// logbufsecs has passed since the previous flush when a new log is written. That means
// that on a quiet system, logs will be buffered indefinitely.
// 2) checks that tcmalloc has not left too much memory in its pageheap
static scoped_ptr<impala::Thread> maintenance_thread;
// A pause monitor thread to monitor process pauses in impala daemons. The thread sleeps
// for a short interval of time (THREAD_SLEEP_TIME_MS), wakes up and calculates the actual
// time slept. If that exceeds PAUSE_WARN_THRESHOLD_MS, a warning is logged.
static scoped_ptr<impala::Thread> pause_monitor;
[[noreturn]] static void MaintenanceThread() {
while (true) {
sleep(FLAGS_logbufsecs);
google::FlushLogFiles(google::GLOG_INFO);
// Tests don't need to run the maintenance thread. It causes issues when
// on teardown.
if (impala::TestInfo::is_test()) continue;
#ifndef ADDRESS_SANITIZER
// Required to ensure memory gets released back to the OS, even if tcmalloc doesn't do
// it for us. This is because tcmalloc releases memory based on the
// TCMALLOC_RELEASE_RATE property, which is not actually a rate but a divisor based
// on the number of blocks that have been deleted. When tcmalloc does decide to
// release memory, it removes a single span from the PageHeap. This means there are
// certain allocation patterns that can lead to OOM due to not enough memory being
// released by tcmalloc, even when that memory is no longer being used.
// One example is continually resizing a vector which results in many allocations.
// Even after the vector goes out of scope, all the memory will not be released
// unless there are enough other deletions that are occurring in the system.
// This can eventually lead to OOM/crashes (see IMPALA-818).
// See: http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc.html#runtime
size_t bytes_used = 0;
size_t bytes_in_pageheap = 0;
MallocExtension::instance()->GetNumericProperty(
"generic.current_allocated_bytes", &bytes_used);
MallocExtension::instance()->GetNumericProperty(
"generic.heap_size", &bytes_in_pageheap);
if (bytes_used < bytes_in_pageheap * TCMALLOC_RELEASE_FREE_MEMORY_FRACTION) {
MallocExtension::instance()->ReleaseFreeMemory();
}
// When using tcmalloc, the process limit as measured by our trackers will
// be out of sync with the process usage. Update the process tracker periodically.
impala::ExecEnv* env = impala::ExecEnv::GetInstance();
if (env != NULL && env->process_mem_tracker() != NULL) {
env->process_mem_tracker()->RefreshConsumptionFromMetric();
}
#endif
// TODO: we should also update the process mem tracker with the reported JVM
// mem usage.
// Check for log rotation in every interval of the maintenance thread
impala::CheckAndRotateLogFiles(FLAGS_max_log_files);
}
}
static void PauseMonitorLoop() {
if (FLAGS_pause_monitor_warn_threshold_ms <= 0) return;
int64_t time_before_sleep = MonotonicMillis();
while (true) {
SleepForMs(FLAGS_pause_monitor_sleep_time_ms);
int64_t sleep_time = MonotonicMillis() - time_before_sleep;
time_before_sleep += sleep_time;
if (sleep_time > FLAGS_pause_monitor_warn_threshold_ms) {
LOG(WARNING) << "A process pause was detected for approximately " <<
PrettyPrinter::Print(sleep_time, TUnit::TIME_MS);
}
}
}
void impala::InitCommonRuntime(int argc, char** argv, bool init_jvm,
TestInfo::Mode test_mode) {
CpuInfo::Init();
DiskInfo::Init();
MemInfo::Init();
OsInfo::Init();
DecimalUtil::InitMaxUnscaledDecimal16();
TestInfo::Init(test_mode);
// Verify CPU meets the minimum requirements before calling InitGoogleLoggingSafe()
// which might use SSSE3 instructions (see IMPALA-160).
CpuInfo::VerifyCpuRequirements();
// Set the default hostname. The user can override this with the hostname flag.
GetHostname(&FLAGS_hostname);
google::SetVersionString(impala::GetBuildVersion());
google::ParseCommandLineFlags(&argc, &argv, true);
if (!FLAGS_redaction_rules_file.empty()) {
if (VLOG_ROW_IS_ON || !FLAGS_vmodule.empty()) {
CLEAN_EXIT_WITH_ERROR("Redaction cannot be used in combination with log level 3 or "
"higher or the -vmodule option because these log levels may log data in "
"ways redaction rules may not anticipate.");
}
const string& error_message = SetRedactionRulesFromFile(FLAGS_redaction_rules_file);
if (!error_message.empty()) CLEAN_EXIT_WITH_ERROR(error_message);
}
impala::InitGoogleLoggingSafe(argv[0]);
// Breakpad needs flags and logging to initialize.
ABORT_IF_ERROR(RegisterMinidump(argv[0]));
AtomicOps_x86CPUFeaturesInit();
impala::InitThreading();
impala::TimestampParser::Init();
impala::SeedOpenSSLRNG();
ABORT_IF_ERROR(impala::TimezoneDatabase::Initialize());
ABORT_IF_ERROR(impala::InitAuth(argv[0]));
// Initialize maintenance_thread after InitGoogleLoggingSafe and InitThreading.
maintenance_thread.reset(
new Thread("common", "maintenance-thread", &MaintenanceThread));
pause_monitor.reset(
new Thread("common", "pause-monitor", &PauseMonitorLoop));
LOG(INFO) << impala::GetVersionString();
LOG(INFO) << "Using hostname: " << FLAGS_hostname;
impala::LogCommandLineFlags();
InitThriftLogging();
LOG(INFO) << CpuInfo::DebugString();
LOG(INFO) << DiskInfo::DebugString();
LOG(INFO) << MemInfo::DebugString();
LOG(INFO) << OsInfo::DebugString();
LOG(INFO) << "Process ID: " << getpid();
// Required for the FE's Catalog
impala::LibCache::Init();
Status fs_cache_init_status = impala::HdfsFsCache::Init();
if (!fs_cache_init_status.ok()) CLEAN_EXIT_WITH_ERROR(fs_cache_init_status.GetDetail());
if (init_jvm) {
ABORT_IF_ERROR(JniUtil::Init());
InitJvmLoggingSupport();
}
if (argc == -1) {
// Should not be called. We need BuiltinsInit() so the builtin symbols are
// not stripped.
DCHECK(false);
Expr::InitBuiltinsDummy();
}
if (impala::KuduIsAvailable()) impala::InitKuduLogging();
#ifndef ADDRESS_SANITIZER
// tcmalloc and address sanitizer can not be used together
if (FLAGS_enable_process_lifetime_heap_profiling) {
HeapProfilerStart(FLAGS_heap_profile_dir.c_str());
}
#endif
}