mirror of
https://github.com/openjdk/jdk.git
synced 2026-06-10 12:37:09 +00:00
8327246: Add a jcmd diagnostic command to list Class origin URLs where possible
This commit is contained in:
parent
4ff29cbde4
commit
a2bbf228f5
@ -36,6 +36,7 @@
|
||||
#include "classfile/classLoaderData.inline.hpp"
|
||||
#include "classfile/javaClasses.hpp"
|
||||
#include "classfile/moduleEntry.hpp"
|
||||
#include "classfile/symbolTable.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "classfile/systemDictionaryShared.hpp"
|
||||
#include "classfile/verifier.hpp"
|
||||
@ -2364,8 +2365,8 @@ Method* InstanceKlass::lookup_method_in_all_interfaces(Symbol* name,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PrintClassClosure::PrintClassClosure(outputStream* st, bool verbose)
|
||||
:_st(st), _verbose(verbose) {
|
||||
PrintClassClosure::PrintClassClosure(outputStream* st, bool verbose, bool location)
|
||||
:_st(st), _verbose(verbose), _location(location), _aot_statics(0), _aot_dynamics(0) {
|
||||
ResourceMark rm;
|
||||
_st->print("%-18s ", "KlassAddr");
|
||||
_st->print("%-4s ", "Size");
|
||||
@ -2382,27 +2383,68 @@ void PrintClassClosure::do_klass(Klass* k) {
|
||||
// klass size
|
||||
_st->print("%4d ", k->size());
|
||||
// initialization state
|
||||
if (k->is_instance_klass()) {
|
||||
_st->print("%-20s ",InstanceKlass::cast(k)->init_state_name());
|
||||
} else {
|
||||
_st->print("%-20s ","");
|
||||
}
|
||||
|
||||
InstanceKlass* ik = k->is_instance_klass() ? InstanceKlass::cast(k) : nullptr;
|
||||
_st->print("%-20s ", ik != nullptr ? InstanceKlass::cast(k)->init_state_name() : "");
|
||||
|
||||
// misc flags(Changes should synced with ClassesDCmd::ClassesDCmd help doc)
|
||||
char buf[10];
|
||||
int i = 0;
|
||||
if (k->has_finalizer()) buf[i++] = 'F';
|
||||
if (k->is_instance_klass()) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||
if (ik != nullptr) {
|
||||
if (ik->has_final_method()) buf[i++] = 'f';
|
||||
if (ik->is_rewritten()) buf[i++] = 'W';
|
||||
if (ik->is_contended()) buf[i++] = 'C';
|
||||
if (ik->has_been_redefined()) buf[i++] = 'R';
|
||||
if (ik->in_aot_cache()) buf[i++] = 'S';
|
||||
if (ik->in_aot_cache()) {
|
||||
buf[i++] = 'S';
|
||||
|
||||
if (_location) {
|
||||
if (AOTMetaspace::in_aot_cache_static_region((void*) k)) {
|
||||
_aot_statics++;
|
||||
buf[i++] = 's';
|
||||
} else if (AOTMetaspace::in_aot_cache_dynamic_region((void*) k)) {
|
||||
_aot_dynamics++;
|
||||
buf[i++] = 'd';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
buf[i++] = '\0';
|
||||
_st->print("%-7s ", buf);
|
||||
// klass name
|
||||
_st->print("%-5s ", k->external_name());
|
||||
|
||||
if (ik != nullptr && _location) {
|
||||
oop pd = java_lang_Class::protection_domain(k->java_mirror());
|
||||
|
||||
if (pd != nullptr) {
|
||||
assert(pd->klass()->is_instance_klass(), "pd klass is not InstanceKlass");
|
||||
TempNewSymbol css = SymbolTable::new_symbol("codesource");
|
||||
TempNewSymbol csss = SymbolTable::new_symbol("Ljava/security/CodeSource;");
|
||||
fieldDescriptor csfd;
|
||||
|
||||
if (InstanceKlass::cast(pd->klass())->find_field(css, csss, &csfd)) {
|
||||
oop cs = pd->obj_field(csfd.offset());
|
||||
|
||||
if (cs != nullptr) {
|
||||
assert(cs->klass()->is_instance_klass(), "cs klass is not InstanceKlass");
|
||||
fieldDescriptor locfd;
|
||||
TempNewSymbol csls = SymbolTable::new_symbol("locationNoFragString");
|
||||
TempNewSymbol cslss = SymbolTable::new_symbol("Ljava/lang/String;");
|
||||
|
||||
if (InstanceKlass::cast(cs->klass())->find_field(csls, cslss, &locfd)) {
|
||||
oop loc = cs->obj_field(locfd.offset());
|
||||
assert(loc == nullptr || loc->klass() == vmClasses::String_klass(), "locationNoFragString field is not a String");
|
||||
|
||||
if (loc != nullptr) {
|
||||
java_lang_String::print(loc, _st, JVM_MAXPATHLEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// end
|
||||
_st->cr();
|
||||
if (_verbose) {
|
||||
|
||||
@ -1207,8 +1207,12 @@ class PrintClassClosure : public KlassClosure {
|
||||
private:
|
||||
outputStream* _st;
|
||||
bool _verbose;
|
||||
bool _location;
|
||||
public:
|
||||
PrintClassClosure(outputStream* st, bool verbose);
|
||||
unsigned int _aot_statics;
|
||||
unsigned int _aot_dynamics;
|
||||
|
||||
PrintClassClosure(outputStream* st, bool verbose, bool location);
|
||||
|
||||
void do_klass(Klass* k);
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,6 +25,7 @@
|
||||
#include "cds/aotMetaspace.hpp"
|
||||
#include "cds/cds_globals.hpp"
|
||||
#include "cds/cdsConfig.hpp"
|
||||
#include "cds/filemap.hpp"
|
||||
#include "classfile/classLoaderDataGraph.hpp"
|
||||
#include "classfile/classLoaderHierarchyDCmd.hpp"
|
||||
#include "classfile/classLoaderStats.hpp"
|
||||
@ -947,36 +948,37 @@ void ClassHierarchyDCmd::execute(DCmdSource source, TRAPS) {
|
||||
|
||||
ClassesDCmd::ClassesDCmd(outputStream* output, bool heap) :
|
||||
DCmdWithParser(output, heap),
|
||||
_verbose("-verbose",
|
||||
"Dump the detailed content of a Java class. "
|
||||
"Some classes are annotated with flags: "
|
||||
"F = has, or inherits, a non-empty finalize method, "
|
||||
"f = has final method, "
|
||||
"W = methods rewritten, "
|
||||
"C = marked with @Contended annotation, "
|
||||
"R = has been redefined, "
|
||||
"S = is shared class",
|
||||
"BOOLEAN", false, "false") {
|
||||
_verbose("-verbose", "Dump the detailed content of a Java class", "BOOLEAN", false, "false"),
|
||||
_location("-location", "Print class file location URL (if available)", "BOOLEAN", false, "false") {
|
||||
_dcmdparser.add_dcmd_option(&_verbose);
|
||||
_dcmdparser.add_dcmd_option(&_location);
|
||||
}
|
||||
|
||||
class VM_PrintClasses : public VM_Operation {
|
||||
private:
|
||||
outputStream* _out;
|
||||
bool _verbose;
|
||||
bool _location;
|
||||
public:
|
||||
VM_PrintClasses(outputStream* out, bool verbose) : _out(out), _verbose(verbose) {}
|
||||
VM_PrintClasses(outputStream* out, bool verbose, bool location) : _out(out), _verbose(verbose), _location(location) {}
|
||||
|
||||
virtual VMOp_Type type() const { return VMOp_PrintClasses; }
|
||||
|
||||
virtual void doit() {
|
||||
PrintClassClosure closure(_out, _verbose);
|
||||
PrintClassClosure closure(_out, _verbose, _location);
|
||||
ClassLoaderDataGraph::classes_do(&closure);
|
||||
|
||||
if (_location) {
|
||||
if (closure._aot_statics > 0)
|
||||
_out->print_cr("\n%d classes shared from static cache: %s", closure._aot_statics, FileMapInfo::current_info()->full_path());
|
||||
if (closure._aot_dynamics > 0)
|
||||
_out->print_cr("\n%d classes shared from dynamic cache: %s", closure._aot_dynamics, FileMapInfo::dynamic_info()->full_path());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void ClassesDCmd::execute(DCmdSource source, TRAPS) {
|
||||
VM_PrintClasses vmop(output(), _verbose.value());
|
||||
VM_PrintClasses vmop(output(), _verbose.value(), _location.value());
|
||||
VMThread::execute(&vmop);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -744,16 +744,20 @@ public:
|
||||
};
|
||||
|
||||
class ClassesDCmd : public DCmdWithParser {
|
||||
private:
|
||||
static constexpr const char *desc = "Print all loaded classes,\nclasses are annotated with flags:\n F = has, or inherits, a non-empty finalize method,\n f = has final method,\n W = methods rewritten,\n C = marked with @Contended annotation,\n R = has been redefined,\n S = is an (App)CDS shared class,\n (if -location is specified, append: 's' (static) or 'd' (dynamic) for AOT cache location)";
|
||||
|
||||
protected:
|
||||
DCmdArgument<bool> _verbose;
|
||||
DCmdArgument<bool> _location;
|
||||
public:
|
||||
static int num_arguments() { return 1; }
|
||||
static int num_arguments() { return 2; }
|
||||
ClassesDCmd(outputStream* output, bool heap);
|
||||
static const char* name() {
|
||||
return "VM.classes";
|
||||
}
|
||||
static const char* description() {
|
||||
return "Print all loaded classes";
|
||||
return desc;
|
||||
}
|
||||
static const char* impact() {
|
||||
return "Medium: Depends on number of loaded classes.";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
# Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
@ -800,19 +800,35 @@ The following commands are available:
|
||||
no default value)
|
||||
|
||||
`VM.classes` \[*options*\]
|
||||
: Print all loaded classes
|
||||
: Print all loaded classes.
|
||||
|
||||
Classes may be annotated with flags:
|
||||
- `F` = has, or inherits, a non-empty finalize method,
|
||||
- `f` = has final method,
|
||||
- `W` = methods rewritten,
|
||||
- `C` = marked with `@Contended` annotation,
|
||||
- `R` = has been redefined,
|
||||
- `S` = is shared class (if `-location` is specified then either 's' (static) or 'd' (dynamic) for AOT cache origin is appended)
|
||||
|
||||
Impact: Medium: Depends on number of loaded classes.
|
||||
|
||||
The following *options* must be specified using either *key* or
|
||||
*key*`=`*value* syntax.
|
||||
The following *options* must be specified using either *key* or *key*`=`*value* syntax.
|
||||
|
||||
*options*:
|
||||
|
||||
- `-verbose`: (Optional) Dump the detailed content of a Java class.
|
||||
Some classes are annotated with flags: `F` = has, or inherits, a non-empty finalize method,
|
||||
`f` = has final method, `W` = methods rewritten, `C` = marked with `@Contended` annotation,
|
||||
`R` = has been redefined, `S` = is shared class (BOOLEAN, false)
|
||||
- `-verbose`: (Optional) Dump the detailed content of a Java class. (BOOLEAN, false)
|
||||
|
||||
- `-location`: (Optional) Print the location of the class file from which the class is loaded (if available).
|
||||
If provided by its defining ClassLoader, this option will print a URL specifying the location of the
|
||||
class file (directory, jar or other URL location) from which this class was initially loaded.
|
||||
|
||||
Note: JDK (and other classes) loaded by a ClassLoader that does not provide a location URL to the JVM will omit this field.
|
||||
|
||||
Note: if any classes are loaded from an AOT cache, their location reported is that of the original
|
||||
URL from which they were loaded at the time of the training run that created the AOT cache.
|
||||
Additionally the flags will also be annotated to indicate the AOT cache origin (static or dynamic).
|
||||
|
||||
The total number of classes loaded (if any) from either AOT cache (and the associated cache path location) are summarized.
|
||||
|
||||
`VM.classloader_stats`
|
||||
: Print statistics about all ClassLoaders.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, Alibaba Group Holding Limited. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -59,5 +59,9 @@ public class PrintClasses {
|
||||
|
||||
// Test for previous bug in misc flags printing
|
||||
output.shouldNotContain("##name");
|
||||
|
||||
pb.command(new PidJcmdExecutor().getCommandLine("VM.classes", "-location"));
|
||||
output = new OutputAnalyzer(pb.start());
|
||||
output.stdoutShouldMatch("^.*(file:/|jar:).*$");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user