Skip to content

Commit 377b346

Browse files
Hui ShiMarkus Grönlund
authored andcommitted
8264752: SIGFPE crash with option FlightRecorderOptions:threadbuffersize=30M
Reviewed-by: mgronlun
1 parent dc323a9 commit 377b346

File tree

5 files changed

+112
-13
lines changed

5 files changed

+112
-13
lines changed

src/hotspot/share/jfr/recorder/service/jfrMemorySizer.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -30,13 +30,15 @@
3030
const julong MAX_ADJUSTED_GLOBAL_BUFFER_SIZE = 1 * M;
3131
const julong MIN_ADJUSTED_GLOBAL_BUFFER_SIZE_CUTOFF = 512 * K;
3232
const julong MIN_GLOBAL_BUFFER_SIZE = 64 * K;
33+
const julong MAX_GLOBAL_BUFFER_SIZE = 2 * G;
3334
// implies at least 2 * MIN_GLOBAL_BUFFER SIZE
3435
const julong MIN_BUFFER_COUNT = 2;
3536
// MAX global buffer count open ended
3637
const julong DEFAULT_BUFFER_COUNT = 20;
3738
// MAX thread local buffer size == size of a single global buffer (runtime determined)
3839
// DEFAULT thread local buffer size = 2 * os page size (runtime determined)
3940
const julong MIN_THREAD_BUFFER_SIZE = 4 * K;
41+
const julong MAX_THREAD_BUFFER_SIZE = 2 * G;
4042
const julong MIN_MEMORY_SIZE = 1 * M;
4143
const julong DEFAULT_MEMORY_SIZE = 10 * M;
4244

@@ -305,6 +307,11 @@ static void thread_buffer_size(JfrMemoryOptions* options) {
305307
options->global_buffer_size = div_total_by_units(options->memory_size, options->buffer_count);
306308
if (options->thread_buffer_size > options->global_buffer_size) {
307309
options->global_buffer_size = options->thread_buffer_size;
310+
if (options->memory_size_configured) {
311+
options->buffer_count = div_total_by_per_unit(options->memory_size, options->global_buffer_size);
312+
} else {
313+
options->memory_size = multiply(options->global_buffer_size, options->buffer_count);
314+
}
308315
options->buffer_count = div_total_by_per_unit(options->memory_size, options->global_buffer_size);
309316
}
310317
assert(options->global_buffer_size >= options->thread_buffer_size, "invariant");
@@ -324,7 +331,8 @@ static void assert_post_condition(const JfrMemoryOptions* options) {
324331
assert(options->memory_size % os::vm_page_size() == 0, "invariant");
325332
assert(options->global_buffer_size % os::vm_page_size() == 0, "invariant");
326333
assert(options->thread_buffer_size % os::vm_page_size() == 0, "invariant");
327-
assert(options->buffer_count > 0, "invariant");
334+
assert(options->buffer_count >= MIN_BUFFER_COUNT, "invariant");
335+
assert(options->global_buffer_size >= options->thread_buffer_size, "invariant");
328336
}
329337
#endif
330338

@@ -429,6 +437,10 @@ bool JfrMemorySizer::adjust_options(JfrMemoryOptions* options) {
429437
default:
430438
default_size(options);
431439
}
440+
if (options->buffer_count < MIN_BUFFER_COUNT ||
441+
options->global_buffer_size < options->thread_buffer_size) {
442+
return false;
443+
}
432444
DEBUG_ONLY(assert_post_condition(options);)
433445
return true;
434446
}

src/hotspot/share/jfr/recorder/service/jfrMemorySizer.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,8 @@ extern const julong MIN_BUFFER_COUNT;
3232
extern const julong MIN_GLOBAL_BUFFER_SIZE;
3333
extern const julong MIN_MEMORY_SIZE;
3434
extern const julong MIN_THREAD_BUFFER_SIZE;
35+
extern const julong MAX_GLOBAL_BUFFER_SIZE;
36+
extern const julong MAX_THREAD_BUFFER_SIZE;
3537

3638
struct JfrMemoryOptions {
3739
julong memory_size;

src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -393,34 +393,40 @@ static julong divide_with_user_unit(Argument& memory_argument, julong value) {
393393
return value;
394394
}
395395

396-
template <typename Argument>
397-
static void log_lower_than_min_value(Argument& memory_argument, julong min_value) {
396+
static const char higher_than_msg[] = "This value is higher than the maximum size limited ";
397+
static const char lower_than_msg[] = "This value is lower than the minimum size required ";
398+
template <typename Argument, char const *msg>
399+
static void log_out_of_range_value(Argument& memory_argument, julong min_value) {
398400
if (memory_argument.value()._size != memory_argument.value()._val) {
399401
// has multiplier
400402
log_error(arguments) (
401-
"This value is lower than the minimum size required " JULONG_FORMAT "%c",
403+
"%s" JULONG_FORMAT "%c", msg,
402404
divide_with_user_unit(memory_argument, min_value),
403405
memory_argument.value()._multiplier);
404406
return;
405407
}
406408
log_error(arguments) (
407-
"This value is lower than the minimum size required " JULONG_FORMAT,
409+
"%s" JULONG_FORMAT, msg,
408410
divide_with_user_unit(memory_argument, min_value));
409411
}
410412

413+
static const char default_val_msg[] = "Value default for option ";
414+
static const char specified_val_msg[] = "Value specified for option ";
411415
template <typename Argument>
412416
static void log_set_value(Argument& memory_argument) {
413417
if (memory_argument.value()._size != memory_argument.value()._val) {
414418
// has multiplier
415419
log_error(arguments) (
416-
"Value specified for option \"%s\" is " JULONG_FORMAT "%c",
420+
"%s\"%s\" is " JULONG_FORMAT "%c",
421+
memory_argument.is_set() ? specified_val_msg: default_val_msg,
417422
memory_argument.name(),
418423
memory_argument.value()._val,
419424
memory_argument.value()._multiplier);
420425
return;
421426
}
422427
log_error(arguments) (
423-
"Value specified for option \"%s\" is " JULONG_FORMAT,
428+
"%s\"%s\" is " JULONG_FORMAT,
429+
memory_argument.is_set() ? specified_val_msg: default_val_msg,
424430
memory_argument.name(), memory_argument.value()._val);
425431
}
426432

@@ -541,6 +547,10 @@ static bool valid_memory_relations(const JfrMemoryOptions& options) {
541547
return false;
542548
}
543549
}
550+
} else if (options.thread_buffer_size_configured && options.memory_size_configured) {
551+
if (!ensure_first_gteq_second(_dcmd_memorysize, _dcmd_threadbuffersize)) {
552+
return false;
553+
}
544554
}
545555
return true;
546556
}
@@ -609,7 +619,7 @@ template <typename Argument>
609619
static bool ensure_gteq(Argument& memory_argument, const jlong value) {
610620
if ((jlong)memory_argument.value()._size < value) {
611621
log_set_value(memory_argument);
612-
log_lower_than_min_value(memory_argument, value);
622+
log_out_of_range_value<Argument, lower_than_msg>(memory_argument, value);
613623
return false;
614624
}
615625
return true;
@@ -640,14 +650,38 @@ static bool ensure_valid_minimum_sizes() {
640650
return true;
641651
}
642652

653+
template <typename Argument>
654+
static bool ensure_lteq(Argument& memory_argument, const jlong value) {
655+
if ((jlong)memory_argument.value()._size > value) {
656+
log_set_value(memory_argument);
657+
log_out_of_range_value<Argument, higher_than_msg>(memory_argument, value);
658+
return false;
659+
}
660+
return true;
661+
}
662+
663+
static bool ensure_valid_maximum_sizes() {
664+
if (_dcmd_globalbuffersize.is_set()) {
665+
if (!ensure_lteq(_dcmd_globalbuffersize, MAX_GLOBAL_BUFFER_SIZE)) {
666+
return false;
667+
}
668+
}
669+
if (_dcmd_threadbuffersize.is_set()) {
670+
if (!ensure_lteq(_dcmd_threadbuffersize, MAX_THREAD_BUFFER_SIZE)) {
671+
return false;
672+
}
673+
}
674+
return true;
675+
}
676+
643677
/**
644678
* Starting with the initial set of memory values from the user,
645679
* sanitize, enforce min/max rules and adjust to a set of consistent options.
646680
*
647681
* Adjusted memory sizes will be page aligned.
648682
*/
649683
bool JfrOptionSet::adjust_memory_options() {
650-
if (!ensure_valid_minimum_sizes()) {
684+
if (!ensure_valid_minimum_sizes() || !ensure_valid_maximum_sizes()) {
651685
return false;
652686
}
653687
JfrMemoryOptions options;
@@ -656,6 +690,24 @@ bool JfrOptionSet::adjust_memory_options() {
656690
return false;
657691
}
658692
if (!JfrMemorySizer::adjust_options(&options)) {
693+
if (options.buffer_count < MIN_BUFFER_COUNT || options.global_buffer_size < options.thread_buffer_size) {
694+
log_set_value(_dcmd_memorysize);
695+
log_set_value(_dcmd_globalbuffersize);
696+
log_error(arguments) ("%s \"%s\" is " JLONG_FORMAT,
697+
_dcmd_numglobalbuffers.is_set() ? specified_val_msg: default_val_msg,
698+
_dcmd_numglobalbuffers.name(), _dcmd_numglobalbuffers.value());
699+
log_set_value(_dcmd_threadbuffersize);
700+
if (options.buffer_count < MIN_BUFFER_COUNT) {
701+
log_error(arguments) ("numglobalbuffers " JULONG_FORMAT " is less than minimal value " JULONG_FORMAT,
702+
options.buffer_count, MIN_BUFFER_COUNT);
703+
log_error(arguments) ("Decrease globalbuffersize/threadbuffersize or increase memorysize");
704+
} else {
705+
log_error(arguments) ("globalbuffersize " JULONG_FORMAT " is less than threadbuffersize" JULONG_FORMAT,
706+
options.global_buffer_size, options.thread_buffer_size);
707+
log_error(arguments) ("Decrease globalbuffersize or increase memorysize or adjust global/threadbuffersize");
708+
}
709+
return false;
710+
}
659711
if (!check_for_ambiguity(_dcmd_memorysize, _dcmd_globalbuffersize, _dcmd_numglobalbuffers)) {
660712
return false;
661713
}

test/jdk/jdk/jfr/startupargs/TestBadOptionValues.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import jdk.test.lib.Asserts;
2727
import jdk.test.lib.process.OutputAnalyzer;
2828
import jdk.test.lib.process.ProcessTools;
29+
import sun.hotspot.WhiteBox;
2930

3031
/**
3132
* @test
@@ -37,7 +38,9 @@
3738
* java.management
3839
* jdk.jfr
3940
*
40-
* @run main jdk.jfr.startupargs.TestBadOptionValues
41+
* @build sun.hotspot.WhiteBox
42+
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
43+
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI jdk.jfr.startupargs.TestBadOptionValues
4144
*/
4245
public class TestBadOptionValues {
4346

@@ -119,6 +122,31 @@ public static void main(String[] args) throws Exception {
119122
test(START_FLIGHT_RECORDING, "Parsing error memory size value: invalid value",
120123
"maxsize=");
121124

125+
// globalbuffersize exceeds limit
126+
test(FLIGHT_RECORDER_OPTIONS, "This value is higher than the maximum size limit",
127+
"globalbuffersize=4G");
128+
129+
// threadbuffersize exceeds limit
130+
test(FLIGHT_RECORDER_OPTIONS, "This value is higher than the maximum size limit",
131+
"threadbuffersize=4G");
132+
133+
// computed numglobalbuffers smaller than MIN_BUFFER_COUNT
134+
test(FLIGHT_RECORDER_OPTIONS, "Decrease globalbuffersize/threadbuffersize or increase memorysize",
135+
"memorysize=1m,globalbuffersize=1m");
136+
137+
// memorysize smaller than threadbuffersize
138+
test(FLIGHT_RECORDER_OPTIONS, "The value for option \"threadbuffersize\" should not be larger than the value specified for option \"memorysize\"",
139+
"memorysize=1m,threadbuffersize=2m");
140+
141+
// computed globalbuffersize smaller than threadbuffersize
142+
// test is on when vm page isn't larger than 4K, avoiding both buffer sizes align to vm page size
143+
WhiteBox wb = WhiteBox.getWhiteBox();
144+
long smallPageSize = wb.getVMPageSize();
145+
if (smallPageSize <= 4096) {
146+
test(FLIGHT_RECORDER_OPTIONS, "Decrease globalbuffersize or increase memorysize or adjust global/threadbuffersize",
147+
"memorysize=1m,numglobalbuffers=256");
148+
}
149+
122150
test(FLIGHT_RECORDER_OPTIONS, "Parsing error memory size value: invalid value",
123151
"threadbuffersize=a",
124152
"globalbuffersize=G",

test/jdk/jdk/jfr/startupargs/TestMemoryOptions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,11 @@ public static void runTestCase(TestCase tc) throws Exception {
638638
tc.setGlobalBufferSizeTestParam(64, 'k');
639639
tc.setGlobalBufferCountTestParam(16, 'b');
640640
testCases.add(tc);
641+
642+
// threadbuffersize exceeds default memorysize
643+
tc = new TestCase("ThreadBufferSizeExceedMemorySize", false);
644+
tc.setThreadBufferSizeTestParam(30, 'm');
645+
testCases.add(tc);
641646
}
642647

643648
public static void main(String[] args) throws Exception {

0 commit comments

Comments
 (0)