javacore-analyser 2.0rc3__py3-none-any.whl → 2.1.0.dev66__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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/javacore.py +5 -3
- javacore_analyser/javacore_analyser_batch.py +34 -14
- javacore_analyser/javacore_analyser_web.py +58 -26
- javacore_analyser/javacore_set.py +44 -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 -1
- {javacore_analyser-2.0rc3.dist-info → javacore_analyser-2.1.0.dev66.dist-info}/METADATA +24 -14
- {javacore_analyser-2.0rc3.dist-info → javacore_analyser-2.1.0.dev66.dist-info}/RECORD +20 -19
- {javacore_analyser-2.0rc3.dist-info → javacore_analyser-2.1.0.dev66.dist-info}/WHEEL +1 -1
- {javacore_analyser-2.0rc3.dist-info → javacore_analyser-2.1.0.dev66.dist-info}/entry_points.txt +0 -0
- {javacore_analyser-2.0rc3.dist-info → javacore_analyser-2.1.0.dev66.dist-info}/licenses/LICENSE +0 -0
@@ -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
|
}
|
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:
|
@@ -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
|
@@ -69,15 +70,20 @@ def main():
|
|
69
70
|
logging.info("Preferred encoding: " + locale.getpreferredencoding())
|
70
71
|
|
71
72
|
parser = argparse.ArgumentParser()
|
72
|
-
parser.add_argument("
|
73
|
-
|
73
|
+
parser.add_argument("input", help="Input javacore file(s) or directory with javacores. "
|
74
|
+
"The javacores can be packed "
|
75
|
+
"into one of the supported archive formats: zip, gz, bz2, lzma, 7z. "
|
76
|
+
"Additional the verbose GC logs from the time "
|
77
|
+
"when the javacores were collected can be added. "
|
78
|
+
"See doc: https://github.com/IBM/javacore-analyser/wiki")
|
79
|
+
parser.add_argument("output", help="Name of directory where report will be generated")
|
74
80
|
parser.add_argument("--separator",
|
75
81
|
help='Input files separator (default "' + DEFAULT_FILE_DELIMITER + '")',
|
76
82
|
default=DEFAULT_FILE_DELIMITER)
|
77
83
|
args = parser.parse_args()
|
78
84
|
|
79
|
-
input_param = args.
|
80
|
-
output_param = args.
|
85
|
+
input_param = args.input
|
86
|
+
output_param = args.output
|
81
87
|
files_separator = args.separator
|
82
88
|
|
83
89
|
logging.info("Input parameter: " + input_param)
|
@@ -89,17 +95,15 @@ def main():
|
|
89
95
|
# Check whether as input we got list of files or single file
|
90
96
|
# Semicolon is separation mark for list of input files
|
91
97
|
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
|
98
|
+
# Process list of the files (copy all or them to output dir)
|
93
99
|
files = input_param.split(files_separator)
|
94
100
|
else:
|
95
101
|
files = [input_param]
|
96
|
-
|
97
102
|
try:
|
98
103
|
process_javacores_and_generate_report_data(files, output_param)
|
99
104
|
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)
|
105
|
+
logging.exception(ex)
|
106
|
+
logging.error("Processing was not successful. Correct the problem and try again. Exiting with error 13")
|
103
107
|
exit(13)
|
104
108
|
|
105
109
|
|
@@ -115,12 +119,9 @@ def generate_javecore_set_data(files):
|
|
115
119
|
Returns:
|
116
120
|
- JavacoreSet: Generated JavacoreSet object containing the processed data.
|
117
121
|
"""
|
118
|
-
|
119
|
-
|
122
|
+
javacores_temp_dir = tempfile.TemporaryDirectory()
|
120
123
|
try:
|
121
124
|
# Location when we store extracted archive or copied javacores files
|
122
|
-
javacores_temp_dir = tempfile.TemporaryDirectory()
|
123
|
-
|
124
125
|
javacores_temp_dir_name = javacores_temp_dir.name
|
125
126
|
for file in files:
|
126
127
|
if os.path.isdir(file):
|
@@ -137,6 +138,24 @@ def generate_javecore_set_data(files):
|
|
137
138
|
javacores_temp_dir.cleanup()
|
138
139
|
|
139
140
|
|
141
|
+
def create_output_files_structure(output_dir):
|
142
|
+
if not os.path.isdir(output_dir):
|
143
|
+
os.mkdir(output_dir)
|
144
|
+
data_output_dir = os.path.normpath(os.path.join(output_dir, 'data'))
|
145
|
+
if not data_output_dir.startswith(output_dir):
|
146
|
+
raise Exception("Security exception: Uncontrolled data used in path expression")
|
147
|
+
if os.path.isdir(data_output_dir):
|
148
|
+
shutil.rmtree(data_output_dir, ignore_errors=True)
|
149
|
+
logging.info("Data dir: " + data_output_dir)
|
150
|
+
|
151
|
+
style_css_resource: Traversable = importlib_resources.files("javacore_analyser") / "data" / "style.css"
|
152
|
+
data_dir = os.path.dirname(str(style_css_resource))
|
153
|
+
os.mkdir(data_output_dir)
|
154
|
+
shutil.copytree(data_dir, data_output_dir, dirs_exist_ok=True)
|
155
|
+
shutil.copy2(os.path.join(data_output_dir, "html", "processing_data.html"),
|
156
|
+
os.path.join(output_dir, "index.html"))
|
157
|
+
|
158
|
+
|
140
159
|
# Assisted by WCA@IBM
|
141
160
|
# Latest GenAI contribution: ibm/granite-8b-code-instruct
|
142
161
|
def process_javacores_and_generate_report_data(input_files, output_dir):
|
@@ -150,6 +169,7 @@ def process_javacores_and_generate_report_data(input_files, output_dir):
|
|
150
169
|
Returns:
|
151
170
|
None
|
152
171
|
"""
|
172
|
+
create_output_files_structure(output_dir)
|
153
173
|
javacore_set = generate_javecore_set_data(input_files)
|
154
174
|
javacore_set.generate_report_files(output_dir)
|
155
175
|
|
@@ -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,46 @@ 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
|
+
global reports_dir
|
136
|
+
reports_dir = args.reports_dir
|
137
|
+
|
138
|
+
create_console_logging()
|
139
|
+
logging.info("Javacore analyser")
|
140
|
+
logging.info("Python version: " + sys.version)
|
141
|
+
logging.info("Preferred encoding: " + locale.getpreferredencoding())
|
142
|
+
logging.info("Reports directory: " + reports_dir)
|
143
|
+
create_file_logging(reports_dir)
|
144
|
+
create_temp_data_in_reports_dir(reports_dir)
|
145
|
+
|
115
146
|
if debug:
|
116
147
|
app.run(debug=True, port=port) # Run Flask for development
|
117
148
|
else:
|
118
149
|
serve(app, port=port) # Run Waitress in production
|
119
150
|
|
151
|
+
|
120
152
|
if __name__ == '__main__':
|
121
153
|
"""
|
122
154
|
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
|
@@ -476,6 +482,7 @@ class JavacoreSet:
|
|
476
482
|
Returns:
|
477
483
|
str: The JavaCore set in the XML format.
|
478
484
|
"""
|
485
|
+
file = None
|
479
486
|
try:
|
480
487
|
file = open(self.report_xml_file, "r")
|
481
488
|
content = file.read()
|
@@ -494,11 +501,12 @@ class JavacoreSet:
|
|
494
501
|
def __create_index_html(input_dir, output_dir):
|
495
502
|
|
496
503
|
# 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")
|
504
|
+
# index_xml = validate_uncontrolled_data_used_in_path([output_dir, "data", "xml", "index.xml"])
|
505
|
+
index_xml = os.path.normpath(str(importlib_resources.files("javacore_analyser") / "data" / "xml" / "index.xml"))
|
499
506
|
shutil.copy2(index_xml, input_dir)
|
500
507
|
|
501
|
-
report_xsl = os.path.normpath(
|
508
|
+
report_xsl = os.path.normpath(
|
509
|
+
str(importlib_resources.files("javacore_analyser") / "data" / "xml" / "report.xsl"))
|
502
510
|
shutil.copy2(report_xsl, input_dir)
|
503
511
|
|
504
512
|
xslt_doc = etree.parse(input_dir + "/report.xsl")
|
@@ -521,6 +529,10 @@ class JavacoreSet:
|
|
521
529
|
os.mkdir(output_dir)
|
522
530
|
shutil.copy2(report_xml_file, data_input_dir)
|
523
531
|
|
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
|
+
|
524
536
|
list_files = os.listdir(data_input_dir)
|
525
537
|
progress_bar = tqdm(desc="Generating html files", unit=' files')
|
526
538
|
|
@@ -529,9 +541,6 @@ class JavacoreSet:
|
|
529
541
|
for file in list_files:
|
530
542
|
generate_html_from_xml_xsl_files_params.append((file, data_input_dir, output_dir, progress_bar))
|
531
543
|
|
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
544
|
with Pool(threads_no) as p:
|
536
545
|
p.map(JavacoreSet.generate_html_from_xml_xsl_files, generate_html_from_xml_xsl_files_params)
|
537
546
|
|
@@ -575,7 +584,8 @@ class JavacoreSet:
|
|
575
584
|
|
576
585
|
progress_bar.update(1)
|
577
586
|
|
578
|
-
|
587
|
+
@staticmethod
|
588
|
+
def create_xml_xsl_for_collection(tmp_dir, xml_xsls_prefix_path, collection, output_file_prefix):
|
579
589
|
logging.info("Creating xmls and xsls in " + tmp_dir)
|
580
590
|
os.mkdir(tmp_dir)
|
581
591
|
extensions = [".xsl", ".xml"]
|
@@ -595,7 +605,7 @@ class JavacoreSet:
|
|
595
605
|
@staticmethod
|
596
606
|
def parse_mem_arg(line):
|
597
607
|
line = line.split()[-1] # avoid matching the '2' in tag name 2CIUSERARG
|
598
|
-
tokens = re.findall("
|
608
|
+
tokens = re.findall("\d+[KkMmGg]?$", line)
|
599
609
|
if len(tokens) != 1: return UNKNOWN
|
600
610
|
return tokens[0]
|
601
611
|
|
@@ -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
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: javacore_analyser
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.1.0.dev66
|
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
|
@@ -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
|
@@ -280,9 +281,7 @@ This is recommended for geeks only:
|
|
280
281
|
#### Running cmd application:
|
281
282
|
1. Install application if not done yet
|
282
283
|
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
|
-
`pip install requirements.txt`
|
285
|
-
4. Run the following command from cmd: `javacore-analyser-batch <input-data> <generated-reports-dir>`
|
284
|
+
3. Run the following command from cmd: `javacore-analyser-batch <input-data> <generated-reports-dir>`
|
286
285
|
Where `<input-data>` is one of the following:
|
287
286
|
* The directory containing javacores and optionally verbose gc
|
288
287
|
* Archive (7z, zip, tar.gz, tar.bz2) containing the same
|
@@ -292,17 +291,28 @@ You can type the following command to obtain the help:
|
|
292
291
|
|
293
292
|
#### Running web application:
|
294
293
|
1. Repeat steps 1-3 from cmd application
|
295
|
-
2.
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
The first parameter sets where the reports need to be stored. If not set, then the `reports` dir will be created in current location.
|
301
|
-
The first parameter set the port to use by application. If not specified, 5000 will be used.
|
302
|
-
3. Execute the following command from cmd:
|
303
|
-
`javacore_analyser_web`
|
294
|
+
2. Execute the following command from cmd:
|
295
|
+
`javacore_analyser_web --port=500 --reports-dir=/data/reports_dir`
|
296
|
+
|
297
|
+
The first parameter set the port to use by application. If not specified, 5000 will be used.
|
298
|
+
The second parameter sets where the reports need to be stored. If not set, then the `reports` dir will be created in current location.
|
304
299
|
|
305
300
|
Now you can type (http://localhost:5000/).
|
301
|
+
|
302
|
+
### Running container image
|
303
|
+
There is an unofficial Docker/Podman container managed by one of projects developers. Use the following command
|
304
|
+
to start it:
|
305
|
+
|
306
|
+
`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`
|
307
|
+
|
308
|
+
or
|
309
|
+
`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`
|
310
|
+
|
311
|
+
The `mount` option specifies where you want locally to store the reports. The reports in the container are stored in
|
312
|
+
`/reports` directory. If you remove mount option, the application will work but the reports will not persist after
|
313
|
+
restart.
|
314
|
+
The application is running in the container on port 5000. By using `-p 5001:5000` option, you specify to map container
|
315
|
+
port 5000 to port 5001 on your machine. Therefore the application will be available under `http://localhost:5001/`.
|
306
316
|
|
307
317
|
<!-- The following are OPTIONAL, but strongly suggested to have in your repository. -->
|
308
318
|
<!--
|
@@ -1,23 +1,24 @@
|
|
1
1
|
javacore_analyser/__init__.py,sha256=Sw2ZeqcI2Kx1cDDv083n1SiSY_FDRCmidTuzaN1uRSw,76
|
2
|
-
javacore_analyser/abstract_snapshot_collection.py,sha256=
|
2
|
+
javacore_analyser/abstract_snapshot_collection.py,sha256=jGfd2XgujurRlKgEtlJjqNJK9sUvTdFsdgFnX9oLzt4,5589
|
3
3
|
javacore_analyser/code_snapshot_collection.py,sha256=6_C5myag5ocjOTwXVDbBamN6Lf1szgS3ylSHEEjUdVg,2655
|
4
|
-
javacore_analyser/constants.py,sha256=
|
4
|
+
javacore_analyser/constants.py,sha256=iGYAznPK8AySq6uqk-cpCU8Dbjbq6PrCh6q2mF8oeu8,1003
|
5
5
|
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=
|
6
|
+
javacore_analyser/javacore.py,sha256=_2abTyvXEaZ6Tx8r9d3NEzRCIBcf4Is8lSjpKyPu-R8,6884
|
7
|
+
javacore_analyser/javacore_analyser_batch.py,sha256=g6xVPYFDHTaXDbaruyMRUrD3g2IlJK3Ht-XUzHv3LoQ,6802
|
8
|
+
javacore_analyser/javacore_analyser_web.py,sha256=Ef91ZTI5TBaJPJhl0FtzrFHvzJNPLe07fmvbvik3OJQ,5788
|
9
|
+
javacore_analyser/javacore_set.py,sha256=YM6ZKUhbfqzjuKE5y7Jy19Hbl-BSLA1i5gIsP7NbX38,31241
|
10
10
|
javacore_analyser/logging_utils.py,sha256=vLob0ikezysjGv9XGqv9GbLekxu4eO_csq22M-gtLiQ,966
|
11
11
|
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=
|
12
|
+
javacore_analyser/snapshot_collection_collection.py,sha256=1PV1TX4QQk01dAbX-k-kTpgKr6Il867Bw6X7HHBuv-Q,1346
|
13
|
+
javacore_analyser/stack_trace.py,sha256=8sb8z4ac_L0yyxqJX1ukrTZRyngkHcA3zkXyqxG5ygA,1664
|
14
|
+
javacore_analyser/stack_trace_element.py,sha256=pZPrK1ACBUDE7YsVOFhTfewXequ1m5P-B0N-9RuhkWo,1143
|
15
15
|
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=
|
16
|
+
javacore_analyser/thread_snapshot.py,sha256=2Do8NPBXdpUezQrUI_9nyGbtU4b2s5euPnHqcuuKq7U,13255
|
17
|
+
javacore_analyser/tips.py,sha256=EhwLUAha0FvFJtO5kmvba9a1nKXGdqNHFa2jFbHZr4U,8655
|
18
|
+
javacore_analyser/verbose_gc.py,sha256=LLJ-PS2maOkP6zfW-RBGoEoHQBI99Reejb1tqpZispY,6872
|
19
|
+
javacore_analyser/data/expand.js,sha256=KwqvNUoO7yMDeQKcnLDywfMdR3Zsjan5L8QoPsQQLGo,956
|
20
20
|
javacore_analyser/data/style.css,sha256=YzHl0NSnfRik4ar6AInp7pZ_re1rirQy6L5jqdbKoKg,2246
|
21
|
+
javacore_analyser/data/html/processing_data.html,sha256=S1S2GMXQ8oQdqdYTcPzVMxUib-NLtM4ffT3OJaIIO7w,421
|
21
22
|
javacore_analyser/data/jquery/chart.js,sha256=tgiW1vJqfIKxE0F2uVvsXbgUlTyrhPMY_sm30hh_Sxc,203464
|
22
23
|
javacore_analyser/data/jquery/chartjs-adapter-date-fns.bundle.min.js,sha256=6nqzDSbDjc8fLSa7Q-c6lFN7WPGQb1XhpUbdCTIbVhU,50650
|
23
24
|
javacore_analyser/data/jquery/jq.css,sha256=CXwJ7OOkFWXsebmAipjne1TnNanpp_2z241eLsVB7Ls,6751
|
@@ -25,7 +26,7 @@ javacore_analyser/data/jquery/jquery.mark.min.js,sha256=p_nep2K57or4Km0eXahvl6J_
|
|
25
26
|
javacore_analyser/data/jquery/jquery.min.js,sha256=pvPw-upLPUjgMXY0G-8O0xUf-_Im1MZjXxxgOcBQBXU,89947
|
26
27
|
javacore_analyser/data/jquery/jquery.tablesorter.min.js,sha256=dtGH1XcAyKopMui5x20KnPxuGuSx9Rs6piJB_4Oqu6I,44365
|
27
28
|
javacore_analyser/data/jquery/jquery.tablesorter.widgets.min.js,sha256=GxbszpUzg-iYIcyDGyNVLz9Y0dQvzmQgXXVk5cHJbw0,53100
|
28
|
-
javacore_analyser/data/jquery/search.js,sha256=
|
29
|
+
javacore_analyser/data/jquery/search.js,sha256=Jwi-cBJ9YKDHJwqIlcKXqrpcM1BX-wx93uKAR44JLww,4200
|
29
30
|
javacore_analyser/data/jquery/sorting.js,sha256=HsuVLa7F70IM4ZMXZpjj7wtVI1TXL1SPbZGWenv0Jp8,369
|
30
31
|
javacore_analyser/data/jquery/theme.blue.css,sha256=mI0RCGd6G5GOKSG7BPagp0N58xipSjPXUKvrcHJ4h1Q,7528
|
31
32
|
javacore_analyser/data/jquery/theme.default.min.css,sha256=5sgExNTnkN8NcApKIU73_aqgZmqq_zJp9-9zXf9aSEw,4502
|
@@ -37,8 +38,8 @@ javacore_analyser/data/xml/javacores/javacore.xsl,sha256=5cnIp08Q9FccljHH8duoJQY
|
|
37
38
|
javacore_analyser/data/xml/threads/thread.xml,sha256=6dG89Whx1_kpEYVS_F6Upa2XuXnXorlQATFc8kD5Mfc,280
|
38
39
|
javacore_analyser/data/xml/threads/thread.xsl,sha256=rkqr5GQ2aZ_xrdhUjl2QZDCZ-09zxqUmtV8DFZVjTAA,13927
|
39
40
|
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.
|
41
|
+
javacore_analyser-2.1.0.dev66.dist-info/METADATA,sha256=Viz5zYXbffyGSshp-4U0fTiKLOFoTN4wmNP2aAziw18,21231
|
42
|
+
javacore_analyser-2.1.0.dev66.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
43
|
+
javacore_analyser-2.1.0.dev66.dist-info/entry_points.txt,sha256=W3S799zI58g5-jWMsC3wY9xksz21LPEMYOILv8sayfM,160
|
44
|
+
javacore_analyser-2.1.0.dev66.dist-info/licenses/LICENSE,sha256=xllut76FgcGL5zbIRvuRc7aezPbvlMUTWJPsVr2Sugg,11358
|
45
|
+
javacore_analyser-2.1.0.dev66.dist-info/RECORD,,
|
{javacore_analyser-2.0rc3.dist-info → javacore_analyser-2.1.0.dev66.dist-info}/entry_points.txt
RENAMED
File without changes
|
{javacore_analyser-2.0rc3.dist-info → javacore_analyser-2.1.0.dev66.dist-info}/licenses/LICENSE
RENAMED
File without changes
|