7
7
import zipfile
8
8
from concurrent .futures import ThreadPoolExecutor , as_completed
9
9
from datetime import datetime
10
- from typing import Any
11
10
11
+ import psutil
12
12
from prettytable import PrettyTable
13
13
14
14
from logicytics import Log , Execute , Check , Get , FileManagement , Flag , DEBUG , DELETE_LOGS
18
18
ACTION , SUB_ACTION = None , None
19
19
config = configparser .ConfigParser ()
20
20
config .read ("config.ini" )
21
- MAX_WORKERS = config .get ("Settings" , "max_workers" , fallback = None )
21
+ MAX_WORKERS = config .getint ("Settings" , "max_workers" , fallback = min ( 32 , ( os . cpu_count () or 1 ) + 4 ) )
22
22
log .debug (f"MAX_WORKERS: { MAX_WORKERS } " )
23
23
24
24
25
25
class ExecuteScript :
26
26
def __init__ (self ):
27
- self .execution_list = self .generate_execution_list ()
27
+ self .execution_list = self .__generate_execution_list ()
28
28
29
29
@staticmethod
30
- def generate_execution_list () -> list | list [str ] | list [str | Any ]:
30
+ def __safe_remove (file_name : str , file_list : list [str ] | set [str ]) -> list [str ]:
31
+ file_set = set (file_list )
32
+ if file_name in file_set :
33
+ file_set .remove (file_name )
34
+ else :
35
+ log .critical (f"The file { file_name } should exist in this directory - But was not found!" )
36
+ return list (file_set )
37
+
38
+ @staticmethod
39
+ def __safe_append (file_name : str , file_list : list [str ] | set [str ]) -> list [str ]:
40
+ file_set = set (file_list )
41
+ if os .path .exists (file_name ):
42
+ file_set .add (file_name )
43
+ else :
44
+ log .critical (f"Missing required file: { file_name } " )
45
+ return list (file_set )
46
+
47
+ def __generate_execution_list (self ) -> list [str ]:
31
48
"""
32
49
Generate an execution list of scripts based on the specified action.
33
50
@@ -51,11 +68,14 @@ def generate_execution_list() -> list | list[str] | list[str | Any]:
51
68
- Warns users about potential long execution times for certain actions
52
69
"""
53
70
execution_list = Get .list_of_files ("." , extensions = (".py" , ".exe" , ".ps1" , ".bat" ))
54
- execution_list .remove ("sensitive_data_miner.py" )
55
- execution_list .remove ("dir_list.py" )
56
- execution_list .remove ("tree.ps1" )
57
- execution_list .remove ("vulnscan.py" )
58
- execution_list .remove ("event_log.py" )
71
+ files_to_remove = {
72
+ "sensitive_data_miner.py" ,
73
+ "dir_list.py" ,
74
+ "tree.ps1" ,
75
+ "vulnscan.py" ,
76
+ "event_log.py" ,
77
+ }
78
+ execution_list = [file for file in execution_list if file not in files_to_remove ]
59
79
60
80
if ACTION == "minimal" :
61
81
execution_list = [
@@ -69,7 +89,7 @@ def generate_execution_list() -> list | list[str] | list[str | Any]:
69
89
"event_log.py" ,
70
90
]
71
91
72
- if ACTION == "nopy" :
92
+ elif ACTION == "nopy" :
73
93
execution_list = [
74
94
"browser_miner.ps1" ,
75
95
"netadapter.ps1" ,
@@ -78,24 +98,36 @@ def generate_execution_list() -> list | list[str] | list[str | Any]:
78
98
"tree.ps1"
79
99
]
80
100
81
- if ACTION == "modded" :
101
+ elif ACTION == "modded" :
82
102
# Add all files in MODS to execution list
83
103
execution_list = Get .list_of_files ("../MODS" ,
84
104
extensions = (".py" , ".exe" , ".ps1" , ".bat" ),
85
105
append_file_list = execution_list )
86
106
87
- if ACTION == "depth" :
107
+ elif ACTION == "depth" :
88
108
log .warning (
89
109
"This flag will use clunky and huge scripts, and so may take a long time, but reap great rewards." )
90
- execution_list .append ("sensitive_data_miner.py" )
91
- execution_list .append ("dir_list.py" )
92
- execution_list .append ("tree.ps1" )
93
- execution_list .append ("event_log.py" )
110
+ files_to_append = {
111
+ "sensitive_data_miner.py" ,
112
+ "dir_list.py" ,
113
+ "tree.ps1" ,
114
+ "event_log.py" ,
115
+ }
116
+ for file in files_to_append :
117
+ execution_list = self .__safe_append (file , execution_list )
94
118
log .warning ("This flag will use threading!" )
95
119
96
- if ACTION == "vulnscan_ai" :
120
+ elif ACTION == "vulnscan_ai" :
97
121
# Only vulnscan detector
98
- execution_list = ["vulnscan.py" ]
122
+ if os .path .exists ("vulnscan.py" ):
123
+ execution_list = ["vulnscan.py" ]
124
+ else :
125
+ log .critical ("Vulnscan is missing..." )
126
+ exit (1 )
127
+
128
+ if len (execution_list ) == 0 :
129
+ log .critical ("Nothing is in the execution list.. This is due to faulty code or corrupted Logicytics files!" )
130
+ exit (1 )
99
131
100
132
log .debug (f"The following will be executed: { execution_list } " )
101
133
return execution_list
@@ -159,27 +191,43 @@ def __default(self):
159
191
160
192
def __performance (self ):
161
193
"""Checks performance of each script."""
194
+ if DEBUG .lower () != "debug" :
195
+ log .warning ("Advised to turn on DEBUG logging!!" )
196
+
162
197
execution_times = []
198
+ memory_usage = []
199
+ process = psutil .Process ()
163
200
164
201
for file in range (len (self .execution_list )):
165
202
start_time = datetime .now ()
203
+ start_memory = process .memory_info ().rss / 1024 / 1024 # MB
166
204
log .execution (Execute .script (self .execution_list [file ]))
167
205
end_time = datetime .now ()
206
+ end_memory = process .memory_info ().rss / 1024 / 1024 # MB
168
207
elapsed_time = end_time - start_time
208
+ memory_delta = end_memory - start_memory
209
+ memory_usage .append ((self .execution_list [file ], str (memory_delta )))
169
210
execution_times .append ((self .execution_list [file ], elapsed_time ))
170
211
log .info (f"{ self .execution_list [file ]} executed in { elapsed_time } " )
212
+ log .info (f"{ self .execution_list [file ]} used { memory_delta :.2f} MB of memory" )
213
+ log .debug (f"Started with { start_memory } MB of memory and ended with { end_memory } MB of memory" )
171
214
172
215
table = PrettyTable ()
173
- table .field_names = ["Script" , "Execution Time" ]
216
+ table .field_names = ["Script" , "Execution Time" , "Memory Usage (MB)" ]
174
217
for script , elapsed_time in execution_times :
175
- table .add_row ([script , elapsed_time ])
218
+ memory = next (m [1 ] for m in memory_usage if m [0 ] == script )
219
+ table .add_row ([script , elapsed_time , f"{ memory :.2f} " ])
176
220
177
221
try :
178
222
with open (
179
223
f"../ACCESS/LOGS/PERFORMANCE/Performance_Summary_"
180
224
f"{ datetime .now ().strftime ('%Y-%m-%d_%H-%M-%S' )} .txt" , "w"
181
225
) as f :
182
226
f .write (table .get_string ())
227
+ f .write (
228
+ "\n Some values may be negative, Reason may be due to external resources playing with memory usage, "
229
+ "close background tasks to get more accurate readings" )
230
+ f .write ("Note: This is not a low-level memory logger, data here isn't 100% accurate!" )
183
231
log .info ("Performance check complete! Performance log found in ACCESS/LOGS/PERFORMANCE" )
184
232
except Exception as e :
185
233
log .error (f"Error writing performance log: { e } " )
@@ -454,7 +502,12 @@ def Logicytics():
454
502
455
503
456
504
if __name__ == "__main__" :
457
- Logicytics ()
505
+ try :
506
+ Logicytics ()
507
+ except KeyboardInterrupt :
508
+ log .warning ("Shutting down Logicytics utility with force causes many files to remain where they shouldn't" )
509
+ log .warning ("Please don't force shut Logicytics again - As we don't have a cleanup function yet." )
510
+ exit (0 )
458
511
else :
459
512
log .error ("This script cannot be imported!" )
460
513
exit (1 )
0 commit comments