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.
@@ -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
  }
@@ -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:
@@ -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("input_param", help="Input file(s) or directory")
73
- parser.add_argument("output_param", help="Report output directory")
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.input_param
80
- output_param = args.output_param
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
- 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)
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
- 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,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 + '/' + 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
+ 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
- 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
@@ -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(importlib_resources.files("javacore_analyser") / "data" / "xml" / "report.xsl")
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
- def create_xml_xsl_for_collection(self, tmp_dir, xml_xsls_prefix_path, collection, output_file_prefix):
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("[\d]+[KkMmGg]?$", line)
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
- 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
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: javacore_analyser
3
- Version: 2.0rc3
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. 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>`
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. 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`
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=zXLRPQRdFpOufC8OVMh1wAhFnFDYQgWy10zZOZWaZQc,5592
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=hGEsldGuuJGqy1iM7POiQnjVqNLclO5CjIKeIW2oeIQ,924
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=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
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=JyNr038nC8mcX1mjeXjNSzZT4oE9ACFmCYJORUKzyvw,1265
13
- javacore_analyser/stack_trace.py,sha256=RY1nmvSA_lyRqVejHcXBbxT8EhbkJPktBqsQ4kKSgtA,1661
14
- javacore_analyser/stack_trace_element.py,sha256=1WOV7pilb8RAc_hMN7lsXMrPevW6WFHAaLOGpsMmJgs,1145
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=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
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=hY0kp5ZIUazzM32hEmArA3ql6MTiOjjf5wosxXvowAw,3398
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.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,,
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,,
@@ -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