Skip to content

Commit 3b582df

Browse files
committed
8051959: Add thread and timestamp options to java.security.debug system property
Reviewed-by: mullan, weijun
1 parent 816638e commit 3b582df

File tree

3 files changed

+318
-50
lines changed

3 files changed

+318
-50
lines changed

src/java.base/share/classes/sun/security/util/Debug.java

Lines changed: 111 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727

2828
import java.io.PrintStream;
2929
import java.math.BigInteger;
30+
import java.time.Instant;
31+
import java.time.ZoneId;
32+
import java.time.format.DateTimeFormatter;
3033
import java.util.HexFormat;
3134
import java.util.regex.Pattern;
3235
import java.util.regex.Matcher;
@@ -41,8 +44,14 @@
4144
public class Debug {
4245

4346
private String prefix;
47+
private boolean printDateTime;
48+
private boolean printThreadDetails;
4449

4550
private static String args;
51+
private static boolean threadInfoAll;
52+
private static boolean timeStampInfoAll;
53+
private static final String TIMESTAMP_OPTION = "+timestamp";
54+
private static final String THREAD_OPTION = "+thread";
4655

4756
static {
4857
args = GetPropertyAction.privilegedGetProperty("java.security.debug");
@@ -61,12 +70,21 @@ public class Debug {
6170
args = marshal(args);
6271
if (args.equals("help")) {
6372
Help();
73+
} else if (args.contains("all")) {
74+
// "all" option has special handling for decorator options
75+
// If the thread or timestamp decorator option is detected
76+
// with the "all" option, then it impacts decorator options
77+
// for other categories
78+
int beginIndex = args.lastIndexOf("all") + "all".length();
79+
int commaIndex = args.indexOf(',', beginIndex);
80+
if (commaIndex == -1) commaIndex = args.length();
81+
threadInfoAll = args.substring(beginIndex, commaIndex).contains(THREAD_OPTION);
82+
timeStampInfoAll = args.substring(beginIndex, commaIndex).contains(TIMESTAMP_OPTION);
6483
}
6584
}
6685
}
6786

68-
public static void Help()
69-
{
87+
public static void Help() {
7088
System.err.println();
7189
System.err.println("all turn on all debugging");
7290
System.err.println("access print all checkPermission results");
@@ -95,6 +113,11 @@ public static void Help()
95113
System.err.println("ts timestamping");
96114
System.err.println("x509 X.509 certificate debugging");
97115
System.err.println();
116+
System.err.println("+timestamp can be appended to any of above options to print");
117+
System.err.println(" a timestamp for that debug option");
118+
System.err.println("+thread can be appended to any of above options to print");
119+
System.err.println(" thread and caller information for that debug option");
120+
System.err.println();
98121
System.err.println("The following can be used with access:");
99122
System.err.println();
100123
System.err.println("stack include stack trace");
@@ -139,26 +162,60 @@ public static void Help()
139162
* option is set. Set the prefix to be the same as option.
140163
*/
141164

142-
public static Debug getInstance(String option)
143-
{
165+
public static Debug getInstance(String option) {
144166
return getInstance(option, option);
145167
}
146168

147169
/**
148170
* Get a Debug object corresponding to whether or not the given
149171
* option is set. Set the prefix to prefix.
150172
*/
151-
public static Debug getInstance(String option, String prefix)
152-
{
173+
public static Debug getInstance(String option, String prefix) {
153174
if (isOn(option)) {
154175
Debug d = new Debug();
155176
d.prefix = prefix;
177+
d.configureExtras(option);
156178
return d;
157179
} else {
158180
return null;
159181
}
160182
}
161183

184+
private static String formatCaller() {
185+
return StackWalker.getInstance().walk(s ->
186+
s.dropWhile(f ->
187+
f.getClassName().startsWith("sun.security.util.Debug"))
188+
.map(f -> f.getFileName() + ":" + f.getLineNumber())
189+
.findFirst().orElse("unknown caller"));
190+
}
191+
192+
// parse an option string to determine if extra details,
193+
// like thread and timestamp, should be printed
194+
private void configureExtras(String option) {
195+
// treat "all" as special case, only used for java.security.debug property
196+
this.printDateTime = timeStampInfoAll;
197+
this.printThreadDetails = threadInfoAll;
198+
199+
if (printDateTime && printThreadDetails) {
200+
// nothing left to configure
201+
return;
202+
}
203+
204+
// args is converted to lower case for the most part via marshal method
205+
int optionIndex = args.lastIndexOf(option);
206+
if (optionIndex == -1) {
207+
// option not in args list. Only here since "all" was present
208+
// in debug property argument. "all" option already parsed
209+
return;
210+
}
211+
int beginIndex = optionIndex + option.length();
212+
int commaIndex = args.indexOf(',', beginIndex);
213+
if (commaIndex == -1) commaIndex = args.length();
214+
String subOpt = args.substring(beginIndex, commaIndex);
215+
printDateTime = printDateTime || subOpt.contains(TIMESTAMP_OPTION);
216+
printThreadDetails = printThreadDetails || subOpt.contains(THREAD_OPTION);
217+
}
218+
162219
/**
163220
* Get a Debug object corresponding to the given option on the given
164221
* property value.
@@ -173,14 +230,22 @@ public static Debug getInstance(String option, String prefix)
173230
* String property = settings.get("login");
174231
* Debug debug = Debug.of("login", property);
175232
* }
176-
* @param option the debug option name
233+
*
234+
* +timestamp string can be appended to property value
235+
* to print timestamp information. (e.g. true+timestamp)
236+
* +thread string can be appended to property value
237+
* to print thread and caller information. (e.g. true+thread)
238+
*
239+
* @param prefix the debug option name
177240
* @param property debug setting for this option
178241
* @return a new Debug object if the property is true
179242
*/
180-
public static Debug of(String option, String property) {
181-
if ("true".equalsIgnoreCase(property)) {
243+
public static Debug of(String prefix, String property) {
244+
if (property != null && property.toLowerCase(Locale.ROOT).startsWith("true")) {
182245
Debug d = new Debug();
183-
d.prefix = option;
246+
d.prefix = prefix;
247+
d.printThreadDetails = property.contains(THREAD_OPTION);
248+
d.printDateTime = property.contains(TIMESTAMP_OPTION);
184249
return d;
185250
}
186251
return null;
@@ -190,8 +255,7 @@ public static Debug of(String option, String property) {
190255
* True if the system property "security.debug" contains the
191256
* string "option".
192257
*/
193-
public static boolean isOn(String option)
194-
{
258+
public static boolean isOn(String option) {
195259
if (args == null)
196260
return false;
197261
else {
@@ -214,37 +278,53 @@ public static boolean isVerbose() {
214278
* created from the call to getInstance.
215279
*/
216280

217-
public void println(String message)
218-
{
219-
System.err.println(prefix + ": "+message);
281+
public void println(String message) {
282+
System.err.println(prefix + extraInfo() + ": " + message);
220283
}
221284

222285
/**
223286
* print a message to stderr that is prefixed with the prefix
224287
* created from the call to getInstance and obj.
225288
*/
226-
public void println(Object obj, String message)
227-
{
228-
System.err.println(prefix + " [" + obj.getClass().getSimpleName() +
289+
public void println(Object obj, String message) {
290+
System.err.println(prefix + extraInfo() + " [" + obj.getClass().getSimpleName() +
229291
"@" + System.identityHashCode(obj) + "]: "+message);
230292
}
231293

232294
/**
233295
* print a blank line to stderr that is prefixed with the prefix.
234296
*/
235297

236-
public void println()
237-
{
238-
System.err.println(prefix + ":");
298+
public void println() {
299+
System.err.println(prefix + extraInfo() + ":");
239300
}
240301

241302
/**
242303
* print a message to stderr that is prefixed with the prefix.
243304
*/
244305

245-
public static void println(String prefix, String message)
246-
{
247-
System.err.println(prefix + ": "+message);
306+
public void println(String prefix, String message) {
307+
System.err.println(prefix + extraInfo() + ": " + message);
308+
}
309+
310+
/**
311+
* If thread debug option enabled, include information containing
312+
* hex value of threadId and the current thread name
313+
* If timestamp debug option enabled, include timestamp string
314+
* @return extra info if debug option enabled.
315+
*/
316+
private String extraInfo() {
317+
String retString = "";
318+
if (printThreadDetails) {
319+
retString = "0x" + Long.toHexString(
320+
Thread.currentThread().threadId()).toUpperCase(Locale.ROOT) +
321+
"|" + Thread.currentThread().getName() + "|" + formatCaller();
322+
}
323+
if (printDateTime) {
324+
retString += (retString.isEmpty() ? "" : "|")
325+
+ FormatHolder.DATE_TIME_FORMATTER.format(Instant.now());
326+
}
327+
return retString.isEmpty() ? "" : "[" + retString + "]";
248328
}
249329

250330
/**
@@ -364,4 +444,11 @@ public static String toString(BigInteger b) {
364444
return toString(b.toByteArray());
365445
}
366446

447+
// Holder class to break cyclic dependency seen during build
448+
private static class FormatHolder {
449+
private static final String PATTERN = "yyyy-MM-dd kk:mm:ss.SSS";
450+
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter
451+
.ofPattern(PATTERN, Locale.ENGLISH)
452+
.withZone(ZoneId.systemDefault());
453+
}
367454
}

test/jdk/sun/security/krb5/auto/LoginModuleDebug.java

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,42 +23,85 @@
2323

2424
/*
2525
* @test
26-
* @bug 8327818
26+
* @bug 8327818 8051959
2727
* @summary reimplement debug option in Krb5LoginModule
2828
* @library /test/lib
29+
* @run junit LoginModuleDebug
2930
*/
3031
import com.sun.security.auth.module.Krb5LoginModule;
32+
import jdk.test.lib.process.OutputAnalyzer;
3133
import jdk.test.lib.process.ProcessTools;
34+
import org.junit.jupiter.params.ParameterizedTest;
35+
import org.junit.jupiter.params.provider.Arguments;
36+
import org.junit.jupiter.params.provider.MethodSource;
3237

3338
import java.util.Map;
39+
import java.util.stream.Stream;
3440
import javax.security.auth.Subject;
3541

3642
public class LoginModuleDebug {
37-
public static void main(String[] args) throws Exception {
38-
if (args.length == 0) {
39-
// debug option set to true
40-
ProcessTools.executeTestJava("LoginModuleDebug",
41-
"debug", "true")
42-
.stdoutShouldBeEmpty()
43-
.stderrShouldContain("krb5loginmodule:");
44-
// debug option set to false
45-
ProcessTools.executeTestJava("LoginModuleDebug",
46-
"debug", "false")
47-
.stdoutShouldBeEmpty()
48-
.stderrShouldNotContain("krb5loginmodule:");
49-
// no debug option
50-
ProcessTools.executeTestJava("LoginModuleDebug",
51-
"foo", "bar")
52-
.stdoutShouldBeEmpty()
53-
.stderrShouldNotContain("krb5loginmodule:");
54-
} else {
55-
test(args[0], args[1]);
56-
}
43+
static final String DATE_REGEX = "\\d{4}-\\d{2}-\\d{2}";
44+
45+
private static Stream<Arguments> patternMatches() {
46+
return Stream.of(
47+
// debug option set to true - no extra info
48+
Arguments.of("debug",
49+
"true",
50+
"krb5loginmodule:",
51+
"krb5loginmodule\\["),
52+
// debug option set to false
53+
Arguments.of("debug",
54+
"false",
55+
"",
56+
"krb5loginmodule"),
57+
// no debug option
58+
Arguments.of("foo",
59+
"bar",
60+
"",
61+
"krb5loginmodule"),
62+
// thread info only
63+
Arguments.of("debug",
64+
"true+thread",
65+
"krb5loginmodule\\[.*\\|main|\\.*java.*]:",
66+
"\\|" + DATE_REGEX + ".*\\]:"),
67+
// timestamp info only
68+
Arguments.of("debug",
69+
"true+timestamp",
70+
"krb5loginmodule\\[" + DATE_REGEX + ".*\\]",
71+
"\\|main\\]:"),
72+
// both thread and timestamp
73+
Arguments.of("debug",
74+
"true+timestamp+thread",
75+
"krb5loginmodule\\[.*\\|main|" + DATE_REGEX + ".*\\]:",
76+
"krb5loginmodule:")
77+
);
5778
}
5879

59-
static void test(String key, String prop)
60-
throws Exception {
61-
new Krb5LoginModule().initialize(
62-
new Subject(), null, Map.of(), Map.of(key, prop));
80+
@ParameterizedTest
81+
@MethodSource("patternMatches")
82+
public void shouldContain(String value, String key, String expected, String notExpected) throws Exception {
83+
OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava(
84+
"LoginModuleDebug",
85+
value, key);
86+
outputAnalyzer.shouldHaveExitValue(0)
87+
.shouldMatch(expected)
88+
.shouldNotMatch(notExpected);
89+
// let's also run with java debug property enabled
90+
outputAnalyzer = ProcessTools.executeTestJava(
91+
"-Djava.security.debug=all",
92+
"LoginModuleDebug",
93+
value, key);
94+
outputAnalyzer.shouldHaveExitValue(0)
95+
.shouldMatch(expected)
96+
.shouldNotMatch(notExpected);
97+
}
98+
99+
public static void main(String[] args) throws Exception {
100+
System.err.println(args.length);
101+
if (args.length == 2) {
102+
// something to trigger "krb5" debug output
103+
new Krb5LoginModule().initialize(
104+
new Subject(), null, Map.of(), Map.of(args[0], args[1]));
105+
}
63106
}
64-
}
107+
}

0 commit comments

Comments
 (0)