javacore-analyser 2.0rc1__py3-none-any.whl → 2.1.0.dev66__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- javacore_analyser/abstract_snapshot_collection.py +2 -2
- javacore_analyser/constants.py +2 -1
- javacore_analyser/data/expand.js +2 -0
- javacore_analyser/data/html/processing_data.html +17 -0
- javacore_analyser/data/jquery/search.js +22 -0
- javacore_analyser/data/jquery/wait2scripts.js +27 -5
- javacore_analyser/data/xml/javacores/javacore.xsl +126 -117
- javacore_analyser/data/xml/report.xsl +1 -1
- javacore_analyser/data/xml/threads/thread.xsl +162 -153
- 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 +78 -43
- 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/templates/index.html +9 -4
- javacore_analyser/thread_snapshot.py +3 -2
- javacore_analyser/tips.py +5 -3
- javacore_analyser/verbose_gc.py +4 -1
- {javacore_analyser-2.0rc1.dist-info → javacore_analyser-2.1.0.dev66.dist-info}/METADATA +25 -15
- javacore_analyser-2.1.0.dev66.dist-info/RECORD +45 -0
- {javacore_analyser-2.0rc1.dist-info → javacore_analyser-2.1.0.dev66.dist-info}/WHEEL +1 -1
- javacore_analyser-2.0rc1.dist-info/RECORD +0 -44
- {javacore_analyser-2.0rc1.dist-info → javacore_analyser-2.1.0.dev66.dist-info}/entry_points.txt +0 -0
- {javacore_analyser-2.0rc1.dist-info → javacore_analyser-2.1.0.dev66.dist-info}/licenses/LICENSE +0 -0
@@ -20,177 +20,186 @@
|
|
20
20
|
<script type="text/javascript" src="../data/jquery/chart.js"> _ </script>
|
21
21
|
<script type="text/javascript" src="../data/jquery/chartjs-adapter-date-fns.bundle.min.js"> _ </script>
|
22
22
|
<script type="text/javascript" src="../data/jquery/wait2scripts.js"> _ </script>
|
23
|
-
|
23
|
+
<script src="../data/jquery/jquery.mark.min.js"> _ </script>
|
24
|
+
<script type="text/javascript" src="../data/jquery/search.js"> _ </script>
|
24
25
|
</head>
|
25
|
-
|
26
26
|
<body id="doc_body" height="100%">
|
27
|
-
<
|
28
|
-
|
29
|
-
|
30
|
-
<
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
<
|
42
|
-
<
|
43
|
-
<
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
<
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
</xsl:choose>
|
73
|
-
<xsl:choose>
|
74
|
-
<xsl:when test="position()=1">
|
75
|
-
<td>N/A</td>
|
76
|
-
</xsl:when>
|
77
|
-
<xsl:otherwise>
|
78
|
-
<td><xsl:value-of select='format-number(cpu_usage, "0.##")'/></td>
|
79
|
-
</xsl:otherwise>
|
80
|
-
</xsl:choose>
|
81
|
-
<xsl:choose>
|
27
|
+
<div class="searchbar">
|
28
|
+
<input id="search-input" type="search" />
|
29
|
+
<button data-search="search" id="search-button">Search</button>
|
30
|
+
<button data-search="next">Next</button>
|
31
|
+
<button data-search="prev">Prev</button>
|
32
|
+
<button data-search="clear">✖</button>
|
33
|
+
</div>
|
34
|
+
<div class="content">
|
35
|
+
<p class="right"><a href="../index.html"> Back to Main page </a></p>
|
36
|
+
<h2>Wait Report for thread: <b><xsl:value-of select="thread_name"/></b></h2>
|
37
|
+
<xsl:choose>
|
38
|
+
<xsl:when test="//javacore_count = 1">
|
39
|
+
System resource utilization data cannot be calculated with only a single javacore.
|
40
|
+
</xsl:when>
|
41
|
+
<xsl:otherwise>
|
42
|
+
<div class="chart-container" height="25%">
|
43
|
+
<canvas id="myChart" height="100%"></canvas>
|
44
|
+
</div>
|
45
|
+
</xsl:otherwise>
|
46
|
+
</xsl:choose>
|
47
|
+
<div id="all_threads">
|
48
|
+
<table id="all_threads_table_thread_xsl">
|
49
|
+
<thead>
|
50
|
+
<tr>
|
51
|
+
<th>Timestamp</th>
|
52
|
+
<th>Elapsed time (s)</th>
|
53
|
+
<th>CPU usage (s)</th>
|
54
|
+
<th>% CPU usage</th>
|
55
|
+
<th class='sixty'>Stack trace</th>
|
56
|
+
<th>State</th>
|
57
|
+
<th>Blocking</th>
|
58
|
+
</tr>
|
59
|
+
</thead>
|
60
|
+
<!-- Snapshot starts here -->
|
61
|
+
<xsl:for-each select="*[starts-with(name(), 'stack')]">
|
62
|
+
<tr>
|
63
|
+
<td>
|
64
|
+
<a>
|
65
|
+
<xsl:attribute name="href">
|
66
|
+
../javacores/<xsl:value-of select="file_name"/>.html
|
67
|
+
</xsl:attribute>
|
68
|
+
<xsl:value-of select='timestamp'/>
|
69
|
+
</a>
|
70
|
+
</td>
|
71
|
+
<xsl:choose>
|
82
72
|
<xsl:when test="position()=1">
|
83
73
|
<td>N/A</td>
|
84
74
|
</xsl:when>
|
85
75
|
<xsl:otherwise>
|
86
|
-
<td
|
76
|
+
<td>
|
77
|
+
<xsl:value-of select='format-number(elapsed_time, "0.##")'/>
|
78
|
+
</td>
|
87
79
|
</xsl:otherwise>
|
88
80
|
</xsl:choose>
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
<xsl:value-of select="current()"/>
|
101
|
-
</span>
|
102
|
-
<br/>
|
103
|
-
</xsl:for-each>
|
104
|
-
</p>
|
81
|
+
<xsl:choose>
|
82
|
+
<xsl:when test="position()=1">
|
83
|
+
<td>N/A</td>
|
84
|
+
</xsl:when>
|
85
|
+
<xsl:otherwise>
|
86
|
+
<td><xsl:value-of select='format-number(cpu_usage, "0.##")'/></td>
|
87
|
+
</xsl:otherwise>
|
88
|
+
</xsl:choose>
|
89
|
+
<xsl:choose>
|
90
|
+
<xsl:when test="position()=1">
|
91
|
+
<td>N/A</td>
|
105
92
|
</xsl:when>
|
106
93
|
<xsl:otherwise>
|
107
|
-
|
94
|
+
<td><xsl:value-of select='format-number(cpu_percentage, "0.#")'/></td>
|
108
95
|
</xsl:otherwise>
|
109
96
|
</xsl:choose>
|
110
|
-
|
111
|
-
|
112
|
-
<xsl:choose>
|
113
|
-
<xsl:when test="state='CW'">
|
114
|
-
<td class="waiting">
|
115
|
-
<xsl:choose>
|
116
|
-
<xsl:when test="blocked_by=''">
|
117
|
-
Waiting on condition
|
118
|
-
</xsl:when>
|
119
|
-
<xsl:otherwise>
|
120
|
-
<a target="_blank">
|
121
|
-
<xsl:attribute name="href">
|
122
|
-
<xsl:value-of select="concat('thread_', blocked_by/@thread_hash, '.html')"/>
|
123
|
-
</xsl:attribute>
|
124
|
-
<xsl:attribute name="title">
|
125
|
-
<xsl:value-of select="blocked_by/@name" />
|
126
|
-
</xsl:attribute>
|
127
|
-
Waiting for <xsl:value-of select="blocked_by/@thread_id"/>
|
128
|
-
</a>
|
129
|
-
</xsl:otherwise>
|
130
|
-
</xsl:choose>
|
131
|
-
</td>
|
132
|
-
</xsl:when>
|
133
|
-
<xsl:when test="state='R'">
|
134
|
-
<td class="runnable">Runnable</td>
|
135
|
-
</xsl:when>
|
136
|
-
<xsl:when test="state='P'">
|
137
|
-
<td class="parked">
|
97
|
+
<td class="left">
|
98
|
+
<div>
|
138
99
|
<xsl:choose>
|
139
|
-
<xsl:when test="
|
140
|
-
|
100
|
+
<xsl:when test="stack_depth > 0">
|
101
|
+
<div class="toggle_expand">
|
102
|
+
<a href="javaScript:;" class="show">[+] Expand</a>
|
103
|
+
</div>
|
104
|
+
<p class="stacktrace">
|
105
|
+
<xsl:for-each select="*[starts-with(name(), 'line')]">
|
106
|
+
<span>
|
107
|
+
<xsl:attribute name="class"><xsl:value-of select="@kind"/></xsl:attribute>
|
108
|
+
<xsl:value-of select="current()"/>
|
109
|
+
</span>
|
110
|
+
<br/>
|
111
|
+
</xsl:for-each>
|
112
|
+
</p>
|
141
113
|
</xsl:when>
|
142
114
|
<xsl:otherwise>
|
143
|
-
|
144
|
-
<xsl:attribute name="href">
|
145
|
-
<xsl:value-of select="concat('thread_', blocked_by/@thread_hash, '.html')"/>
|
146
|
-
</xsl:attribute>
|
147
|
-
<xsl:attribute name="title">
|
148
|
-
<xsl:value-of select="blocked_by/@name" />
|
149
|
-
</xsl:attribute>
|
150
|
-
Parked on <xsl:value-of select="blocked_by/@thread_id"/>
|
151
|
-
</a>
|
115
|
+
No Stack
|
152
116
|
</xsl:otherwise>
|
153
117
|
</xsl:choose>
|
154
|
-
</
|
155
|
-
</
|
156
|
-
<xsl:when test="state='B'">
|
157
|
-
<td class="blocked">
|
158
|
-
<a target="_blank">
|
159
|
-
<xsl:attribute name="href">
|
160
|
-
<xsl:value-of select="concat('thread_', blocked_by/@thread_hash, '.html')"/>
|
161
|
-
</xsl:attribute>
|
162
|
-
<xsl:attribute name="title">
|
163
|
-
<xsl:value-of select="blocked_by/@name" />
|
164
|
-
</xsl:attribute>
|
165
|
-
Blocked by <xsl:value-of select="blocked_by/@thread_id"/>
|
166
|
-
</a>
|
167
|
-
</td>
|
168
|
-
</xsl:when>
|
169
|
-
<xsl:otherwise>
|
170
|
-
<td><xsl:value-of select="state"/></td>
|
171
|
-
</xsl:otherwise>
|
172
|
-
</xsl:choose>
|
173
|
-
<td>
|
118
|
+
</div>
|
119
|
+
</td>
|
174
120
|
<xsl:choose>
|
175
|
-
|
176
|
-
|
177
|
-
<xsl:
|
178
|
-
<
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
<
|
183
|
-
<xsl:
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
121
|
+
<xsl:when test="state='CW'">
|
122
|
+
<td class="waiting">
|
123
|
+
<xsl:choose>
|
124
|
+
<xsl:when test="blocked_by=''">
|
125
|
+
Waiting on condition
|
126
|
+
</xsl:when>
|
127
|
+
<xsl:otherwise>
|
128
|
+
<a target="_blank">
|
129
|
+
<xsl:attribute name="href">
|
130
|
+
<xsl:value-of select="concat('thread_', blocked_by/@thread_hash, '.html')"/>
|
131
|
+
</xsl:attribute>
|
132
|
+
<xsl:attribute name="title">
|
133
|
+
<xsl:value-of select="blocked_by/@name" />
|
134
|
+
</xsl:attribute>
|
135
|
+
Waiting for <xsl:value-of select="blocked_by/@thread_id"/>
|
136
|
+
</a>
|
137
|
+
</xsl:otherwise>
|
138
|
+
</xsl:choose>
|
139
|
+
</td>
|
140
|
+
</xsl:when>
|
141
|
+
<xsl:when test="state='R'">
|
142
|
+
<td class="runnable">Runnable</td>
|
143
|
+
</xsl:when>
|
144
|
+
<xsl:when test="state='P'">
|
145
|
+
<td class="parked">
|
146
|
+
<xsl:choose>
|
147
|
+
<xsl:when test="blocked_by=''">
|
148
|
+
Parked
|
149
|
+
</xsl:when>
|
150
|
+
<xsl:otherwise>
|
151
|
+
<a target="_blank">
|
152
|
+
<xsl:attribute name="href">
|
153
|
+
<xsl:value-of select="concat('thread_', blocked_by/@thread_hash, '.html')"/>
|
154
|
+
</xsl:attribute>
|
155
|
+
<xsl:attribute name="title">
|
156
|
+
<xsl:value-of select="blocked_by/@name" />
|
157
|
+
</xsl:attribute>
|
158
|
+
Parked on <xsl:value-of select="blocked_by/@thread_id"/>
|
159
|
+
</a>
|
160
|
+
</xsl:otherwise>
|
161
|
+
</xsl:choose>
|
162
|
+
</td>
|
163
|
+
</xsl:when>
|
164
|
+
<xsl:when test="state='B'">
|
165
|
+
<td class="blocked">
|
166
|
+
<a target="_blank">
|
167
|
+
<xsl:attribute name="href">
|
168
|
+
<xsl:value-of select="concat('thread_', blocked_by/@thread_hash, '.html')"/>
|
169
|
+
</xsl:attribute>
|
170
|
+
<xsl:attribute name="title">
|
171
|
+
<xsl:value-of select="blocked_by/@name" />
|
172
|
+
</xsl:attribute>
|
173
|
+
Blocked by <xsl:value-of select="blocked_by/@thread_id"/>
|
174
|
+
</a>
|
175
|
+
</td>
|
176
|
+
</xsl:when>
|
177
|
+
<xsl:otherwise>
|
178
|
+
<td><xsl:value-of select="state"/></td>
|
179
|
+
</xsl:otherwise>
|
180
|
+
</xsl:choose>
|
181
|
+
<td>
|
182
|
+
<xsl:choose>
|
183
|
+
<xsl:when test="blocking/thread">
|
184
|
+
blocking:
|
185
|
+
<xsl:for-each select="blocking/thread">
|
186
|
+
<a target="_blank">
|
187
|
+
<xsl:attribute name="href">
|
188
|
+
<xsl:value-of select="concat('thread_', @thread_hash, '.html')"/>
|
189
|
+
</xsl:attribute>
|
190
|
+
<xsl:attribute name="title">
|
191
|
+
<xsl:value-of select="@name" />
|
192
|
+
</xsl:attribute>
|
193
|
+
<xsl:value-of select="@thread_id" />
|
194
|
+
</a>;
|
195
|
+
</xsl:for-each>
|
196
|
+
</xsl:when>
|
197
|
+
</xsl:choose>
|
198
|
+
</td>
|
199
|
+
</tr>
|
200
|
+
</xsl:for-each>
|
201
|
+
</table>
|
202
|
+
</div>
|
194
203
|
</div>
|
195
204
|
</body>
|
196
205
|
<script>loadChart();</script>
|
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:
|