javacore-analyser 2.0rc3__py3-none-any.whl → 2.1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- javacore_analyser/__main__.py +43 -0
- javacore_analyser/abstract_snapshot_collection.py +2 -2
- javacore_analyser/constants.py +2 -1
- javacore_analyser/data/expand.js +2 -0
- javacore_analyser/data/html/processing_data.html +17 -0
- javacore_analyser/data/jquery/search.js +22 -0
- javacore_analyser/data/jquery/wait2scripts.js +4 -2
- javacore_analyser/data/xml/report.xsl +35 -19
- javacore_analyser/javacore.py +5 -3
- javacore_analyser/javacore_analyser_batch.py +45 -22
- javacore_analyser/javacore_analyser_web.py +62 -26
- javacore_analyser/javacore_set.py +48 -34
- javacore_analyser/snapshot_collection_collection.py +5 -3
- javacore_analyser/stack_trace.py +2 -2
- javacore_analyser/stack_trace_element.py +1 -1
- javacore_analyser/thread_snapshot.py +3 -2
- javacore_analyser/tips.py +5 -3
- javacore_analyser/verbose_gc.py +4 -2
- {javacore_analyser-2.0rc3.dist-info → javacore_analyser-2.1.dist-info}/METADATA +45 -19
- {javacore_analyser-2.0rc3.dist-info → javacore_analyser-2.1.dist-info}/RECORD +23 -21
- {javacore_analyser-2.0rc3.dist-info → javacore_analyser-2.1.dist-info}/WHEEL +1 -1
- {javacore_analyser-2.0rc3.dist-info → javacore_analyser-2.1.dist-info}/entry_points.txt +0 -0
- {javacore_analyser-2.0rc3.dist-info → javacore_analyser-2.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
#
|
2
|
+
# Copyright IBM Corp. 2024 - 2024
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
4
|
+
#
|
5
|
+
import argparse
|
6
|
+
|
7
|
+
from javacore_analyser import javacore_analyser_batch, constants, javacore_analyser_web
|
8
|
+
|
9
|
+
|
10
|
+
def main():
|
11
|
+
parser = argparse.ArgumentParser(prog="python -m javacore_analyser")
|
12
|
+
subparsers = parser.add_subparsers(dest="type", help="Application type", required=True)
|
13
|
+
|
14
|
+
batch = subparsers.add_parser("batch", description="Run batch application")
|
15
|
+
batch.add_argument("input", help="Input file(s) or directory")
|
16
|
+
batch.add_argument("output", help="Destination report directory")
|
17
|
+
batch.add_argument("--separator", default=constants.DEFAULT_FILE_DELIMITER)
|
18
|
+
|
19
|
+
web = subparsers.add_parser("web", description="Run web application")
|
20
|
+
web.add_argument("--debug", help="Debug mode. Use True only for app development", default=False)
|
21
|
+
web.add_argument("--port", help="Application port", default=constants.DEFAULT_PORT)
|
22
|
+
web.add_argument("--reports-dir", help="Directory to store reports data",
|
23
|
+
default=constants.DEFAULT_REPORTS_DIR)
|
24
|
+
|
25
|
+
args = parser.parse_args()
|
26
|
+
|
27
|
+
app_type: str = args.type
|
28
|
+
|
29
|
+
if app_type.lower() == "web":
|
30
|
+
print("Running web application")
|
31
|
+
javacore_analyser_web.run_web(args.debug, args.port, args.reports_dir)
|
32
|
+
elif app_type.lower() == "batch":
|
33
|
+
print("Running batch application")
|
34
|
+
javacore_analyser_batch.batch_process(args.input, args.output, args.separator)
|
35
|
+
else:
|
36
|
+
print('Invalid application type. Available types: "batch" or "web"')
|
37
|
+
|
38
|
+
|
39
|
+
if __name__ == '__main__':
|
40
|
+
# This is the code to be able to run the app from command line.
|
41
|
+
# Try this:
|
42
|
+
# python -m javacore_analyser -h
|
43
|
+
main()
|
@@ -59,7 +59,7 @@ class AbstractSnapshotCollection(abc.ABC):
|
|
59
59
|
self.thread_snapshots.append(snapshot)
|
60
60
|
|
61
61
|
def index_of(self, snapshot):
|
62
|
-
for i in range(len(self.
|
62
|
+
for i in range(len(self.thread_snapshots)):
|
63
63
|
if self.thread_snapshots[i] == snapshot: return i
|
64
64
|
return -1
|
65
65
|
|
@@ -142,7 +142,7 @@ class AbstractSnapshotCollection(abc.ABC):
|
|
142
142
|
result = 0
|
143
143
|
for i in self.thread_snapshots:
|
144
144
|
el = i.get_java_stack_depth()
|
145
|
-
if el>result:
|
145
|
+
if el > result:
|
146
146
|
result = el
|
147
147
|
return result
|
148
148
|
|
javacore_analyser/constants.py
CHANGED
@@ -30,10 +30,11 @@ ENCODING = '1TICHARSET'
|
|
30
30
|
DATA_OUTPUT_SUBDIR = '/data/'
|
31
31
|
DEFAULT_FILE_DELIMITER = ';'
|
32
32
|
|
33
|
-
MIN_JAVACORE_SIZE = 5 * 1024
|
33
|
+
MIN_JAVACORE_SIZE = 5 * 1024 # Minimal Javacore size in bytes
|
34
34
|
|
35
35
|
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
|
36
36
|
|
37
37
|
# Web application constants
|
38
38
|
DEFAULT_REPORTS_DIR = "reports"
|
39
39
|
DEFAULT_PORT = 5000
|
40
|
+
TEMP_DIR = "temp_data" # Folder to store temporary data for creating reports
|
javacore_analyser/data/expand.js
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
|
3
|
+
<!--
|
4
|
+
# Copyright IBM Corp. 2024 - 2024
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
-->
|
7
|
+
|
8
|
+
<html lang="en">
|
9
|
+
<head>
|
10
|
+
<meta http-equiv="refresh" content="30" /> <!-- Refresh this page every 30s until generated page appears -->
|
11
|
+
<meta charset="UTF-8">
|
12
|
+
<title>The page is being generated</title>
|
13
|
+
</head>
|
14
|
+
<body>
|
15
|
+
<h1>The page is being generated. Once it is ready, it will appear here.</h1>
|
16
|
+
</body>
|
17
|
+
</html>
|
@@ -49,10 +49,32 @@ $(function() {
|
|
49
49
|
searchInNode(rootNode, searchTerm);
|
50
50
|
}
|
51
51
|
|
52
|
+
function processChild(child) {
|
53
|
+
try {
|
54
|
+
if (isDomNode(child) && child.classList.contains('toggle_expand')) {
|
55
|
+
for (i = 0; i < child.childNodes.length; ++i) {
|
56
|
+
grandchild = child.childNodes[i];
|
57
|
+
if (isDomNode(grandchild) && grandchild.text == '[+] Expand') {
|
58
|
+
grandchild.text = '[-] Collapse';
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
} catch(err) {
|
63
|
+
console.log(err);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
52
67
|
function searchInNode(node, searchTerm) {
|
53
68
|
if (!isDomNode(node)) return;
|
54
69
|
if (node.textContent.toUpperCase().match(searchTerm.toUpperCase())) {
|
55
70
|
// expand the node here
|
71
|
+
if (!node.classList.contains('show-all')) {
|
72
|
+
node.classList.add('show-all');
|
73
|
+
for (i = 0; i < node.childNodes.length; ++i) {
|
74
|
+
child = node.childNodes[i];
|
75
|
+
processChild(child);
|
76
|
+
}
|
77
|
+
}
|
56
78
|
if (node.getAttribute('style') && node.style.display == "none") {
|
57
79
|
node.style.display = "";
|
58
80
|
}
|
@@ -99,7 +99,9 @@ const loadChartGC = function() {
|
|
99
99
|
}
|
100
100
|
|
101
101
|
const sysResourceE3Elem = document.getElementById('systemresources_myChartGC');
|
102
|
-
sysResourceE3Elem
|
102
|
+
if (sysResourceE3Elem) {
|
103
|
+
sysResourceE3Elem.classList.remove('hide');
|
104
|
+
}
|
103
105
|
|
104
106
|
const ctx = document.getElementById('myChartGC');
|
105
107
|
|
@@ -125,7 +127,7 @@ const loadChartGC = function() {
|
|
125
127
|
startingPoint = timestamp;
|
126
128
|
|
127
129
|
if(endingPoint < timestamp)
|
128
|
-
endingPoint
|
130
|
+
endingPoint = timestamp;
|
129
131
|
}
|
130
132
|
|
131
133
|
coresTimeRange['startTime'] = startingPoint;
|
@@ -355,25 +355,41 @@
|
|
355
355
|
<h4>Garbage Collection Activity</h4>
|
356
356
|
<a id="togglememusagedoc" href="javascript:expand_it(memusagedoc,togglememusagedoc)" class="expandit">
|
357
357
|
What does this chart tell me?</a>
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
358
|
+
<xsl:choose>
|
359
|
+
<xsl:when test="doc/report_info/verbose_gc_list/verbose_gc">
|
360
|
+
<xsl:choose>
|
361
|
+
<xsl:when test="//verbose_gc_list/@total_collects_in_time_limits = 0">
|
362
|
+
<br/>
|
363
|
+
There were no garbage collections withing the javacore time limits
|
364
|
+
</xsl:when>
|
365
|
+
<xsl:otherwise>
|
366
|
+
<div id="memusagedoc" style="display:none;">
|
367
|
+
This chart shows all the garbage collections that happened between the time
|
368
|
+
of the first and the last javacore in the data set.
|
369
|
+
Garbage collections that happened before the first
|
370
|
+
or after the last javacore generation time are not included.
|
371
|
+
<ul>
|
372
|
+
<li><strong>Heap Usage</strong>
|
373
|
+
is the available Java heap memory over time,
|
374
|
+
based on the garbage collection data from the verbose GC log files.
|
375
|
+
</li>
|
376
|
+
<li><strong>Total Heap</strong>
|
377
|
+
is the maximum size of the Java heap, configured by using the Xmx Java argument,
|
378
|
+
expressed in megabytes.
|
379
|
+
</li>
|
380
|
+
</ul>
|
381
|
+
</div>
|
382
|
+
<div id="systemresources_myChartGC" class="chart-container hide">
|
383
|
+
<canvas id="myChartGC" height="200"></canvas>
|
384
|
+
</div>
|
385
|
+
</xsl:otherwise>
|
386
|
+
</xsl:choose>
|
387
|
+
</xsl:when>
|
388
|
+
<xsl:otherwise>
|
389
|
+
<br/>
|
390
|
+
No verbosegc logs were provided
|
391
|
+
</xsl:otherwise>
|
392
|
+
</xsl:choose>
|
377
393
|
<h4>CPU Load</h4>
|
378
394
|
<a id="togglecpuloaddoc" href="javascript:expand_it(cpuloaddoc,togglecpuloaddoc)" class="expandit">
|
379
395
|
What does this chart tell me?</a>
|
javacore_analyser/javacore.py
CHANGED
@@ -127,7 +127,8 @@ class Javacore:
|
|
127
127
|
def encode(self, string):
|
128
128
|
bts = str.encode(string, self.get_encoding(), 'ignore')
|
129
129
|
for i in range(0, len(bts)):
|
130
|
-
|
130
|
+
# fix for 'XML Syntax error PCDATA invalid char#405'
|
131
|
+
if bts[i] < 32 and bts[i] != 9 and bts[i] != 10 and bts[i] != 13 and bts[i] != 1:
|
131
132
|
raise CorruptedJavacoreException("Javacore " + self.filename + " is corrupted in line " + string)
|
132
133
|
string = bts.decode('utf-8', 'ignore')
|
133
134
|
return string
|
@@ -145,7 +146,7 @@ class Javacore:
|
|
145
146
|
break
|
146
147
|
line = self.encode(line)
|
147
148
|
if line.startswith(THREAD_INFO):
|
148
|
-
line =
|
149
|
+
line = Javacore.process_thread_name(line, file)
|
149
150
|
snapshot = ThreadSnapshot.create(line, file, self)
|
150
151
|
self.snapshots.append(snapshot)
|
151
152
|
except Exception as e:
|
@@ -158,7 +159,8 @@ class Javacore:
|
|
158
159
|
finally:
|
159
160
|
file.close()
|
160
161
|
|
161
|
-
|
162
|
+
@staticmethod
|
163
|
+
def process_thread_name(line, file):
|
162
164
|
count = line.count('"')
|
163
165
|
if count == 0: return line # anonymous native threads
|
164
166
|
while True:
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright IBM Corp. 2024 -
|
2
|
+
# Copyright IBM Corp. 2024 - 2025
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
4
4
|
#
|
5
5
|
|
@@ -12,10 +12,11 @@ import shutil
|
|
12
12
|
import sys
|
13
13
|
import tarfile
|
14
14
|
import tempfile
|
15
|
-
import traceback
|
16
15
|
import zipfile
|
17
16
|
|
17
|
+
import importlib_resources
|
18
18
|
import py7zr
|
19
|
+
from importlib_resources.abc import Traversable
|
19
20
|
|
20
21
|
from javacore_analyser import logging_utils
|
21
22
|
from javacore_analyser.constants import DEFAULT_FILE_DELIMITER
|
@@ -63,43 +64,49 @@ def extract_archive(input_archive_filename, output_path):
|
|
63
64
|
|
64
65
|
|
65
66
|
def main():
|
66
|
-
logging_utils.create_console_logging()
|
67
|
-
logging.info("IBM Javacore analyser")
|
68
|
-
logging.info("Python version: " + sys.version)
|
69
|
-
logging.info("Preferred encoding: " + locale.getpreferredencoding())
|
70
|
-
|
71
67
|
parser = argparse.ArgumentParser()
|
72
|
-
parser.add_argument("
|
73
|
-
|
68
|
+
parser.add_argument("input", help="Input javacore file(s) or directory with javacores. "
|
69
|
+
"The javacores can be packed "
|
70
|
+
"into one of the supported archive formats: zip, gz, bz2, lzma, 7z. "
|
71
|
+
"Additional the verbose GC logs from the time "
|
72
|
+
"when the javacores were collected can be added. "
|
73
|
+
"See doc: https://github.com/IBM/javacore-analyser/wiki")
|
74
|
+
parser.add_argument("output", help="Name of directory where report will be generated")
|
74
75
|
parser.add_argument("--separator",
|
75
76
|
help='Input files separator (default "' + DEFAULT_FILE_DELIMITER + '")',
|
76
77
|
default=DEFAULT_FILE_DELIMITER)
|
77
78
|
args = parser.parse_args()
|
78
79
|
|
79
|
-
input_param = args.
|
80
|
-
output_param = args.
|
80
|
+
input_param = args.input
|
81
|
+
output_param = args.output
|
81
82
|
files_separator = args.separator
|
82
83
|
|
84
|
+
batch_process(input_param, output_param, files_separator)
|
85
|
+
|
86
|
+
|
87
|
+
def batch_process(input_param, output_param, files_separator=DEFAULT_FILE_DELIMITER):
|
88
|
+
logging_utils.create_console_logging()
|
89
|
+
logging.info("IBM Javacore analyser")
|
90
|
+
logging.info("Python version: " + sys.version)
|
91
|
+
logging.info("Preferred encoding: " + locale.getpreferredencoding())
|
83
92
|
logging.info("Input parameter: " + input_param)
|
84
93
|
logging.info("Report directory: " + output_param)
|
85
|
-
|
94
|
+
output_param = os.path.normpath(output_param)
|
86
95
|
# Needs to be created once output file structure is ready.
|
87
96
|
logging_utils.create_file_logging(output_param)
|
88
|
-
|
89
97
|
# Check whether as input we got list of files or single file
|
90
98
|
# Semicolon is separation mark for list of input files
|
91
99
|
if files_separator in input_param or fnmatch.fnmatch(input_param, '*javacore*.txt'):
|
92
|
-
# Process list of the files (copy all or them to output dir
|
100
|
+
# Process list of the files (copy all or them to output dir)
|
93
101
|
files = input_param.split(files_separator)
|
94
102
|
else:
|
95
103
|
files = [input_param]
|
96
|
-
|
97
104
|
try:
|
105
|
+
files = [os.path.normpath(file) for file in files]
|
98
106
|
process_javacores_and_generate_report_data(files, output_param)
|
99
107
|
except Exception as ex:
|
100
|
-
|
101
|
-
logging.error("Processing was not successful. Correct the problem and try again. Exiting with error 13"
|
102
|
-
exc_info=True)
|
108
|
+
logging.exception(ex)
|
109
|
+
logging.error("Processing was not successful. Correct the problem and try again. Exiting with error 13")
|
103
110
|
exit(13)
|
104
111
|
|
105
112
|
|
@@ -115,12 +122,9 @@ def generate_javecore_set_data(files):
|
|
115
122
|
Returns:
|
116
123
|
- JavacoreSet: Generated JavacoreSet object containing the processed data.
|
117
124
|
"""
|
118
|
-
|
119
|
-
|
125
|
+
javacores_temp_dir = tempfile.TemporaryDirectory()
|
120
126
|
try:
|
121
127
|
# Location when we store extracted archive or copied javacores files
|
122
|
-
javacores_temp_dir = tempfile.TemporaryDirectory()
|
123
|
-
|
124
128
|
javacores_temp_dir_name = javacores_temp_dir.name
|
125
129
|
for file in files:
|
126
130
|
if os.path.isdir(file):
|
@@ -137,6 +141,24 @@ def generate_javecore_set_data(files):
|
|
137
141
|
javacores_temp_dir.cleanup()
|
138
142
|
|
139
143
|
|
144
|
+
def create_output_files_structure(output_dir):
|
145
|
+
if not os.path.isdir(output_dir):
|
146
|
+
os.mkdir(output_dir)
|
147
|
+
data_output_dir = os.path.normpath(os.path.join(output_dir, 'data'))
|
148
|
+
if not data_output_dir.startswith(output_dir):
|
149
|
+
raise Exception("Security exception: Uncontrolled data used in path expression")
|
150
|
+
if os.path.isdir(data_output_dir):
|
151
|
+
shutil.rmtree(data_output_dir, ignore_errors=True)
|
152
|
+
logging.info("Data dir: " + data_output_dir)
|
153
|
+
|
154
|
+
style_css_resource: Traversable = importlib_resources.files("javacore_analyser") / "data" / "style.css"
|
155
|
+
data_dir = os.path.dirname(str(style_css_resource))
|
156
|
+
os.mkdir(data_output_dir)
|
157
|
+
shutil.copytree(data_dir, data_output_dir, dirs_exist_ok=True)
|
158
|
+
shutil.copy2(os.path.join(data_output_dir, "html", "processing_data.html"),
|
159
|
+
os.path.join(output_dir, "index.html"))
|
160
|
+
|
161
|
+
|
140
162
|
# Assisted by WCA@IBM
|
141
163
|
# Latest GenAI contribution: ibm/granite-8b-code-instruct
|
142
164
|
def process_javacores_and_generate_report_data(input_files, output_dir):
|
@@ -150,6 +172,7 @@ def process_javacores_and_generate_report_data(input_files, output_dir):
|
|
150
172
|
Returns:
|
151
173
|
None
|
152
174
|
"""
|
175
|
+
create_output_files_structure(output_dir)
|
153
176
|
javacore_set = generate_javecore_set_data(input_files)
|
154
177
|
javacore_set.generate_report_files(output_dir)
|
155
178
|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# Copyright IBM Corp. 2024 - 2024
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
4
4
|
#
|
5
|
+
import argparse
|
5
6
|
import locale
|
6
7
|
import logging
|
7
8
|
import os
|
@@ -9,6 +10,7 @@ import re
|
|
9
10
|
import shutil
|
10
11
|
import sys
|
11
12
|
import tempfile
|
13
|
+
import threading
|
12
14
|
import time
|
13
15
|
from pathlib import Path
|
14
16
|
|
@@ -16,29 +18,30 @@ from flask import Flask, render_template, request, send_from_directory, redirect
|
|
16
18
|
from waitress import serve
|
17
19
|
|
18
20
|
import javacore_analyser.javacore_analyser_batch
|
19
|
-
from javacore_analyser.constants import DEFAULT_REPORTS_DIR, DEFAULT_PORT
|
21
|
+
from javacore_analyser.constants import DEFAULT_REPORTS_DIR, DEFAULT_PORT, TEMP_DIR
|
20
22
|
from javacore_analyser.logging_utils import create_console_logging, create_file_logging
|
21
23
|
|
22
24
|
"""
|
23
25
|
To run the application from cmd type:
|
24
|
-
|
26
|
+
|
25
27
|
flask --app javacore_analyser_web run
|
26
28
|
"""
|
27
29
|
app = Flask(__name__)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
reports_dir = DEFAULT_REPORTS_DIR
|
31
|
+
|
32
|
+
|
33
|
+
# Assisted by watsonx Code Assistant
|
34
|
+
def create_temp_data_in_reports_dir(directory):
|
35
|
+
tmp_reports_dir = os.path.join(directory, TEMP_DIR)
|
36
|
+
if os.path.isdir(tmp_reports_dir):
|
37
|
+
shutil.rmtree(tmp_reports_dir, ignore_errors=True)
|
38
|
+
os.mkdir(tmp_reports_dir)
|
36
39
|
|
37
40
|
|
38
41
|
@app.route('/')
|
39
42
|
def index():
|
40
43
|
reports = [{"name": Path(f).name, "date": time.ctime(os.path.getctime(f)), "timestamp": os.path.getctime(f)}
|
41
|
-
for f in os.scandir(reports_dir) if f.is_dir()]
|
44
|
+
for f in os.scandir(reports_dir) if f.is_dir() and Path(f).name is not TEMP_DIR]
|
42
45
|
reports.sort(key=lambda item: item["timestamp"], reverse=True)
|
43
46
|
return render_template('index.html', reports=reports)
|
44
47
|
|
@@ -50,8 +53,8 @@ def dir_listing(path):
|
|
50
53
|
|
51
54
|
@app.route('/zip/<path:path>')
|
52
55
|
def compress(path):
|
56
|
+
temp_zip_dir = tempfile.TemporaryDirectory()
|
53
57
|
try:
|
54
|
-
temp_zip_dir = tempfile.TemporaryDirectory()
|
55
58
|
temp_zip_dir_name = temp_zip_dir.name
|
56
59
|
zip_filename = path + ".zip"
|
57
60
|
report_location = os.path.join(reports_dir, path)
|
@@ -68,7 +71,7 @@ def compress(path):
|
|
68
71
|
def delete(path):
|
69
72
|
# Checking if the report exists. This is to prevent attempt to delete any data by deleting any file outside
|
70
73
|
# report dir if you prepare path variable.
|
71
|
-
reports_list = os.listdir(reports_dir)
|
74
|
+
# reports_list = os.listdir(reports_dir)
|
72
75
|
report_location = os.path.normpath(os.path.join(reports_dir, path))
|
73
76
|
if not report_location.startswith(reports_dir):
|
74
77
|
logging.error("Deleted report in report list. Not deleting")
|
@@ -82,10 +85,19 @@ def delete(path):
|
|
82
85
|
# Latest GenAI contribution: ibm/granite-20b-code-instruct-v2
|
83
86
|
@app.route('/upload', methods=['POST'])
|
84
87
|
def upload_file():
|
88
|
+
|
89
|
+
report_name = request.values.get("report_name")
|
90
|
+
report_name = re.sub(r'[^a-zA-Z0-9]', '_', report_name)
|
91
|
+
|
92
|
+
# Create a temporary directory to store uploaded files
|
93
|
+
# Note We have to use permanent files and then delete them.
|
94
|
+
# tempfile.Temporary_directory function does not work when you want to access files from another threads.
|
95
|
+
javacores_temp_dir_name = os.path.normpath(os.path.join(reports_dir, TEMP_DIR, report_name))
|
96
|
+
if not javacores_temp_dir_name.startswith(reports_dir):
|
97
|
+
raise Exception("Security exception: Uncontrolled data used in path expression")
|
98
|
+
|
85
99
|
try:
|
86
|
-
|
87
|
-
javacores_temp_dir = tempfile.TemporaryDirectory()
|
88
|
-
javacores_temp_dir_name = javacores_temp_dir.name
|
100
|
+
os.mkdir(javacores_temp_dir_name)
|
89
101
|
|
90
102
|
# Get the list of files from webpage
|
91
103
|
files = request.files.getlist("files")
|
@@ -97,26 +109,50 @@ def upload_file():
|
|
97
109
|
file.save(file_name)
|
98
110
|
input_files.append(file_name)
|
99
111
|
|
100
|
-
report_name = request.values.get("report_name")
|
101
|
-
report_name = re.sub(r'[^a-zA-Z0-9]', '_', report_name)
|
102
|
-
|
103
112
|
# Process the uploaded file
|
104
|
-
report_output_dir = reports_dir
|
105
|
-
|
106
|
-
|
107
|
-
|
113
|
+
report_output_dir = os.path.join(reports_dir, report_name)
|
114
|
+
processing_thread = threading.Thread(
|
115
|
+
target=javacore_analyser.javacore_analyser_batch.process_javacores_and_generate_report_data,
|
116
|
+
name="Processing javacore data", args=(input_files, report_output_dir)
|
117
|
+
)
|
118
|
+
processing_thread.start()
|
119
|
+
|
120
|
+
time.sleep(1) # Give 1 second to generate index.html in processing_thread before redirecting
|
108
121
|
return redirect("/reports/" + report_name + "/index.html")
|
109
122
|
finally:
|
110
|
-
|
123
|
+
shutil.rmtree(javacores_temp_dir_name, ignore_errors=True)
|
124
|
+
|
111
125
|
|
112
126
|
def main():
|
113
|
-
|
114
|
-
|
127
|
+
parser = argparse.ArgumentParser()
|
128
|
+
parser.add_argument("--debug", help="Debug mode. Use only for development", default=False)
|
129
|
+
parser.add_argument("--port", help="Port to run application", default=DEFAULT_PORT)
|
130
|
+
parser.add_argument("--reports-dir", help="Directory where app reports are stored",
|
131
|
+
default=DEFAULT_REPORTS_DIR)
|
132
|
+
args = parser.parse_args()
|
133
|
+
debug = args.debug
|
134
|
+
port = args.port
|
135
|
+
reports_directory = args.reports_dir
|
136
|
+
|
137
|
+
run_web(debug, port, reports_directory)
|
138
|
+
|
139
|
+
|
140
|
+
def run_web(debug=False, port=5000, reports_directory=DEFAULT_REPORTS_DIR):
|
141
|
+
global reports_dir
|
142
|
+
reports_dir = reports_directory
|
143
|
+
create_console_logging()
|
144
|
+
logging.info("Javacore analyser")
|
145
|
+
logging.info("Python version: " + sys.version)
|
146
|
+
logging.info("Preferred encoding: " + locale.getpreferredencoding())
|
147
|
+
logging.info("Reports directory: " + reports_dir)
|
148
|
+
create_file_logging(reports_dir)
|
149
|
+
create_temp_data_in_reports_dir(reports_dir)
|
115
150
|
if debug:
|
116
151
|
app.run(debug=True, port=port) # Run Flask for development
|
117
152
|
else:
|
118
153
|
serve(app, port=port) # Run Waitress in production
|
119
154
|
|
155
|
+
|
120
156
|
if __name__ == '__main__':
|
121
157
|
"""
|
122
158
|
The application passes the following environmental variables:
|
@@ -15,7 +15,6 @@ from pathlib import Path
|
|
15
15
|
from xml.dom.minidom import parseString
|
16
16
|
|
17
17
|
import importlib_resources
|
18
|
-
from importlib_resources.abc import Traversable
|
19
18
|
from lxml import etree
|
20
19
|
from lxml.etree import XMLSyntaxError
|
21
20
|
from tqdm import tqdm
|
@@ -34,7 +33,7 @@ def _create_xml_xsl_for_collection(tmp_dir, templates_dir, xml_xsl_filename, col
|
|
34
33
|
logging.info("Creating xmls and xsls in " + tmp_dir)
|
35
34
|
os.mkdir(tmp_dir)
|
36
35
|
extensions = [".xsl", ".xml"]
|
37
|
-
for extension in extensions:
|
36
|
+
for extension in tqdm(extensions, desc="Creating xml/xsl files", unit=" file"):
|
38
37
|
file_full_path = os.path.normpath(os.path.join(templates_dir, xml_xsl_filename + extension))
|
39
38
|
if not file_full_path.startswith(templates_dir):
|
40
39
|
raise Exception("Security exception: Uncontrolled data used in path expression")
|
@@ -91,6 +90,7 @@ class JavacoreSet:
|
|
91
90
|
|
92
91
|
# Assisted by WCA@IBM
|
93
92
|
# Latest GenAI contribution: ibm/granite-8b-code-instruct
|
93
|
+
@staticmethod
|
94
94
|
def process_javacores(input_path):
|
95
95
|
"""
|
96
96
|
Processes Java core data and generates tips based on the analysis.
|
@@ -126,26 +126,30 @@ class JavacoreSet:
|
|
126
126
|
temp_dir = tempfile.TemporaryDirectory()
|
127
127
|
temp_dir_name = temp_dir.name
|
128
128
|
logging.info("Created temp dir: " + temp_dir_name)
|
129
|
-
self.__create_output_files_structure(output_dir)
|
130
129
|
self.__create_report_xml(temp_dir_name + "/report.xml")
|
130
|
+
placeholder_filename = os.path.join(output_dir, "data", "html", "processing_data.html")
|
131
|
+
self.__generate_placeholder_htmls(placeholder_filename,
|
132
|
+
os.path.join(output_dir, "threads"),
|
133
|
+
self.threads, "thread")
|
134
|
+
self.__generate_placeholder_htmls(placeholder_filename,
|
135
|
+
os.path.join(output_dir, "javacores"),
|
136
|
+
self.javacores, "")
|
137
|
+
self.__create_index_html(temp_dir_name, output_dir)
|
131
138
|
self.__generate_htmls_for_threads(output_dir, temp_dir_name)
|
132
139
|
self.__generate_htmls_for_javacores(output_dir, temp_dir_name)
|
133
|
-
self.__create_index_html(temp_dir_name, output_dir)
|
134
140
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
raise Exception("Security exception: Uncontrolled data used in path expression")
|
141
|
-
if os.path.isdir(data_output_dir):
|
142
|
-
shutil.rmtree(data_output_dir, ignore_errors=True)
|
143
|
-
logging.info("Data dir: " + data_output_dir)
|
141
|
+
@staticmethod
|
142
|
+
def __generate_placeholder_htmls(placeholder_file, directory, collection, file_prefix):
|
143
|
+
if os.path.exists(directory):
|
144
|
+
shutil.rmtree(directory)
|
145
|
+
os.mkdir(directory)
|
144
146
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
147
|
+
for element in tqdm(collection, desc="Generating placeholder htmls", unit=" file"):
|
148
|
+
filename = file_prefix + "_" + element.get_id() + ".html"
|
149
|
+
if filename.startswith("_"):
|
150
|
+
filename = filename[1:]
|
151
|
+
file_path = os.path.join(directory, filename)
|
152
|
+
shutil.copy2(placeholder_file, file_path)
|
149
153
|
|
150
154
|
def __generate_htmls_for_threads(self, output_dir, temp_dir_name):
|
151
155
|
_create_xml_xsl_for_collection(os.path.join(temp_dir_name, "threads"),
|
@@ -168,7 +172,7 @@ class JavacoreSet:
|
|
168
172
|
def populate_snapshot_collections(self):
|
169
173
|
for javacore in self.javacores:
|
170
174
|
javacore.print_javacore()
|
171
|
-
for s in javacore.snapshots:
|
175
|
+
for s in tqdm(javacore.snapshots, desc="Populating snapshot collection", unit=" javacore"):
|
172
176
|
self.threads.add_snapshot(s)
|
173
177
|
self.stacks.add_snapshot(s)
|
174
178
|
|
@@ -236,6 +240,7 @@ class JavacoreSet:
|
|
236
240
|
filename = os.path.join(self.path, filename)
|
237
241
|
curr_line = ""
|
238
242
|
i = 0
|
243
|
+
file = None
|
239
244
|
try:
|
240
245
|
file = open(filename, 'r')
|
241
246
|
for line in file:
|
@@ -255,17 +260,18 @@ class JavacoreSet:
|
|
255
260
|
elif line.startswith(JAVA_VERSION):
|
256
261
|
self.java_version = line[len(JAVA_VERSION) + 1:].strip()
|
257
262
|
continue
|
258
|
-
except Exception as
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
+
except Exception as ex:
|
264
|
+
logging.exception(ex)
|
265
|
+
logging.error(f'Error during processing file: {file.name} \n'
|
266
|
+
f'line number: {i} \n'
|
267
|
+
f'line: {curr_line}\n'
|
268
|
+
f'Check the exception below what happened')
|
263
269
|
finally:
|
264
270
|
file.close()
|
265
271
|
|
266
272
|
def parse_javacores(self):
|
267
273
|
""" creates a Javacore object for each javacore...txt file in the given path """
|
268
|
-
for filename in self.files:
|
274
|
+
for filename in tqdm(self.files, "Parsing javacore files", unit=" file"):
|
269
275
|
filename = os.path.join(self.path, filename)
|
270
276
|
javacore = Javacore()
|
271
277
|
javacore.create(filename, self)
|
@@ -285,7 +291,7 @@ class JavacoreSet:
|
|
285
291
|
# return None
|
286
292
|
|
287
293
|
def sort_snapshots(self):
|
288
|
-
for thread in self.threads:
|
294
|
+
for thread in tqdm(self.threads, "Sorting snapshot data", unit=" snapshot"):
|
289
295
|
thread.sort_snapshots()
|
290
296
|
# thread.compare_call_stacks()
|
291
297
|
|
@@ -316,7 +322,7 @@ class JavacoreSet:
|
|
316
322
|
def print_thread_states(self):
|
317
323
|
for thread in self.threads:
|
318
324
|
logging.debug("max running states:" + str(thread.get_continuous_running_states()))
|
319
|
-
logging.debug(thread.name + "(id: " + str(thread.id) + "; hash: " + thread.get_hash() + ") " +
|
325
|
+
logging.debug(thread.name + "(id: " + str(thread.id) + "; hash: " + thread.get_hash() + ") " +
|
320
326
|
"states: " + thread.get_snapshot_states())
|
321
327
|
|
322
328
|
# Assisted by WCA@IBM
|
@@ -379,6 +385,8 @@ class JavacoreSet:
|
|
379
385
|
|
380
386
|
verbose_gc_list_node = self.doc.createElement("verbose_gc_list")
|
381
387
|
report_info_node.appendChild(verbose_gc_list_node)
|
388
|
+
|
389
|
+
total_collects_in_time_limits = 0
|
382
390
|
for vgc in self.gc_parser.get_files():
|
383
391
|
verbose_gc_node = self.doc.createElement("verbose_gc")
|
384
392
|
verbose_gc_list_node.appendChild(verbose_gc_node)
|
@@ -388,9 +396,11 @@ class JavacoreSet:
|
|
388
396
|
verbose_gc_collects_node = self.doc.createElement("verbose_gc_collects")
|
389
397
|
verbose_gc_node.appendChild(verbose_gc_collects_node)
|
390
398
|
verbose_gc_collects_node.appendChild(self.doc.createTextNode(str(vgc.get_number_of_collects())))
|
399
|
+
total_collects_in_time_limits += vgc.get_number_of_collects()
|
391
400
|
verbose_gc_total_collects_node = self.doc.createElement("verbose_gc_total_collects")
|
392
401
|
verbose_gc_node.appendChild(verbose_gc_total_collects_node)
|
393
402
|
verbose_gc_total_collects_node.appendChild(self.doc.createTextNode(str(vgc.get_total_number_of_collects())))
|
403
|
+
verbose_gc_list_node.setAttribute("total_collects_in_time_limits", str(total_collects_in_time_limits))
|
394
404
|
|
395
405
|
system_info_node = self.doc.createElement("system_info")
|
396
406
|
doc_node.appendChild(system_info_node)
|
@@ -476,6 +486,7 @@ class JavacoreSet:
|
|
476
486
|
Returns:
|
477
487
|
str: The JavaCore set in the XML format.
|
478
488
|
"""
|
489
|
+
file = None
|
479
490
|
try:
|
480
491
|
file = open(self.report_xml_file, "r")
|
481
492
|
content = file.read()
|
@@ -494,11 +505,12 @@ class JavacoreSet:
|
|
494
505
|
def __create_index_html(input_dir, output_dir):
|
495
506
|
|
496
507
|
# Copy index.xml and report.xsl to temp - for index.html we don't need to generate anything. Copying is enough.
|
497
|
-
#index_xml = validate_uncontrolled_data_used_in_path([output_dir, "data", "xml", "index.xml"])
|
498
|
-
index_xml = os.path.normpath(importlib_resources.files("javacore_analyser") / "data" / "xml" / "index.xml")
|
508
|
+
# index_xml = validate_uncontrolled_data_used_in_path([output_dir, "data", "xml", "index.xml"])
|
509
|
+
index_xml = os.path.normpath(str(importlib_resources.files("javacore_analyser") / "data" / "xml" / "index.xml"))
|
499
510
|
shutil.copy2(index_xml, input_dir)
|
500
511
|
|
501
|
-
report_xsl = os.path.normpath(
|
512
|
+
report_xsl = os.path.normpath(
|
513
|
+
str(importlib_resources.files("javacore_analyser") / "data" / "xml" / "report.xsl"))
|
502
514
|
shutil.copy2(report_xsl, input_dir)
|
503
515
|
|
504
516
|
xslt_doc = etree.parse(input_dir + "/report.xsl")
|
@@ -521,6 +533,10 @@ class JavacoreSet:
|
|
521
533
|
os.mkdir(output_dir)
|
522
534
|
shutil.copy2(report_xml_file, data_input_dir)
|
523
535
|
|
536
|
+
# https://docs.python.org/3.8/library/multiprocessing.html
|
537
|
+
threads_no = JavacoreSet.get_number_of_parallel_threads()
|
538
|
+
logging.info(f"Using {threads_no} threads to generate html files")
|
539
|
+
|
524
540
|
list_files = os.listdir(data_input_dir)
|
525
541
|
progress_bar = tqdm(desc="Generating html files", unit=' files')
|
526
542
|
|
@@ -529,9 +545,6 @@ class JavacoreSet:
|
|
529
545
|
for file in list_files:
|
530
546
|
generate_html_from_xml_xsl_files_params.append((file, data_input_dir, output_dir, progress_bar))
|
531
547
|
|
532
|
-
# https://docs.python.org/3.8/library/multiprocessing.html
|
533
|
-
threads_no = JavacoreSet.get_number_of_parallel_threads()
|
534
|
-
logging.info(f"Using {threads_no} threads to generate html files")
|
535
548
|
with Pool(threads_no) as p:
|
536
549
|
p.map(JavacoreSet.generate_html_from_xml_xsl_files, generate_html_from_xml_xsl_files_params)
|
537
550
|
|
@@ -575,7 +588,8 @@ class JavacoreSet:
|
|
575
588
|
|
576
589
|
progress_bar.update(1)
|
577
590
|
|
578
|
-
|
591
|
+
@staticmethod
|
592
|
+
def create_xml_xsl_for_collection(tmp_dir, xml_xsls_prefix_path, collection, output_file_prefix):
|
579
593
|
logging.info("Creating xmls and xsls in " + tmp_dir)
|
580
594
|
os.mkdir(tmp_dir)
|
581
595
|
extensions = [".xsl", ".xml"]
|
@@ -595,7 +609,7 @@ class JavacoreSet:
|
|
595
609
|
@staticmethod
|
596
610
|
def parse_mem_arg(line):
|
597
611
|
line = line.split()[-1] # avoid matching the '2' in tag name 2CIUSERARG
|
598
|
-
tokens = re.findall("
|
612
|
+
tokens = re.findall("\d+[KkMmGg]?$", line)
|
599
613
|
if len(tokens) != 1: return UNKNOWN
|
600
614
|
return tokens[0]
|
601
615
|
|
@@ -2,6 +2,8 @@
|
|
2
2
|
# Copyright IBM Corp. 2024 - 2024
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
4
4
|
#
|
5
|
+
from tqdm import tqdm
|
6
|
+
|
5
7
|
|
6
8
|
class SnapshotCollectionCollection:
|
7
9
|
|
@@ -23,15 +25,15 @@ class SnapshotCollectionCollection:
|
|
23
25
|
def __iter__(self):
|
24
26
|
return self.snapshot_collections.__iter__()
|
25
27
|
|
26
|
-
def __next__(self):
|
27
|
-
|
28
|
+
# def __next__(self):
|
29
|
+
# return self.snapshot_collections.__next__()
|
28
30
|
|
29
31
|
def get_xml(self, doc):
|
30
32
|
info_node = doc.createElement(self.snapshot_collection_type.__name__)
|
31
33
|
|
32
34
|
all_threads_node = doc.createElement('all_snapshot_collection')
|
33
35
|
info_node.appendChild(all_threads_node)
|
34
|
-
for collection in self.snapshot_collections:
|
36
|
+
for collection in tqdm(self.snapshot_collections, desc=" Generating threads data", unit=" thread"):
|
35
37
|
all_threads_node.appendChild(collection.get_xml(doc))
|
36
38
|
|
37
39
|
return info_node
|
javacore_analyser/stack_trace.py
CHANGED
@@ -26,8 +26,8 @@ class StackTrace:
|
|
26
26
|
def __iter__(self):
|
27
27
|
return self.stack_trace_elements.__iter__()
|
28
28
|
|
29
|
-
def __next__(self):
|
30
|
-
|
29
|
+
# def __next__(self):
|
30
|
+
# return self.stack_trace_elements.__next__()
|
31
31
|
|
32
32
|
def equals(self, stack_trace):
|
33
33
|
self_stack_trace_size = len(self.stack_trace_elements)
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# Copyright IBM Corp. 2024 - 2024
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
4
4
|
#
|
5
|
-
|
5
|
+
import logging
|
6
6
|
import os
|
7
7
|
import re
|
8
8
|
from datetime import datetime
|
@@ -123,7 +123,8 @@ class ThreadSnapshot:
|
|
123
123
|
try:
|
124
124
|
token = m.group(1)
|
125
125
|
self.cpu_usage = float(token)
|
126
|
-
except:
|
126
|
+
except Exception as ex:
|
127
|
+
logging.warning(ex)
|
127
128
|
self.cpu_usage = 0
|
128
129
|
# tokens = re.findall("[0-9]+\.[0-9]+", line)
|
129
130
|
# if len(tokens) == 0:
|
javacore_analyser/tips.py
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
# Copyright IBM Corp. 2024 - 2024
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
4
4
|
#
|
5
|
+
import logging
|
5
6
|
|
6
7
|
# This is a module containing list of the tips.
|
7
8
|
# Each tip has to implement dynamic method generate(javacore_set)
|
@@ -16,6 +17,7 @@ class TestTip:
|
|
16
17
|
|
17
18
|
@staticmethod
|
18
19
|
def generate(javacore_set):
|
20
|
+
logging.info(javacore_set)
|
19
21
|
return ["this is a test tip. Ignore it."]
|
20
22
|
|
21
23
|
|
@@ -157,7 +159,7 @@ class BlockingThreadsTip:
|
|
157
159
|
result.append(BlockingThreadsTip.BLOCKING_THREADS_TEXT.format(blocker_name,
|
158
160
|
blocked_size / javacores_no))
|
159
161
|
if len(result) >= BlockingThreadsTip.MAX_BLOCKING_THREADS_NO:
|
160
|
-
break
|
162
|
+
break
|
161
163
|
return result
|
162
164
|
|
163
165
|
|
@@ -165,9 +167,9 @@ class HighCpuUsageTip:
|
|
165
167
|
# Generates the tip if the thread is using above x percent of CPU. Also informs, if this is verbose gc thread.
|
166
168
|
|
167
169
|
# Report as high cpu usage for the application using the cpu usage above this value
|
168
|
-
CRITICAL_CPU_USAGE = 50
|
170
|
+
CRITICAL_CPU_USAGE = 50
|
169
171
|
|
170
|
-
CRITICAL_USAGE_FOR_GC = 5
|
172
|
+
CRITICAL_USAGE_FOR_GC = 5
|
171
173
|
|
172
174
|
MAX_NUMBER_OF_HIGH_CPU_USAGE_THREADS = 5
|
173
175
|
|
javacore_analyser/verbose_gc.py
CHANGED
@@ -8,6 +8,8 @@ import ntpath
|
|
8
8
|
from datetime import datetime
|
9
9
|
from xml.dom.minidom import Element, parseString
|
10
10
|
|
11
|
+
from tqdm import tqdm
|
12
|
+
|
11
13
|
ROOT_CLOSING_TAG = "</verbosegc>"
|
12
14
|
GC_START = "gc-start"
|
13
15
|
GC_END = "gc-end"
|
@@ -43,7 +45,7 @@ class VerboseGcParser:
|
|
43
45
|
|
44
46
|
def parse_files(self, start_time, stop_time):
|
45
47
|
logging.info("Started parsing GC files")
|
46
|
-
for file_path in self.__file_paths:
|
48
|
+
for file_path in tqdm(self.__file_paths, desc="Parsing verbose gc", unit=" file"):
|
47
49
|
try:
|
48
50
|
collects_from_time_range = 0
|
49
51
|
file = VerboseGcFile(file_path)
|
@@ -109,6 +111,7 @@ class VerboseGcFile:
|
|
109
111
|
|
110
112
|
def __parse(self):
|
111
113
|
# read in the file as collection of lines
|
114
|
+
file = None
|
112
115
|
try:
|
113
116
|
xml_text = ""
|
114
117
|
root_closing_tag_available = False
|
@@ -152,7 +155,6 @@ class VerboseGcFile:
|
|
152
155
|
gets the total number of gc collections in this VerboseGcFile
|
153
156
|
regardless of the time when tey occurred with regards to the javacores
|
154
157
|
'''
|
155
|
-
|
156
158
|
def get_total_number_of_collects(self):
|
157
159
|
if self.__total_number_of_collects < 0:
|
158
160
|
self.get_collects()
|
@@ -1,10 +1,10 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: javacore_analyser
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.1
|
4
4
|
Summary: The tool to review IBM Javacore files
|
5
5
|
Project-URL: Homepage, https://github.com/IBM/javacore-analyser
|
6
6
|
Project-URL: Issues, https://github.com/IBM/javacore-analyser/issues
|
7
|
-
Project-URL: Changelog, https://github.com/IBM/javacore-analyser/CHANGELOG.md
|
7
|
+
Project-URL: Changelog, https://github.com/IBM/javacore-analyser/blob/main/CHANGELOG.md
|
8
8
|
Project-URL: Source, https://github.com/IBM/javacore-analyser
|
9
9
|
Author-email: Krzysztof Kazmierczyk <kazm@ibm.com>, Piotr Aniola <Piotr.Aniola@ibm.com>, Tadeusz Janasiewicz <t.janasiewicz@ibm.com>
|
10
10
|
License: Apache License
|
@@ -209,6 +209,7 @@ License: Apache License
|
|
209
209
|
See the License for the specific language governing permissions and
|
210
210
|
limitations under the License.
|
211
211
|
|
212
|
+
License-File: LICENSE
|
212
213
|
Classifier: Development Status :: 5 - Production/Stable
|
213
214
|
Classifier: Environment :: Console
|
214
215
|
Classifier: Environment :: Web Environment
|
@@ -238,8 +239,18 @@ Description-Content-Type: text/markdown
|
|
238
239
|
<!-- This should be the location of the title of the repository, normally the short name -->
|
239
240
|
# Javacore Analyser
|
240
241
|
|
242
|
+
![GitHub License](https://img.shields.io/github/license/IBM/javacore-analyser)
|
243
|
+
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/javacore-analyser)
|
244
|
+
![GitHub contributors](https://img.shields.io/github/contributors/IBM/javacore-analyser)
|
241
245
|
<!-- Build Status, is a great thing to have at the top of your repository, it shows that you take your CI/CD as first class citizens -->
|
242
246
|
[![Build Status](https://app.travis-ci.com/IBM/javacore-analyser.svg?token=w3i4X11XppEi2tJQsxDb&branch=main)](https://app.travis-ci.com/IBM/javacore-analyser)
|
247
|
+
![GitHub last commit](https://img.shields.io/github/last-commit/IBM/javacore-analyser)
|
248
|
+
![GitHub Release Date](https://img.shields.io/github/release-date/IBM/javacore-analyser)
|
249
|
+
![GitHub commit activity](https://img.shields.io/github/commit-activity/t/IBM/javacore-analyser)
|
250
|
+
![GitHub Issues or Pull Requests](https://img.shields.io/github/issues-pr/IBM/javacore-analyser)
|
251
|
+
![GitHub Issues or Pull Requests](https://img.shields.io/github/issues-pr-closed/IBM/javacore-analyser)
|
252
|
+
![PyPI - Downloads](https://img.shields.io/pypi/dm/javacore-analyser)
|
253
|
+
|
243
254
|
|
244
255
|
<!-- Not always needed, but a scope helps the user understand in a short sentance like below, why this repo exists -->
|
245
256
|
## Scope
|
@@ -266,7 +277,7 @@ Steps:
|
|
266
277
|
3. Run the following command:
|
267
278
|
`pip install javacore-analyser`
|
268
279
|
OR
|
269
|
-
`pip install --extra-index-url https://test.pypi.org/simple/ javacore-analyser` - if you want an experimental version
|
280
|
+
`pip install --pre --extra-index-url https://test.pypi.org/simple/ javacore-analyser` - if you want an experimental version
|
270
281
|
|
271
282
|
#### Installing from sources
|
272
283
|
This is recommended for geeks only:
|
@@ -280,29 +291,44 @@ This is recommended for geeks only:
|
|
280
291
|
#### Running cmd application:
|
281
292
|
1. Install application if not done yet
|
282
293
|
2. Activate your created virtual environment according to activate Virtual Environment according to [Creating virtual environments](https://docs.python.org/3/tutorial/venv.html#creating-virtual-environments)
|
283
|
-
3.
|
284
|
-
|
285
|
-
|
294
|
+
3. Run the following command from cmd:
|
295
|
+
`javacore-analyser-batch <input-data> <generated-reports-dir>`
|
296
|
+
or
|
297
|
+
`python -m javacore_analyser batch <input-data> <generated-reports-dir>`
|
298
|
+
|
286
299
|
Where `<input-data>` is one of the following:
|
287
300
|
* The directory containing javacores and optionally verbose gc
|
288
301
|
* Archive (7z, zip, tar.gz, tar.bz2) containing the same
|
289
302
|
* List of the javacores separated by `;` character. Optionally you can add `--separator` option to define your own separator.
|
290
303
|
You can type the following command to obtain the help:
|
291
|
-
`javacore-analyser-batch --help`
|
304
|
+
`javacore-analyser-batch --help` or `python -m javacore_analyser barch --help`
|
292
305
|
|
293
306
|
#### Running web application:
|
294
307
|
1. Repeat steps 1-3 from cmd application
|
295
|
-
2.
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
308
|
+
2. Execute the following command from cmd:
|
309
|
+
`javacore_analyser_web --port=5000 --reports-dir=/data/reports_dir`
|
310
|
+
or
|
311
|
+
`python -m javacore_analyser web --port=5000 --reports-dir=/data/reports_dir`
|
312
|
+
|
313
|
+
The first parameter set the port to use by application. If not specified, 5000 will be used.
|
314
|
+
The second parameter sets where the reports need to be stored. If not set, then the `reports` dir will be created in current location.
|
315
|
+
|
316
|
+
Now you can type (http://localhost:5000/).
|
317
|
+
|
318
|
+
### Running container image
|
319
|
+
There is an unofficial Docker/Podman container managed by one of projects developers. Use the following command
|
320
|
+
to start it:
|
321
|
+
|
322
|
+
`podman run -it --rm --name javacore-analyser --mount type=bind,src="/local-reports-dir",target=/reports -p 5001:5000 ghcr.io/kkazmierczyk/javacore-analyser:latest`
|
323
|
+
|
324
|
+
or
|
325
|
+
`docker run -it --rm --name javacore-analyser --mount type=bind,src="/local-reports-dir",target=/reports -p 5001:5000 ghcr.io/kkazmierczyk/javacore-analyser:latest`
|
326
|
+
|
327
|
+
The `mount` option specifies where you want locally to store the reports. The reports in the container are stored in
|
328
|
+
`/reports` directory. If you remove mount option, the application will work but the reports will not persist after
|
329
|
+
restart.
|
330
|
+
The application is running in the container on port 5000. By using `-p 5001:5000` option, you specify to map container
|
331
|
+
port 5000 to port 5001 on your machine. Therefore the application will be available under `http://localhost:5001/`.
|
306
332
|
|
307
333
|
<!-- The following are OPTIONAL, but strongly suggested to have in your repository. -->
|
308
334
|
<!--
|
@@ -1,23 +1,25 @@
|
|
1
1
|
javacore_analyser/__init__.py,sha256=Sw2ZeqcI2Kx1cDDv083n1SiSY_FDRCmidTuzaN1uRSw,76
|
2
|
-
javacore_analyser/
|
2
|
+
javacore_analyser/__main__.py,sha256=wQCPgu8Gp7XczyNckNGmY30c5YMUMRByW7jrdFO0OBY,1694
|
3
|
+
javacore_analyser/abstract_snapshot_collection.py,sha256=jGfd2XgujurRlKgEtlJjqNJK9sUvTdFsdgFnX9oLzt4,5589
|
3
4
|
javacore_analyser/code_snapshot_collection.py,sha256=6_C5myag5ocjOTwXVDbBamN6Lf1szgS3ylSHEEjUdVg,2655
|
4
|
-
javacore_analyser/constants.py,sha256=
|
5
|
+
javacore_analyser/constants.py,sha256=iGYAznPK8AySq6uqk-cpCU8Dbjbq6PrCh6q2mF8oeu8,1003
|
5
6
|
javacore_analyser/java_thread.py,sha256=4zUfmmlH47SrIxgfPmrJHl_YUziVJXVNVedD5X25vXY,4464
|
6
|
-
javacore_analyser/javacore.py,sha256=
|
7
|
-
javacore_analyser/javacore_analyser_batch.py,sha256=
|
8
|
-
javacore_analyser/javacore_analyser_web.py,sha256=
|
9
|
-
javacore_analyser/javacore_set.py,sha256=
|
7
|
+
javacore_analyser/javacore.py,sha256=_2abTyvXEaZ6Tx8r9d3NEzRCIBcf4Is8lSjpKyPu-R8,6884
|
8
|
+
javacore_analyser/javacore_analyser_batch.py,sha256=bz2ukMp-S8HX4-t8bjtiz5RSOVaeuztFp6pUTAmyM60,7058
|
9
|
+
javacore_analyser/javacore_analyser_web.py,sha256=o-Spq119Wi7w4pvBa7M7ZrxiZnzKmWqDhCdGtEReQAU,5951
|
10
|
+
javacore_analyser/javacore_set.py,sha256=81bKBwn4GHdPkKH4yb2j3z1HPKrys1KTBon1TTUH5gI,31469
|
10
11
|
javacore_analyser/logging_utils.py,sha256=vLob0ikezysjGv9XGqv9GbLekxu4eO_csq22M-gtLiQ,966
|
11
12
|
javacore_analyser/snapshot_collection.py,sha256=fLEnwg9-cOjVVUUludtzI7R2yO9BBVgJgxkhvqG5QDg,443
|
12
|
-
javacore_analyser/snapshot_collection_collection.py,sha256=
|
13
|
-
javacore_analyser/stack_trace.py,sha256=
|
14
|
-
javacore_analyser/stack_trace_element.py,sha256=
|
13
|
+
javacore_analyser/snapshot_collection_collection.py,sha256=1PV1TX4QQk01dAbX-k-kTpgKr6Il867Bw6X7HHBuv-Q,1346
|
14
|
+
javacore_analyser/stack_trace.py,sha256=8sb8z4ac_L0yyxqJX1ukrTZRyngkHcA3zkXyqxG5ygA,1664
|
15
|
+
javacore_analyser/stack_trace_element.py,sha256=pZPrK1ACBUDE7YsVOFhTfewXequ1m5P-B0N-9RuhkWo,1143
|
15
16
|
javacore_analyser/stack_trace_kind.py,sha256=lOdfb_F3XrwDLciPk_ZgM_fmMn5JoXsIUjr7pjvmU4M,157
|
16
|
-
javacore_analyser/thread_snapshot.py,sha256=
|
17
|
-
javacore_analyser/tips.py,sha256=
|
18
|
-
javacore_analyser/verbose_gc.py,sha256=
|
19
|
-
javacore_analyser/data/expand.js,sha256=
|
17
|
+
javacore_analyser/thread_snapshot.py,sha256=2Do8NPBXdpUezQrUI_9nyGbtU4b2s5euPnHqcuuKq7U,13255
|
18
|
+
javacore_analyser/tips.py,sha256=EhwLUAha0FvFJtO5kmvba9a1nKXGdqNHFa2jFbHZr4U,8655
|
19
|
+
javacore_analyser/verbose_gc.py,sha256=r5ZOZVhdrrgFTGo1Rx-FFPFNRpvX1jflP6AkNATMpIQ,6871
|
20
|
+
javacore_analyser/data/expand.js,sha256=KwqvNUoO7yMDeQKcnLDywfMdR3Zsjan5L8QoPsQQLGo,956
|
20
21
|
javacore_analyser/data/style.css,sha256=YzHl0NSnfRik4ar6AInp7pZ_re1rirQy6L5jqdbKoKg,2246
|
22
|
+
javacore_analyser/data/html/processing_data.html,sha256=S1S2GMXQ8oQdqdYTcPzVMxUib-NLtM4ffT3OJaIIO7w,421
|
21
23
|
javacore_analyser/data/jquery/chart.js,sha256=tgiW1vJqfIKxE0F2uVvsXbgUlTyrhPMY_sm30hh_Sxc,203464
|
22
24
|
javacore_analyser/data/jquery/chartjs-adapter-date-fns.bundle.min.js,sha256=6nqzDSbDjc8fLSa7Q-c6lFN7WPGQb1XhpUbdCTIbVhU,50650
|
23
25
|
javacore_analyser/data/jquery/jq.css,sha256=CXwJ7OOkFWXsebmAipjne1TnNanpp_2z241eLsVB7Ls,6751
|
@@ -25,20 +27,20 @@ javacore_analyser/data/jquery/jquery.mark.min.js,sha256=p_nep2K57or4Km0eXahvl6J_
|
|
25
27
|
javacore_analyser/data/jquery/jquery.min.js,sha256=pvPw-upLPUjgMXY0G-8O0xUf-_Im1MZjXxxgOcBQBXU,89947
|
26
28
|
javacore_analyser/data/jquery/jquery.tablesorter.min.js,sha256=dtGH1XcAyKopMui5x20KnPxuGuSx9Rs6piJB_4Oqu6I,44365
|
27
29
|
javacore_analyser/data/jquery/jquery.tablesorter.widgets.min.js,sha256=GxbszpUzg-iYIcyDGyNVLz9Y0dQvzmQgXXVk5cHJbw0,53100
|
28
|
-
javacore_analyser/data/jquery/search.js,sha256=
|
30
|
+
javacore_analyser/data/jquery/search.js,sha256=Jwi-cBJ9YKDHJwqIlcKXqrpcM1BX-wx93uKAR44JLww,4200
|
29
31
|
javacore_analyser/data/jquery/sorting.js,sha256=HsuVLa7F70IM4ZMXZpjj7wtVI1TXL1SPbZGWenv0Jp8,369
|
30
32
|
javacore_analyser/data/jquery/theme.blue.css,sha256=mI0RCGd6G5GOKSG7BPagp0N58xipSjPXUKvrcHJ4h1Q,7528
|
31
33
|
javacore_analyser/data/jquery/theme.default.min.css,sha256=5sgExNTnkN8NcApKIU73_aqgZmqq_zJp9-9zXf9aSEw,4502
|
32
|
-
javacore_analyser/data/jquery/wait2scripts.js,sha256=
|
34
|
+
javacore_analyser/data/jquery/wait2scripts.js,sha256=z6zmSAak0R6bjh7BeHlIOSImiZSy5Y3z5ygMiN-sPxI,8460
|
33
35
|
javacore_analyser/data/xml/index.xml,sha256=9VH2rmri3FQpXcW39kbyi2dON94C5XTiaQn0ioExCe8,282
|
34
|
-
javacore_analyser/data/xml/report.xsl,sha256=
|
36
|
+
javacore_analyser/data/xml/report.xsl,sha256=qJG0AvYWJSNOnhH5jmSwCPJH_4k5qwCJR23UqROcon4,51012
|
35
37
|
javacore_analyser/data/xml/javacores/javacore.xml,sha256=6dG89Whx1_kpEYVS_F6Upa2XuXnXorlQATFc8kD5Mfc,280
|
36
38
|
javacore_analyser/data/xml/javacores/javacore.xsl,sha256=5cnIp08Q9FccljHH8duoJQYofyW8lwUCGtpdzz5Y0Y8,11644
|
37
39
|
javacore_analyser/data/xml/threads/thread.xml,sha256=6dG89Whx1_kpEYVS_F6Upa2XuXnXorlQATFc8kD5Mfc,280
|
38
40
|
javacore_analyser/data/xml/threads/thread.xsl,sha256=rkqr5GQ2aZ_xrdhUjl2QZDCZ-09zxqUmtV8DFZVjTAA,13927
|
39
41
|
javacore_analyser/templates/index.html,sha256=aEuyry-HZ9HlQNwfbugugvqbSxwlo7LrQnrDmqO34YE,1682
|
40
|
-
javacore_analyser-2.
|
41
|
-
javacore_analyser-2.
|
42
|
-
javacore_analyser-2.
|
43
|
-
javacore_analyser-2.
|
44
|
-
javacore_analyser-2.
|
42
|
+
javacore_analyser-2.1.dist-info/METADATA,sha256=nsUGKcsweg5PjHtSOXyzr2rJ_GwAyr0tiaynpT9ptkM,22257
|
43
|
+
javacore_analyser-2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
44
|
+
javacore_analyser-2.1.dist-info/entry_points.txt,sha256=W3S799zI58g5-jWMsC3wY9xksz21LPEMYOILv8sayfM,160
|
45
|
+
javacore_analyser-2.1.dist-info/licenses/LICENSE,sha256=xllut76FgcGL5zbIRvuRc7aezPbvlMUTWJPsVr2Sugg,11358
|
46
|
+
javacore_analyser-2.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|