Skip to content

Commit 138aba7

Browse files
authored
improve --heap-size-hint arg handling (#48050)
Previously, `--heap-size-hint` would silently ignore many flavors of "bad" input, parsing things like "3PB" as 3 bytes. This change makes it significantly less permissive, erroring unless it can parse a number (still relying on the C `sscanf` `%Lf` format specifier there) with an optional unit (case-insensitive, either with or without the trailing `b`). Also test it.
1 parent 9839aa3 commit 138aba7

File tree

4 files changed

+73
-42
lines changed

4 files changed

+73
-42
lines changed

doc/man/julia.1

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,10 @@ fallbacks to the latest compatible BugReporting.jl if not. For more information,
228228
--bug-report=help.
229229

230230
.TP
231-
--heap-size-hint=<size>
232-
Forces garbage collection if memory usage is higher than that value. The value can be
233-
specified in units of K, M, G, T, or % of physical memory.
231+
--heap-size-hint=<value>
232+
Forces garbage collection if memory usage is higher than the given value.
233+
The value may be specified as a number of bytes, optionally in units of
234+
KB, MB, GB, or TB, or as a percentage of physical memory with %.
234235

235236
.TP
236237
--compile={yes*|no|all|min}

doc/src/manual/command-line-interface.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ The following is a complete list of command-line switches available when launchi
213213
|`--output-incremental={yes\|no*}` |Generate an incremental output file (rather than complete)|
214214
|`--trace-compile={stderr,name}` |Print precompile statements for methods compiled during execution or save to a path|
215215
|`--image-codegen` |Force generate code in imaging mode|
216-
|`--heap-size-hint=<size>` |Forces garbage collection if memory usage is higher than that value. The memory hint might be specified in megabytes (e.g., 500M) or gigabytes (e.g., 1G)|
216+
|`--heap-size-hint=<size>` |Forces garbage collection if memory usage is higher than the given value. The value may be specified as a number of bytes, optionally in units of KB, MB, GB, or TB, or as a percentage of physical memory with %.|
217217

218218

219219
!!! compat "Julia 1.1"

src/jloptions.c

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <unistd.h>
1010
#include <getopt.h>
11+
1112
#include "julia_assert.h"
1213

1314
#ifdef _OS_WINDOWS_
@@ -18,6 +19,15 @@ char *shlib_ext = ".dylib";
1819
char *shlib_ext = ".so";
1920
#endif
2021

22+
/* This simple hand-crafted tolower exists to avoid locale-dependent effects in
23+
* behaviors (and utf8proc_tolower wasn't linking properly on all platforms) */
24+
static char ascii_tolower(char c)
25+
{
26+
if ('A' <= c && c <= 'Z')
27+
return c - 'A' + 'a';
28+
return c;
29+
}
30+
2131
static const char system_image_path[256] = "\0" JL_SYSTEM_IMAGE_PATH;
2232
JL_DLLEXPORT const char *jl_get_default_sysimg_path(void)
2333
{
@@ -187,9 +197,9 @@ static const char opts[] =
187197
" expressions. It first tries to use BugReporting.jl installed in current environment and\n"
188198
" fallbacks to the latest compatible BugReporting.jl if not. For more information, see\n"
189199
" --bug-report=help.\n\n"
190-
191-
" --heap-size-hint=<size> Forces garbage collection if memory usage is higher than that value.\n"
192-
" The value can be specified in units of K, M, G, T, or % of physical memory.\n\n"
200+
" --heap-size-hint=<size> Forces garbage collection if memory usage is higher than the given value.\n"
201+
" The value may be specified as a number of bytes, optionally in units of\n"
202+
" KB, MB, GB, or TB, or as a percentage of physical memory with %.\n\n"
193203
;
194204

195205
static const char opts_hidden[] =
@@ -801,43 +811,47 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
801811
break;
802812
case opt_heap_size_hint:
803813
if (optarg != NULL) {
804-
size_t endof = strlen(optarg);
805814
long double value = 0.0;
806-
if (sscanf(optarg, "%Lf", &value) == 1 && value > 1e-7) {
807-
char unit = optarg[endof - 1];
808-
uint64_t multiplier = 1ull;
809-
switch (unit) {
810-
case 'k':
811-
case 'K':
812-
multiplier <<= 10;
813-
break;
814-
case 'm':
815-
case 'M':
816-
multiplier <<= 20;
817-
break;
818-
case 'g':
819-
case 'G':
820-
multiplier <<= 30;
821-
break;
822-
case 't':
823-
case 'T':
824-
multiplier <<= 40;
825-
break;
826-
case '%':
827-
if (value > 100)
828-
jl_errorf("julia: invalid percentage specified in --heap-size-hint");
829-
uint64_t mem = uv_get_total_memory();
830-
uint64_t cmem = uv_get_constrained_memory();
831-
if (cmem > 0 && cmem < mem)
832-
mem = cmem;
833-
multiplier = mem/100;
834-
break;
835-
default:
836-
jl_errorf("julia: invalid unit specified in --heap-size-hint");
837-
break;
838-
}
839-
jl_options.heap_size_hint = (uint64_t)(value * multiplier);
815+
char unit[4] = {0};
816+
int nparsed = sscanf(optarg, "%Lf%3s", &value, unit);
817+
if (nparsed == 0 || strlen(unit) > 2 || (strlen(unit) == 2 && ascii_tolower(unit[1]) != 'b')) {
818+
jl_errorf("julia: invalid argument to --heap-size-hint (%s)", optarg);
819+
}
820+
uint64_t multiplier = 1ull;
821+
switch (ascii_tolower(unit[0])) {
822+
case '\0':
823+
case 'b':
824+
break;
825+
case 'k':
826+
multiplier <<= 10;
827+
break;
828+
case 'm':
829+
multiplier <<= 20;
830+
break;
831+
case 'g':
832+
multiplier <<= 30;
833+
break;
834+
case 't':
835+
multiplier <<= 40;
836+
break;
837+
case '%':
838+
if (value > 100)
839+
jl_errorf("julia: invalid percentage specified in --heap-size-hint");
840+
uint64_t mem = uv_get_total_memory();
841+
uint64_t cmem = uv_get_constrained_memory();
842+
if (cmem > 0 && cmem < mem)
843+
mem = cmem;
844+
multiplier = mem/100;
845+
break;
846+
default:
847+
jl_errorf("julia: invalid argument to --heap-size-hint (%s)", optarg);
848+
break;
849+
}
850+
long double sz = value * multiplier;
851+
if (isnan(sz) || sz < 0) {
852+
jl_errorf("julia: invalid argument to --heap-size-hint (%s)", optarg);
840853
}
854+
jl_options.heap_size_hint = sz < UINT64_MAX ? (uint64_t)sz : UINT64_MAX;
841855
}
842856
if (jl_options.heap_size_hint == 0)
843857
jl_errorf("julia: invalid memory size specified in --heap-size-hint");

test/cmdlineargs.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,3 +1148,19 @@ if Sys.islinux() && Sys.ARCH in (:i686, :x86_64) # rr is only available on these
11481148
"_RR_TRACE_DIR" => temp_trace_dir); #=stderr, stdout=#))
11491149
end
11501150
end
1151+
1152+
@testset "--heap-size-hint" begin
1153+
exename = `$(Base.julia_cmd())`
1154+
@test errors_not_signals(`$exename --heap-size-hint -e "exit(0)"`)
1155+
@testset "--heap-size-hint=$str" for str in ["asdf","","0","1.2vb","b","GB","2.5GB̂","1.2gb2","42gigabytes","5gig","2GiB","NaNt"]
1156+
@test errors_not_signals(`$exename --heap-size-hint=$str -e "exit(0)"`)
1157+
end
1158+
k = 1024
1159+
m = 1024k
1160+
g = 1024m
1161+
t = 1024g
1162+
@testset "--heap-size-hint=$str" for (str, val) in [("1", 1), ("1e7", 1e7), ("2.5e7", 2.5e7), ("1MB", 1m), ("2.5g", 2.5g), ("1e4kB", 1e4k),
1163+
("1e100", typemax(UInt64)), ("1e500g", typemax(UInt64)), ("1e-12t", 1), ("500000000b", 500000000)]
1164+
@test parse(UInt64,read(`$exename --heap-size-hint=$str -E "Base.JLOptions().heap_size_hint"`, String)) == val
1165+
end
1166+
end

0 commit comments

Comments
 (0)