javacore-analyser 2.0rc3__py3-none-any.whl → 2.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.thread_snapshotssnaps)):
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
 
@@ -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 # Minimal Javacore size in bytes
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
@@ -15,6 +15,8 @@ $('.show').click(function () {
15
15
  }
16
16
  });
17
17
 
18
+
19
+
18
20
  function expand_it(whichEl, link) {
19
21
  whichEl.style.display = (whichEl.style.display == "none") ? "" : "none";
20
22
  //if (link) {
@@ -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.classList.remove('hide');
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 > timestamp;
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
- <div id="memusagedoc" style="display:none;">
359
- This chart shows all the garbage collections that happened between the time
360
- of the first and the last javacore in the data set.
361
- Garbage collections that happened before the first
362
- or after the last javacore generation time are not included.
363
- <ul>
364
- <li><strong>Heap Usage</strong>
365
- is the available Java heap memory over time,
366
- based on the garbage collection data from the verbose GC log files.
367
- </li>
368
- <li><strong>Total Heap</strong>
369
- is the maximum size of the Java heap, configured by using the Xmx Java argument,
370
- expressed in megabytes.
371
- </li>
372
- </ul>
373
- </div>
374
- <div id="systemresources_myChartGC" class="chart-container hide">
375
- <canvas id="myChartGC" height="200"></canvas>
376
- </div>
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>
@@ -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
- if bts[i] < 32 and bts[i] != 9 and bts[i] != 10 and bts[i] != 13 and bts[i] != 1: # fix for 'XML Syntax error PCDATA invalid char#405'
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 = self.processThreadName(line, file)
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
- def processThreadName(self, line, file):
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 - 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("input_param", help="Input file(s) or directory")
73
- parser.add_argument("output_param", help="Report output directory")
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.input_param
80
- output_param = args.output_param
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
- traceback.print_exc(file=sys.stdout)
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
- export REPORTS_DIR=/tmp/reports
26
+
25
27
  flask --app javacore_analyser_web run
26
28
  """
27
29
  app = Flask(__name__)
28
- with app.app_context():
29
- create_console_logging()
30
- logging.info("Javacore analyser")
31
- logging.info("Python version: " + sys.version)
32
- logging.info("Preferred encoding: " + locale.getpreferredencoding())
33
- reports_dir = os.getenv("REPORTS_DIR", DEFAULT_REPORTS_DIR)
34
- logging.info("Reports directory: " + reports_dir)
35
- create_file_logging(reports_dir)
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
- # Create a temporary directory to store uploaded files
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 + '/' + report_name
105
- javacore_analyser.javacore_analyser_batch.process_javacores_and_generate_report_data(input_files,
106
- report_output_dir)
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
- javacores_temp_dir.cleanup()
123
+ shutil.rmtree(javacores_temp_dir_name, ignore_errors=True)
124
+
111
125
 
112
126
  def main():
113
- debug = os.getenv("DEBUG", False)
114
- port = os.getenv("PORT", DEFAULT_PORT)
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
- def __create_output_files_structure(self, output_dir):
136
- if not os.path.isdir(output_dir):
137
- os.mkdir(output_dir)
138
- data_output_dir = os.path.normpath(os.path.join(output_dir, 'data'))
139
- if not data_output_dir.startswith(output_dir):
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
- style_css_resource: Traversable = importlib_resources.files("javacore_analyser") / "data" / "style.css"
146
- data_dir = os.path.dirname(style_css_resource)
147
- os.mkdir(data_output_dir)
148
- shutil.copytree(data_dir, data_output_dir, dirs_exist_ok=True)
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 e:
259
- print(f'Error during processing file: {file.name} \n'
260
- f'line number: {i} \n'
261
- f'line: {curr_line}\n'
262
- f'Check the exception below what happened')
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(importlib_resources.files("javacore_analyser") / "data" / "xml" / "report.xsl")
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
- def create_xml_xsl_for_collection(self, tmp_dir, xml_xsls_prefix_path, collection, output_file_prefix):
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("[\d]+[KkMmGg]?$", line)
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
- return self.snapshot_collections.__next__()
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
@@ -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
- return self.stack_trace_elements.__next__()
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)
@@ -9,7 +9,7 @@ from javacore_analyser.stack_trace_kind import StackTraceKind
9
9
 
10
10
  class StackTraceElement:
11
11
 
12
- def __init__(self, line = None):
12
+ def __init__(self, line=None):
13
13
  if line is None:
14
14
  self.line = None
15
15
  self.kind = StackTraceKind.JAVA
@@ -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
 
@@ -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.3
1
+ Metadata-Version: 2.4
2
2
  Name: javacore_analyser
3
- Version: 2.0rc3
3
+ Version: 2.1
4
4
  Summary: The tool to review IBM Javacore files
5
5
  Project-URL: Homepage, https://github.com/IBM/javacore-analyser
6
6
  Project-URL: Issues, https://github.com/IBM/javacore-analyser/issues
7
- Project-URL: Changelog, https://github.com/IBM/javacore-analyser/CHANGELOG.md
7
+ Project-URL: Changelog, https://github.com/IBM/javacore-analyser/blob/main/CHANGELOG.md
8
8
  Project-URL: Source, https://github.com/IBM/javacore-analyser
9
9
  Author-email: Krzysztof Kazmierczyk <kazm@ibm.com>, Piotr Aniola <Piotr.Aniola@ibm.com>, Tadeusz Janasiewicz <t.janasiewicz@ibm.com>
10
10
  License: Apache License
@@ -209,6 +209,7 @@ License: Apache License
209
209
  See the License for the specific language governing permissions and
210
210
  limitations under the License.
211
211
 
212
+ License-File: LICENSE
212
213
  Classifier: Development Status :: 5 - Production/Stable
213
214
  Classifier: Environment :: Console
214
215
  Classifier: Environment :: Web Environment
@@ -238,8 +239,18 @@ Description-Content-Type: text/markdown
238
239
  <!-- This should be the location of the title of the repository, normally the short name -->
239
240
  # Javacore Analyser
240
241
 
242
+ ![GitHub License](https://img.shields.io/github/license/IBM/javacore-analyser)
243
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/javacore-analyser)
244
+ ![GitHub contributors](https://img.shields.io/github/contributors/IBM/javacore-analyser)
241
245
  <!-- Build Status, is a great thing to have at the top of your repository, it shows that you take your CI/CD as first class citizens -->
242
246
  [![Build Status](https://app.travis-ci.com/IBM/javacore-analyser.svg?token=w3i4X11XppEi2tJQsxDb&branch=main)](https://app.travis-ci.com/IBM/javacore-analyser)
247
+ ![GitHub last commit](https://img.shields.io/github/last-commit/IBM/javacore-analyser)
248
+ ![GitHub Release Date](https://img.shields.io/github/release-date/IBM/javacore-analyser)
249
+ ![GitHub commit activity](https://img.shields.io/github/commit-activity/t/IBM/javacore-analyser)
250
+ ![GitHub Issues or Pull Requests](https://img.shields.io/github/issues-pr/IBM/javacore-analyser)
251
+ ![GitHub Issues or Pull Requests](https://img.shields.io/github/issues-pr-closed/IBM/javacore-analyser)
252
+ ![PyPI - Downloads](https://img.shields.io/pypi/dm/javacore-analyser)
253
+
243
254
 
244
255
  <!-- Not always needed, but a scope helps the user understand in a short sentance like below, why this repo exists -->
245
256
  ## Scope
@@ -266,7 +277,7 @@ Steps:
266
277
  3. Run the following command:
267
278
  `pip install javacore-analyser`
268
279
  OR
269
- `pip install --extra-index-url https://test.pypi.org/simple/ javacore-analyser` - if you want an experimental version
280
+ `pip install --pre --extra-index-url https://test.pypi.org/simple/ javacore-analyser` - if you want an experimental version
270
281
 
271
282
  #### Installing from sources
272
283
  This is recommended for geeks only:
@@ -280,29 +291,44 @@ This is recommended for geeks only:
280
291
  #### Running cmd application:
281
292
  1. Install application if not done yet
282
293
  2. Activate your created virtual environment according to activate Virtual Environment according to [Creating virtual environments](https://docs.python.org/3/tutorial/venv.html#creating-virtual-environments)
283
- 3. Install the requirements using pip:
284
- `pip install requirements.txt`
285
- 4. Run the following command from cmd: `javacore-analyser-batch <input-data> <generated-reports-dir>`
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. OPTIONAL: set the following variables:
296
- ```
297
- export REPORTS_DIR=/tmp/reports
298
- export PORT=5000
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`
304
-
305
- Now you can type (http://localhost:5000/).
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/abstract_snapshot_collection.py,sha256=zXLRPQRdFpOufC8OVMh1wAhFnFDYQgWy10zZOZWaZQc,5592
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=hGEsldGuuJGqy1iM7POiQnjVqNLclO5CjIKeIW2oeIQ,924
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=NkvxN6C93eG24wEB_kfH-yf0_f_ECj7Let4bQ1i22AM,6860
7
- javacore_analyser/javacore_analyser_batch.py,sha256=ApC_xcDMIP4DxQ0MUiD6XrF3hzcyFmofVH1Jq4-6_4s,5408
8
- javacore_analyser/javacore_analyser_web.py,sha256=DJgzHKuGUkATKaE7pI6zOQPGITB6hdM86tUXsZ1fCWw,4526
9
- javacore_analyser/javacore_set.py,sha256=3_KfoT4wKYmUROUQXJInaPxKi60zauqAK11Iat3Y55Q,30689
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=JyNr038nC8mcX1mjeXjNSzZT4oE9ACFmCYJORUKzyvw,1265
13
- javacore_analyser/stack_trace.py,sha256=RY1nmvSA_lyRqVejHcXBbxT8EhbkJPktBqsQ4kKSgtA,1661
14
- javacore_analyser/stack_trace_element.py,sha256=1WOV7pilb8RAc_hMN7lsXMrPevW6WFHAaLOGpsMmJgs,1145
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=TR0StpE-r4FYax8oULUANBsKJDYX3FQ0lwzwnvEPxic,13193
17
- javacore_analyser/tips.py,sha256=pA8mpmyPk9RXPVt5GFiB9dlhiBeiGPKUBgJgvPRUmLk,8608
18
- javacore_analyser/verbose_gc.py,sha256=mSFIRb-thl5nrdEd_SOxFcvobso_07NQ2_g1_9IXgY0,6782
19
- javacore_analyser/data/expand.js,sha256=mNjvT_00YHQxWhlAQwFElzlhBWvKP5WEMSWJkL1PKl8,954
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=hY0kp5ZIUazzM32hEmArA3ql6MTiOjjf5wosxXvowAw,3398
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=CMbSqte7Ln0hP9ZScESGdIDfWEmxJwvfLZjDfYNc1Sg,8427
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=XEwgf0qfXL1fXjj-X5iYYwt-D0cPjI9mLjhTmRvyNnk,49697
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.0rc3.dist-info/METADATA,sha256=BuAHmwoorqey194rBsfXSbM8kTaBWfZE0OyjgADWlS0,20358
41
- javacore_analyser-2.0rc3.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
42
- javacore_analyser-2.0rc3.dist-info/entry_points.txt,sha256=W3S799zI58g5-jWMsC3wY9xksz21LPEMYOILv8sayfM,160
43
- javacore_analyser-2.0rc3.dist-info/licenses/LICENSE,sha256=xllut76FgcGL5zbIRvuRc7aezPbvlMUTWJPsVr2Sugg,11358
44
- javacore_analyser-2.0rc3.dist-info/RECORD,,
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.26.3
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any