javacore-analyser 2.0rc3__py3-none-any.whl → 2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+

|
243
|
+

|
244
|
+

|
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
|
[](https://app.travis-ci.com/IBM/javacore-analyser)
|
247
|
+

|
248
|
+

|
249
|
+

|
250
|
+

|
251
|
+

|
252
|
+

|
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
|