javacore-analyser 2.0rc3__py3-none-any.whl → 2.1.0.dev66__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/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
|