Skip to content

Commit

Permalink
8287647: VM debug support: find node by pattern in name or dump
Browse files Browse the repository at this point in the history
Reviewed-by: kvn, chagedorn, thartmann
  • Loading branch information
eme64 committed Jun 15, 2022
1 parent 33f34d5 commit 2471f8f
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 52 deletions.
167 changes: 115 additions & 52 deletions src/hotspot/share/opto/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "utilities/copy.hpp"
#include "utilities/macros.hpp"
#include "utilities/powerOfTwo.hpp"
#include "utilities/stringUtils.hpp"

class RegMask;
// #include "phase.hpp"
Expand Down Expand Up @@ -1604,6 +1605,119 @@ Node* old_root() {
return nullptr;
}

// BFS traverse all reachable nodes from start, call callback on them
template <typename Callback>
void visit_nodes(Node* start, Callback callback, bool traverse_output, bool only_ctrl) {
Unique_Mixed_Node_List worklist;
worklist.add(start);
for (uint i = 0; i < worklist.size(); i++) {
Node* n = worklist[i];
callback(n);
for (uint i = 0; i < n->len(); i++) {
if (!only_ctrl || n->is_Region() || (n->Opcode() == Op_Root) || (i == TypeFunc::Control)) {
// If only_ctrl is set: Add regions, the root node, or control inputs only
worklist.add(n->in(i));
}
}
if (traverse_output && !only_ctrl) {
for (uint i = 0; i < n->outcnt(); i++) {
worklist.add(n->raw_out(i));
}
}
}
}

// BFS traverse from start, return node with idx
Node* find_node_by_idx(Node* start, uint idx, bool traverse_output, bool only_ctrl) {
ResourceMark rm;
Node* result = nullptr;
auto callback = [&] (Node* n) {
if (n->_idx == idx) {
if (result != nullptr) {
tty->print("find_node_by_idx: " INTPTR_FORMAT " and " INTPTR_FORMAT " both have idx==%d\n",
(uintptr_t)result, (uintptr_t)n, idx);
}
result = n;
}
};
visit_nodes(start, callback, traverse_output, only_ctrl);
return result;
}

int node_idx_cmp(Node** n1, Node** n2) {
return (*n1)->_idx - (*n2)->_idx;
}

Node* find_node_by_name(Node* start, const char* name) {
ResourceMark rm;
Node* result = nullptr;
GrowableArray<Node*> ns;
auto callback = [&] (Node* n) {
if (StringUtils::is_star_match(name, n->Name())) {
ns.push(n);
result = n;
}
};
visit_nodes(start, callback, true, false);
ns.sort(node_idx_cmp);
for (int i = 0; i < ns.length(); i++) {
ns.at(i)->dump();
}
return result;
}

Node* find_node_by_dump(Node* start, const char* pattern) {
ResourceMark rm;
Node* result = nullptr;
GrowableArray<Node*> ns;
auto callback = [&] (Node* n) {
stringStream stream;
n->dump("", false, &stream);
if (StringUtils::is_star_match(pattern, stream.base())) {
ns.push(n);
result = n;
}
};
visit_nodes(start, callback, true, false);
ns.sort(node_idx_cmp);
for (int i = 0; i < ns.length(); i++) {
ns.at(i)->dump();
}
return result;
}

// call from debugger: find node with name pattern in new/current graph
// name can contain "*" in match pattern to match any characters
// the matching is case insensitive
Node* find_node_by_name(const char* name) {
Node* root = Compile::current()->root();
return find_node_by_name(root, name);
}

// call from debugger: find node with name pattern in old graph
// name can contain "*" in match pattern to match any characters
// the matching is case insensitive
Node* find_old_node_by_name(const char* name) {
Node* root = old_root();
return find_node_by_name(root, name);
}

// call from debugger: find node with dump pattern in new/current graph
// can contain "*" in match pattern to match any characters
// the matching is case insensitive
Node* find_node_by_dump(const char* pattern) {
Node* root = Compile::current()->root();
return find_node_by_dump(root, pattern);
}

// call from debugger: find node with name pattern in old graph
// can contain "*" in match pattern to match any characters
// the matching is case insensitive
Node* find_old_node_by_dump(const char* pattern) {
Node* root = old_root();
return find_node_by_dump(root, pattern);
}

// Call this from debugger, search in same graph as n:
Node* find_node(Node* n, const int idx) {
return n->find(idx);
Expand Down Expand Up @@ -1649,54 +1763,7 @@ Node* Node::find_ctrl(int idx) {
// not found or if the node to be found is not a control node (search will not find it).
Node* Node::find(const int idx, bool only_ctrl) {
ResourceMark rm;
VectorSet old_space;
VectorSet new_space;
Node_List worklist;
Arena* old_arena = Compile::current()->old_arena();
add_to_worklist(this, &worklist, old_arena, &old_space, &new_space);
Node* result = NULL;
int node_idx = (idx >= 0) ? idx : -idx;

for (uint list_index = 0; list_index < worklist.size(); list_index++) {
Node* n = worklist[list_index];

if ((int)n->_idx == node_idx debug_only(|| n->debug_idx() == node_idx)) {
if (result != NULL) {
tty->print("find: " INTPTR_FORMAT " and " INTPTR_FORMAT " both have idx==%d\n",
(uintptr_t)result, (uintptr_t)n, node_idx);
}
result = n;
}

for (uint i = 0; i < n->len(); i++) {
if (!only_ctrl || n->is_Region() || (n->Opcode() == Op_Root) || (i == TypeFunc::Control)) {
// If only_ctrl is set: Add regions, the root node, or control inputs only
add_to_worklist(n->in(i), &worklist, old_arena, &old_space, &new_space);
}
}

// Also search along forward edges if idx is negative and the search is not done on control nodes only
if (idx < 0 && !only_ctrl) {
for (uint i = 0; i < n->outcnt(); i++) {
add_to_worklist(n->raw_out(i), &worklist, old_arena, &old_space, &new_space);
}
}
}
return result;
}

bool Node::add_to_worklist(Node* n, Node_List* worklist, Arena* old_arena, VectorSet* old_space, VectorSet* new_space) {
if (not_a_node(n)) {
return false; // Gracefully handle NULL, -1, 0xabababab, etc.
}

// Contained in new_space or old_space? Check old_arena first since it's mostly empty.
VectorSet* v = old_arena->contains(n) ? old_space : new_space;
if (!v->test_set(n->_idx)) {
worklist->push(n);
return true;
}
return false;
return find_node_by_idx(this, abs(idx), (idx < 0), only_ctrl);
}

class PrintBFS {
Expand Down Expand Up @@ -1935,10 +2002,6 @@ void PrintBFS::select_shortest_path() {
}
}

int node_idx_cmp(Node** n1, Node** n2) {
return (*n1)->_idx - (*n2)->_idx;
}

// go through worklist in desired order, put the marked ones in print list
void PrintBFS::sort() {
if (_traverse_inputs && !_traverse_outputs) {
Expand Down
30 changes: 30 additions & 0 deletions src/hotspot/share/opto/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1663,6 +1663,36 @@ class Unique_Node_List : public Node_List {
#endif
};

// Unique_Mixed_Node_List
// unique: nodes are added only once
// mixed: allow new and old nodes
class Unique_Mixed_Node_List : public ResourceObj {
public:
Unique_Mixed_Node_List() : _visited_set(cmpkey, hashkey) {}

void add(Node* node) {
if (not_a_node(node)) {
return; // Gracefully handle NULL, -1, 0xabababab, etc.
}
if (_visited_set[node] == nullptr) {
_visited_set.Insert(node, node);
_worklist.push(node);
}
}

Node* operator[] (uint i) const {
return _worklist[i];
}

size_t size() {
return _worklist.size();
}

private:
Dict _visited_set;
Node_List _worklist;
};

// Inline definition of Compile::record_for_igvn must be deferred to this point.
inline void Compile::record_for_igvn(Node* n) {
_for_igvn->push(n);
Expand Down
55 changes: 55 additions & 0 deletions src/hotspot/share/utilities/stringUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
*
*/

#include "jvm_io.h"
#include "precompiled.hpp"
#include "utilities/debug.hpp"
#include "utilities/stringUtils.hpp"

#include <ctype.h>
#include <string.h>

int StringUtils::replace_no_expand(char* string, const char* from, const char* to) {
Expand Down Expand Up @@ -67,3 +69,56 @@ double StringUtils::similarity(const char* str1, size_t len1, const char* str2,

return 2.0 * (double) hit / (double) total;
}

const char* StringUtils::strstr_nocase(const char* haystack, const char* needle) {
if (needle[0] == '\0') {
return haystack; // empty needle matches with anything
}
for (size_t i = 0; haystack[i] != '\0'; i++) {
bool matches = true;
for (size_t j = 0; needle[j] != '\0'; j++) {
if (haystack[i + j] == '\0') {
return nullptr; // hit end of haystack, abort
}
if (tolower(haystack[i + j]) != tolower(needle[j])) {
matches = false;
break; // abort, try next i
}
}
if (matches) {
return &haystack[i]; // all j were ok for this i
}
}
return nullptr; // no i was a match
}

bool StringUtils::is_star_match(const char* star_pattern, const char* str) {
const int N = 1000;
char pattern[N]; // copy pattern into this to ensure null termination
jio_snprintf(pattern, N, "%s", star_pattern);// ensures null termination
char buf[N]; // copy parts of pattern into this
const char* str_idx = str;
const char* pattern_idx = pattern;
while (strlen(pattern_idx) > 0) {
// find next section in pattern
const char* pattern_part_end = strstr(pattern_idx, "*");
const char* pattern_part = pattern_idx;
if (pattern_part_end != nullptr) { // copy part into buffer
size_t pattern_part_len = pattern_part_end-pattern_part;
strncpy(buf, pattern_part, pattern_part_len);
buf[pattern_part_len] = '\0'; // end of string
pattern_part = buf;
}
// find this section in s, case insensitive
const char* str_match = strstr_nocase(str_idx, pattern_part);
if (str_match == nullptr) {
return false; // r_part did not match - abort
}
size_t match_len = strlen(pattern_part);
// advance to match position plus part length
str_idx = str_match + match_len;
// advance by part length and "*"
pattern_idx += match_len + (pattern_part_end == nullptr ? 0 : 1);
}
return true; // all parts of pattern matched
}
9 changes: 9 additions & 0 deletions src/hotspot/share/utilities/stringUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ class StringUtils : AllStatic {

// Compute string similarity based on Dice's coefficient
static double similarity(const char* str1, size_t len1, const char* str2, size_t len2);

// Find needle in haystack, case insensitive.
// Custom implementation of strcasestr, as it is not available on windows.
static const char* strstr_nocase(const char* haystack, const char* needle);

// Check if str matches the star_pattern.
// eg. str "_abc____def__" would match pattern "abc*def".
// The matching is case insensitive.
static bool is_star_match(const char* star_pattern, const char* str);
};

#endif // SHARE_UTILITIES_STRINGUTILS_HPP

1 comment on commit 2471f8f

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.