Path: blob/master/src/hotspot/share/logging/logOutput.cpp
40930 views
/*1* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/23#include "precompiled.hpp"24#include "jvm.h"25#include "logging/log.hpp"26#include "logging/logFileStreamOutput.hpp"27#include "logging/logOutput.hpp"28#include "logging/logSelection.hpp"29#include "logging/logTagSet.hpp"30#include "memory/allocation.inline.hpp"31#include "runtime/mutexLocker.hpp"32#include "runtime/os.hpp"3334LogOutput::~LogOutput() {35os::free(_config_string);36}3738void LogOutput::describe(outputStream *out) {39out->print("%s ", name());40out->print_raw(config_string()); // raw printed because length might exceed O_BUFLEN4142bool has_decorator = false;43char delimiter = ' ';44for (size_t d = 0; d < LogDecorators::Count; d++) {45LogDecorators::Decorator decorator = static_cast<LogDecorators::Decorator>(d);46if (decorators().is_decorator(decorator)) {47has_decorator = true;48out->print("%c%s", delimiter, LogDecorators::name(decorator));49delimiter = ',';50}51}52if (!has_decorator) {53out->print(" none");54}55}5657void LogOutput::set_config_string(const char* string) {58os::free(_config_string);59_config_string = os::strdup(string, mtLogging);60_config_string_buffer_size = strlen(_config_string) + 1;61}6263void LogOutput::add_to_config_string(const LogSelection& selection) {64if (_config_string_buffer_size < InitialConfigBufferSize) {65_config_string_buffer_size = InitialConfigBufferSize;66_config_string = REALLOC_C_HEAP_ARRAY(char, _config_string, _config_string_buffer_size, mtLogging);67}6869size_t offset = strlen(_config_string);70if (offset > 0) {71// Add commas in-between tag and level combinations in the config string72_config_string[offset++] = ',';73}7475for (;;) {76int ret = selection.describe(_config_string + offset,77_config_string_buffer_size - offset);78if (ret == -1) {79// Double the buffer size and retry80_config_string_buffer_size *= 2;81_config_string = REALLOC_C_HEAP_ARRAY(char, _config_string, _config_string_buffer_size, mtLogging);82continue;83}84break;85};86}878889static int tag_cmp(const void *a, const void *b) {90return static_cast<const LogTagType*>(a) - static_cast<const LogTagType*>(b);91}9293static void sort_tags(LogTagType tags[LogTag::MaxTags]) {94size_t ntags = 0;95while (tags[ntags] != LogTag::__NO_TAG) {96ntags++;97}98qsort(tags, ntags, sizeof(*tags), tag_cmp);99}100101static const size_t MaxSubsets = 1 << LogTag::MaxTags;102103// Fill result with all possible subsets of the given tag set. Empty set not included.104// For example, if tags is {gc, heap} then the result is {{gc}, {heap}, {gc, heap}}.105// (Arguments with default values are intended exclusively for recursive calls.)106static void generate_all_subsets_of(LogTagType result[MaxSubsets][LogTag::MaxTags],107size_t* result_size,108const LogTagType tags[LogTag::MaxTags],109LogTagType subset[LogTag::MaxTags] = NULL,110const size_t subset_size = 0,111const size_t depth = 0) {112assert(subset_size <= LogTag::MaxTags, "subset must never have more than MaxTags tags");113assert(depth <= LogTag::MaxTags, "recursion depth overflow");114115if (subset == NULL) {116assert(*result_size == 0, "outer (non-recursive) call expects result_size to be 0");117// Make subset the first element in the result array initially118subset = result[0];119}120assert((void*) subset >= &result[0] && (void*) subset <= &result[MaxSubsets - 1],121"subset should always point to element in result");122123if (depth == LogTag::MaxTags || tags[depth] == LogTag::__NO_TAG) {124if (subset_size == 0) {125// Ignore empty subset126return;127}128if (subset_size != LogTag::MaxTags) {129subset[subset_size] = LogTag::__NO_TAG;130}131assert(*result_size < MaxSubsets, "subsets overflow");132*result_size += 1;133134// Bump subset and copy over current state135memcpy(result[*result_size], subset, sizeof(*subset) * LogTag::MaxTags);136subset = result[*result_size];137return;138}139140// Recurse, excluding the tag of the current depth141generate_all_subsets_of(result, result_size, tags, subset, subset_size, depth + 1);142// ... and with it included143subset[subset_size] = tags[depth];144generate_all_subsets_of(result, result_size, tags, subset, subset_size + 1, depth + 1);145}146147// Generate all possible selections (for the given level) based on the given tag set,148// and add them to the selections array (growing it as necessary).149static void add_selections(LogSelection** selections,150size_t* n_selections,151size_t* selections_cap,152const LogTagSet& tagset,153LogLevelType level) {154LogTagType tags[LogTag::MaxTags] = { LogTag::__NO_TAG };155for (size_t i = 0; i < tagset.ntags(); i++) {156tags[i] = tagset.tag(i);157}158159size_t n_subsets = 0;160LogTagType subsets[MaxSubsets][LogTag::MaxTags];161generate_all_subsets_of(subsets, &n_subsets, tags);162163for (size_t i = 0; i < n_subsets; i++) {164// Always keep tags sorted165sort_tags(subsets[i]);166167// Ignore subsets already represented in selections168bool unique = true;169for (size_t sel = 0; sel < *n_selections; sel++) {170if (level == (*selections)[sel].level() && (*selections)[sel].consists_of(subsets[i])) {171unique = false;172break;173}174}175if (!unique) {176continue;177}178179LogSelection exact_selection(subsets[i], false, level);180LogSelection wildcard_selection(subsets[i], true, level);181182// Check if the two selections match any tag sets183bool wildcard_match = false;184bool exact_match = false;185for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {186if (!wildcard_selection.selects(*ts)) {187continue;188}189190wildcard_match = true;191if (exact_selection.selects(*ts)) {192exact_match = true;193}194if (exact_match) {195break;196}197}198199if (!wildcard_match && !exact_match) {200continue;201}202203// Ensure there's enough room for both wildcard_match and exact_match204if (*n_selections + 2 > *selections_cap) {205*selections_cap *= 2;206*selections = REALLOC_C_HEAP_ARRAY(LogSelection, *selections, *selections_cap, mtLogging);207}208209// Add found matching selections to the result array210if (exact_match) {211(*selections)[(*n_selections)++] = exact_selection;212}213if (wildcard_match) {214(*selections)[(*n_selections)++] = wildcard_selection;215}216}217}218219void LogOutput::update_config_string(const size_t on_level[LogLevel::Count]) {220// Find the most common level (MCL)221LogLevelType mcl = LogLevel::Off;222size_t max = on_level[LogLevel::Off];223for (LogLevelType l = LogLevel::First; l <= LogLevel::Last; l = static_cast<LogLevelType>(l + 1)) {224if (on_level[l] > max) {225mcl = l;226max = on_level[l];227}228}229230// Always let the first part of each output's config string be "all=<MCL>"231{232char buf[64];233jio_snprintf(buf, sizeof(buf), "all=%s", LogLevel::name(mcl));234set_config_string(buf);235}236237// If there are no deviating tag sets, we're done238size_t deviating_tagsets = LogTagSet::ntagsets() - max;239if (deviating_tagsets == 0) {240return;241}242243size_t n_selections = 0;244size_t selections_cap = 4 * MaxSubsets; // Start with some reasonably large initial capacity245LogSelection* selections = NEW_C_HEAP_ARRAY(LogSelection, selections_cap, mtLogging);246247size_t n_deviates = 0;248const LogTagSet** deviates = NEW_C_HEAP_ARRAY(const LogTagSet*, deviating_tagsets, mtLogging);249250// Generate all possible selections involving the deviating tag sets251for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {252LogLevelType level = ts->level_for(this);253if (level == mcl) {254continue;255}256deviates[n_deviates++] = ts;257add_selections(&selections, &n_selections, &selections_cap, *ts, level);258}259260// Reduce deviates greedily, using the "best" selection at each step to reduce the number of deviating tag sets261while (n_deviates > 0) {262size_t prev_deviates = n_deviates;263int max_score = 0;264265guarantee(n_selections > 0, "Cannot find maximal selection.");266const LogSelection* best_selection = &selections[0];267for (size_t i = 0; i < n_selections; i++) {268269// Give the selection a score based on how many deviating tag sets it selects (with correct level)270int score = 0;271for (size_t d = 0; d < n_deviates; d++) {272if (selections[i].selects(*deviates[d]) && deviates[d]->level_for(this) == selections[i].level()) {273score++;274}275}276277// Ignore selections with lower score than the current best even before subtracting mismatched selections278if (score < max_score) {279continue;280}281282// Subtract from the score the number of tag sets it selects with an incorrect level283for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {284if (selections[i].selects(*ts) && ts->level_for(this) != selections[i].level()) {285score--;286}287}288289// Pick the selection with the best score, or in the case of a tie, the one with fewest tags290if (score > max_score ||291(score == max_score && selections[i].ntags() < best_selection->ntags())) {292max_score = score;293best_selection = &selections[i];294}295}296297add_to_config_string(*best_selection);298299// Remove all deviates that this selection covered300for (size_t d = 0; d < n_deviates;) {301if (deviates[d]->level_for(this) == best_selection->level() && best_selection->selects(*deviates[d])) {302deviates[d] = deviates[--n_deviates];303continue;304}305d++;306}307308// Add back any new deviates that this selection added (no array growth since removed > added)309for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {310if (ts->level_for(this) == best_selection->level() || !best_selection->selects(*ts)) {311continue;312}313314bool already_added = false;315for (size_t dev = 0; dev < n_deviates; dev++) {316if (deviates[dev] == ts) {317already_added = true;318break;319}320}321if (already_added) {322continue;323}324325deviates[n_deviates++] = ts;326}327328// Reset the selections and generate a new ones based on the updated deviating tag sets329n_selections = 0;330for (size_t d = 0; d < n_deviates; d++) {331add_selections(&selections, &n_selections, &selections_cap, *deviates[d], deviates[d]->level_for(this));332}333334assert(n_deviates < deviating_tagsets, "deviating tag set array overflow");335assert(prev_deviates > n_deviates, "number of deviating tag sets must never grow");336}337FREE_C_HEAP_ARRAY(LogTagSet*, deviates);338FREE_C_HEAP_ARRAY(Selection, selections);339}340341342343