memory tooling, state handling changes
This commit is contained in:
167
scripts/analyze-memory-baseline.lua
Normal file
167
scripts/analyze-memory-baseline.lua
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env lua
|
||||
-- Memory Baseline Analysis
|
||||
-- Analyzes base memory usage and per-element costs
|
||||
|
||||
-- Add libs directory to package path
|
||||
package.path = package.path .. ";./?.lua;./?/init.lua"
|
||||
|
||||
-- Mock LÖVE
|
||||
_G.love = {
|
||||
graphics = {
|
||||
newCanvas = function() return {} end,
|
||||
newImage = function() return {} end,
|
||||
setCanvas = function() end,
|
||||
clear = function() end,
|
||||
setColor = function() end,
|
||||
draw = function() end,
|
||||
rectangle = function() end,
|
||||
print = function() end,
|
||||
getDimensions = function() return 800, 600 end,
|
||||
getColor = function() return 1, 1, 1, 1 end,
|
||||
setBlendMode = function() end,
|
||||
setScissor = function() end,
|
||||
getScissor = function() return nil end,
|
||||
push = function() end,
|
||||
pop = function() end,
|
||||
translate = function() end,
|
||||
rotate = function() end,
|
||||
scale = function() end,
|
||||
newFont = function() return {} end,
|
||||
setFont = function() end,
|
||||
getFont = function() return { getHeight = function() return 12 end } end,
|
||||
},
|
||||
window = { getMode = function() return 800, 600 end },
|
||||
timer = { getTime = function() return os.clock() end },
|
||||
image = { newImageData = function() return {} end },
|
||||
mouse = { getPosition = function() return 0, 0 end },
|
||||
}
|
||||
|
||||
local FlexLove = require("FlexLove")
|
||||
local MemoryScanner = require("modules.MemoryScanner")
|
||||
local StateManager = require("modules.StateManager")
|
||||
local Context = require("modules.Context")
|
||||
local ImageCache = require("modules.ImageCache")
|
||||
local ErrorHandler = require("modules.ErrorHandler")
|
||||
|
||||
print("=== Memory Baseline Analysis ===")
|
||||
print("")
|
||||
|
||||
-- Baseline: Just FlexLove loaded
|
||||
collectgarbage("collect")
|
||||
collectgarbage("collect")
|
||||
local baseline = collectgarbage("count") / 1024
|
||||
print(string.format("1. FlexLove loaded (no init): %.2f MB", baseline))
|
||||
|
||||
-- Initialize FlexLove
|
||||
FlexLove.init({ immediateMode = true })
|
||||
collectgarbage("collect")
|
||||
collectgarbage("collect")
|
||||
local afterInit = collectgarbage("count") / 1024
|
||||
print(string.format("2. After init(): %.2f MB (+%.2f MB)", afterInit, afterInit - baseline))
|
||||
|
||||
-- Create 1 simple element
|
||||
FlexLove.beginFrame()
|
||||
FlexLove.new({ id = "test1", width = 100, height = 100 })
|
||||
FlexLove.endFrame()
|
||||
collectgarbage("collect")
|
||||
collectgarbage("collect")
|
||||
local after1Element = collectgarbage("count") / 1024
|
||||
print(string.format("3. After 1 element: %.2f MB (+%.2f KB)", after1Element, (after1Element - afterInit) * 1024))
|
||||
|
||||
-- Create 10 more elements (total 11)
|
||||
FlexLove.beginFrame()
|
||||
for i = 1, 10 do
|
||||
FlexLove.new({ id = "elem" .. i, width = 100, height = 100 })
|
||||
end
|
||||
FlexLove.endFrame()
|
||||
collectgarbage("collect")
|
||||
collectgarbage("collect")
|
||||
local after10Elements = collectgarbage("count") / 1024
|
||||
print(string.format("4. After 10 more elements: %.2f MB (+%.2f KB)", after10Elements, (after10Elements - after1Element) * 1024))
|
||||
print(string.format(" Per element: ~%.2f KB", (after10Elements - after1Element) * 1024 / 10))
|
||||
|
||||
-- Create 100 more elements
|
||||
FlexLove.beginFrame()
|
||||
for i = 1, 100 do
|
||||
FlexLove.new({ id = "bulk" .. i, width = 100, height = 100 })
|
||||
end
|
||||
FlexLove.endFrame()
|
||||
collectgarbage("collect")
|
||||
collectgarbage("collect")
|
||||
local after100Elements = collectgarbage("count") / 1024
|
||||
print(string.format("5. After 100 more elements: %.2f MB (+%.2f KB)", after100Elements, (after100Elements - after10Elements) * 1024))
|
||||
print(string.format(" Per element: ~%.2f KB", (after100Elements - after10Elements) * 1024 / 100))
|
||||
|
||||
print("")
|
||||
print("=== Memory Breakdown ===")
|
||||
|
||||
-- Initialize scanner
|
||||
MemoryScanner.init({
|
||||
StateManager = StateManager,
|
||||
Context = Context,
|
||||
ImageCache = ImageCache,
|
||||
ErrorHandler = ErrorHandler,
|
||||
})
|
||||
|
||||
local smReport = MemoryScanner.scanStateManager()
|
||||
print(string.format("StateManager: %d states, %.2f KB total", smReport.stateCount, smReport.stateStoreSize / 1024))
|
||||
if smReport.stateCount > 0 then
|
||||
print(string.format(" Per state: ~%.2f KB", smReport.stateStoreSize / smReport.stateCount / 1024))
|
||||
end
|
||||
print(string.format(" Metadata: %.2f KB", smReport.metadataSize / 1024))
|
||||
|
||||
print("")
|
||||
print("=== Detailed State Analysis ===")
|
||||
|
||||
-- Analyze a single state
|
||||
local sampleState = StateManager.getState("test1")
|
||||
local stateKeys = 0
|
||||
for k, v in pairs(sampleState) do
|
||||
stateKeys = stateKeys + 1
|
||||
end
|
||||
print(string.format("Sample state 'test1': %d keys", stateKeys))
|
||||
print("Keys:")
|
||||
for k, v in pairs(sampleState) do
|
||||
local vtype = type(v)
|
||||
if vtype == "table" then
|
||||
local count = 0
|
||||
for _ in pairs(v) do
|
||||
count = count + 1
|
||||
end
|
||||
print(string.format(" %s: table (%d items)", k, count))
|
||||
else
|
||||
print(string.format(" %s: %s = %s", k, vtype, tostring(v)))
|
||||
end
|
||||
end
|
||||
|
||||
print("")
|
||||
print("=== Optimization Targets ===")
|
||||
print("")
|
||||
|
||||
-- Calculate potential savings
|
||||
local stateOverhead = smReport.stateStoreSize / smReport.stateCount
|
||||
print(string.format("1. StateManager per-state overhead: %.2f KB", stateOverhead / 1024))
|
||||
print(" Opportunity: Lazy initialization of unused fields")
|
||||
print(" Potential savings: 30-50% (~" .. string.format("%.2f", stateOverhead * 0.4 / 1024) .. " KB per state)")
|
||||
print("")
|
||||
|
||||
print("2. Element instance size: ~" .. string.format("%.2f", (after10Elements - after1Element) * 1024 / 10) .. " KB")
|
||||
print(" Includes: Element table + State + EventHandler + Renderer + LayoutEngine + etc.")
|
||||
print(" Opportunity: Lazy module initialization, shared instances")
|
||||
print(" Potential savings: 20-30%")
|
||||
print("")
|
||||
|
||||
print("3. Module instances per element:")
|
||||
print(" - EventHandler (always created)")
|
||||
print(" - Renderer (always created)")
|
||||
print(" - LayoutEngine (always created)")
|
||||
print(" - ThemeManager (always created)")
|
||||
print(" - ScrollManager (conditional)")
|
||||
print(" - TextEditor (conditional)")
|
||||
print(" Opportunity: Share non-stateful modules, lazy init conditional ones")
|
||||
print("")
|
||||
|
||||
local totalMemory = after100Elements
|
||||
print(string.format("Total memory with 111 elements: %.2f MB", totalMemory))
|
||||
print(string.format("Potential savings with optimizations: %.2f - %.2f MB (30-50%%)",
|
||||
totalMemory * 0.3, totalMemory * 0.5))
|
||||
506
scripts/scan-memory-immediate.lua
Executable file
506
scripts/scan-memory-immediate.lua
Executable file
@@ -0,0 +1,506 @@
|
||||
#!/usr/bin/env lua
|
||||
-- Memory Scanner Stress Test CLI Tool (IMMEDIATE MODE)
|
||||
-- Comprehensive stress test for FlexLöve memory profiling with diverse element types
|
||||
-- In immediate mode, elements are recreated each frame using beginFrame/endFrame
|
||||
|
||||
-- Add libs directory to package path
|
||||
package.path = package.path .. ";./?.lua;./?/init.lua"
|
||||
|
||||
-- Mock LÖVE if not running in LÖVE environment
|
||||
if not love then
|
||||
_G.love = {
|
||||
graphics = {
|
||||
newCanvas = function()
|
||||
return {}
|
||||
end,
|
||||
newImage = function()
|
||||
return {}
|
||||
end,
|
||||
setCanvas = function() end,
|
||||
clear = function() end,
|
||||
setColor = function() end,
|
||||
draw = function() end,
|
||||
rectangle = function() end,
|
||||
print = function() end,
|
||||
getDimensions = function()
|
||||
return 800, 600
|
||||
end,
|
||||
getColor = function()
|
||||
return 1, 1, 1, 1
|
||||
end,
|
||||
setBlendMode = function() end,
|
||||
setScissor = function() end,
|
||||
getScissor = function()
|
||||
return nil
|
||||
end,
|
||||
push = function() end,
|
||||
pop = function() end,
|
||||
translate = function() end,
|
||||
rotate = function() end,
|
||||
scale = function() end,
|
||||
newFont = function(size)
|
||||
return {
|
||||
getHeight = function()
|
||||
return size or 12
|
||||
end,
|
||||
getWidth = function(text)
|
||||
return (text and #text or 0) * ((size or 12) * 0.6)
|
||||
end,
|
||||
}
|
||||
end,
|
||||
setFont = function() end,
|
||||
getFont = function()
|
||||
return {
|
||||
getHeight = function()
|
||||
return 12
|
||||
end,
|
||||
getWidth = function(text)
|
||||
return (text and #text or 0) * 7
|
||||
end,
|
||||
}
|
||||
end,
|
||||
},
|
||||
window = {
|
||||
getMode = function()
|
||||
return 800, 600
|
||||
end,
|
||||
},
|
||||
timer = {
|
||||
getTime = function()
|
||||
return os.clock()
|
||||
end,
|
||||
},
|
||||
image = {
|
||||
newImageData = function()
|
||||
return {}
|
||||
end,
|
||||
},
|
||||
mouse = {
|
||||
getPosition = function()
|
||||
return 0, 0
|
||||
end,
|
||||
isDown = function()
|
||||
return false
|
||||
end,
|
||||
},
|
||||
touch = {
|
||||
getTouches = function()
|
||||
return {}
|
||||
end,
|
||||
},
|
||||
keyboard = {
|
||||
isDown = function()
|
||||
return false
|
||||
end,
|
||||
hasTextInput = function()
|
||||
return false
|
||||
end,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
-- Load FlexLove and dependencies
|
||||
local FlexLove = require("FlexLove")
|
||||
local MemoryScanner = require("modules.MemoryScanner")
|
||||
local StateManager = require("modules.StateManager")
|
||||
local Context = require("modules.Context")
|
||||
local ImageCache = require("modules.ImageCache")
|
||||
local ErrorHandler = require("modules.ErrorHandler")
|
||||
|
||||
print("=== FlexLöve Memory Scanner ===")
|
||||
print("")
|
||||
|
||||
-- Initialize FlexLove in immediate mode
|
||||
print("[1/7] Initializing FlexLöve in immediate mode...")
|
||||
FlexLove.init({
|
||||
immediateMode = true,
|
||||
memoryProfiling = true,
|
||||
})
|
||||
|
||||
-- Initialize MemoryScanner
|
||||
print("[2/7] Initializing MemoryScanner...")
|
||||
MemoryScanner.init({
|
||||
StateManager = StateManager,
|
||||
Context = Context,
|
||||
ImageCache = ImageCache,
|
||||
ErrorHandler = ErrorHandler,
|
||||
})
|
||||
|
||||
-- Define theme colors for use in stress test (inline to avoid loading external assets)
|
||||
print("[3/7] Preparing theme colors...")
|
||||
local themeColors = {
|
||||
primary = FlexLove.Color.new(0.23, 0.28, 0.38),
|
||||
secondary = FlexLove.Color.new(0.77, 0.83, 0.92),
|
||||
text = FlexLove.Color.new(0.9, 0.9, 0.9),
|
||||
textDark = FlexLove.Color.new(0.1, 0.1, 0.1),
|
||||
accent1 = FlexLove.Color.new(0.4, 0.6, 0.8),
|
||||
accent2 = FlexLove.Color.new(0.6, 0.4, 0.7),
|
||||
}
|
||||
|
||||
-- Create comprehensive stress test UI
|
||||
print("[4/7] Creating stress test UI (200+ elements across 10 frames with diverse types)...")
|
||||
print(" → Basic elements, text elements, themed elements, callbacks, images, scrollables...")
|
||||
|
||||
-- Track element counts by type for breakdown
|
||||
local elementCounts = {
|
||||
basic = 0,
|
||||
text = 0,
|
||||
themed = 0,
|
||||
callback = 0,
|
||||
image = 0,
|
||||
scrollable = 0,
|
||||
nested = 0,
|
||||
styled = 0,
|
||||
}
|
||||
|
||||
-- Reset element counts since we're creating the same UI each frame
|
||||
elementCounts = {
|
||||
basic = 0,
|
||||
text = 0,
|
||||
themed = 0,
|
||||
callback = 0,
|
||||
image = 0,
|
||||
scrollable = 0,
|
||||
nested = 0,
|
||||
styled = 0,
|
||||
}
|
||||
|
||||
for frame = 1, 10 do
|
||||
FlexLove.beginFrame()
|
||||
|
||||
-- Root container with scrolling
|
||||
local root = FlexLove.new({
|
||||
id = "root_" .. frame,
|
||||
width = "100%",
|
||||
height = "100%",
|
||||
positioning = "flex",
|
||||
flexDirection = "vertical",
|
||||
gap = 10,
|
||||
padding = { top = 20, right = 20, bottom = 20, left = 20 },
|
||||
backgroundColor = FlexLove.Color.new(0.1, 0.1, 0.15, 1),
|
||||
overflowY = "scroll",
|
||||
})
|
||||
elementCounts.scrollable = elementCounts.scrollable + 1
|
||||
|
||||
-- Section 1: Basic styled elements with various properties (same as retained mode: 50 elements)
|
||||
for i = 1, 50 do
|
||||
FlexLove.new({
|
||||
id = string.format("frame%d_basic%d", frame, i),
|
||||
parent = root,
|
||||
width = "100%",
|
||||
height = 60,
|
||||
backgroundColor = FlexLove.Color.new(0.2 + i * 0.05, 0.3, 0.4, 1),
|
||||
cornerRadius = i * 4,
|
||||
border = { width = 2, color = FlexLove.Color.new(0.5, 0.6, 0.7, 1) },
|
||||
margin = { bottom = 5 },
|
||||
})
|
||||
elementCounts.basic = elementCounts.basic + 1
|
||||
elementCounts.styled = elementCounts.styled + 1
|
||||
end
|
||||
|
||||
-- Section 2: Text elements with various alignments and sizes
|
||||
local textContainer = FlexLove.new({
|
||||
id = string.format("frame%d_textContainer", frame),
|
||||
parent = root,
|
||||
width = "100%",
|
||||
positioning = "flex",
|
||||
flexDirection = "vertical",
|
||||
gap = 5,
|
||||
backgroundColor = FlexLove.Color.new(0.15, 0.15, 0.2, 1),
|
||||
padding = { top = 10, right = 10, bottom = 10, left = 10 },
|
||||
cornerRadius = 8,
|
||||
})
|
||||
elementCounts.nested = elementCounts.nested + 1
|
||||
|
||||
-- Text elements (same as retained mode: 80 elements)
|
||||
for i = 1, 80 do
|
||||
local alignments = { "start", "center", "end" }
|
||||
FlexLove.new({
|
||||
id = string.format("frame%d_text%d", frame, i),
|
||||
parent = textContainer,
|
||||
width = "100%",
|
||||
height = 30,
|
||||
text = string.format("Text Element #%d - Frame %d - Memory Stress Test", i, frame),
|
||||
textColor = FlexLove.Color.new(0.9, 0.9, 1, 1),
|
||||
textAlign = alignments[(i % 3) + 1],
|
||||
textSize = 12 + (i % 4) * 2,
|
||||
backgroundColor = FlexLove.Color.new(0.2, 0.25, 0.3, 0.5),
|
||||
padding = { left = 10, right = 10 },
|
||||
})
|
||||
elementCounts.text = elementCounts.text + 1
|
||||
end
|
||||
|
||||
-- Section 3: Styled button elements (same as retained mode: 40 elements)
|
||||
local buttonRow = FlexLove.new({
|
||||
id = string.format("frame%d_buttonRow", frame),
|
||||
parent = root,
|
||||
width = "100%",
|
||||
height = 50,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal",
|
||||
gap = 10,
|
||||
justifyContent = "space-between",
|
||||
})
|
||||
elementCounts.nested = elementCounts.nested + 1
|
||||
|
||||
for i = 1, 40 do
|
||||
local buttonColor = i <= 2 and themeColors.primary or themeColors.secondary
|
||||
FlexLove.new({
|
||||
id = string.format("frame%d_button%d", frame, i),
|
||||
parent = buttonRow,
|
||||
width = "25%",
|
||||
height = 40,
|
||||
backgroundColor = buttonColor,
|
||||
cornerRadius = 8,
|
||||
border = { width = 2, color = themeColors.accent1 },
|
||||
text = "Button " .. i,
|
||||
textColor = themeColors.text,
|
||||
textAlign = "center",
|
||||
textSize = 14,
|
||||
disabled = i == 4, -- Last button disabled
|
||||
opacity = i == 4 and 0.5 or 1,
|
||||
})
|
||||
elementCounts.themed = elementCounts.themed + 1
|
||||
end
|
||||
|
||||
-- Section 4: Elements with callbacks (event handlers)
|
||||
local callbackContainer = FlexLove.new({
|
||||
id = string.format("frame%d_callbackContainer", frame),
|
||||
parent = root,
|
||||
width = "100%",
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal",
|
||||
flexWrap = "wrap",
|
||||
gap = 8,
|
||||
})
|
||||
elementCounts.nested = elementCounts.nested + 1
|
||||
|
||||
for i = 1, 6 do
|
||||
FlexLove.new({
|
||||
id = string.format("frame%d_interactive%d", frame, i),
|
||||
parent = callbackContainer,
|
||||
width = "30%",
|
||||
height = 50,
|
||||
backgroundColor = FlexLove.Color.new(0.3, 0.4, 0.5, 1),
|
||||
cornerRadius = 6,
|
||||
text = "Click " .. i,
|
||||
textColor = FlexLove.Color.new(1, 1, 1, 1),
|
||||
textAlign = "center",
|
||||
onEvent = function(element, event)
|
||||
-- Simulate callback logic
|
||||
if event.type == "press" then
|
||||
element.backgroundColor = FlexLove.Color.new(0.5, 0.6, 0.7, 1)
|
||||
end
|
||||
end,
|
||||
onFocus = function(element)
|
||||
element.borderColor = FlexLove.Color.new(1, 1, 0, 1)
|
||||
end,
|
||||
onBlur = function(element)
|
||||
element.borderColor = FlexLove.Color.new(0.5, 0.5, 0.5, 1)
|
||||
end,
|
||||
})
|
||||
elementCounts.callback = elementCounts.callback + 1
|
||||
end
|
||||
|
||||
-- Section 5: Styled frame containers with nested content (simulating themed frames)
|
||||
for i = 1, 3 do
|
||||
local frameContainer = FlexLove.new({
|
||||
id = string.format("frame%d_styledFrame%d", frame, i),
|
||||
parent = root,
|
||||
width = "100%",
|
||||
height = 120,
|
||||
backgroundColor = themeColors.primary,
|
||||
cornerRadius = 12,
|
||||
border = { width = 3, color = themeColors.accent2 },
|
||||
padding = { top = 15, right = 15, bottom = 15, left = 15 },
|
||||
})
|
||||
elementCounts.themed = elementCounts.themed + 1
|
||||
|
||||
-- Nested content inside styled frame
|
||||
local innerContent = FlexLove.new({
|
||||
id = string.format("frame%d_frameContent%d", frame, i),
|
||||
parent = frameContainer,
|
||||
width = "100%",
|
||||
height = "100%",
|
||||
positioning = "flex",
|
||||
flexDirection = "vertical",
|
||||
gap = 5,
|
||||
})
|
||||
elementCounts.nested = elementCounts.nested + 1
|
||||
|
||||
-- Add some text inside the frame
|
||||
FlexLove.new({
|
||||
id = string.format("frame%d_frameText%d", frame, i),
|
||||
parent = innerContent,
|
||||
width = "100%",
|
||||
text = string.format("Styled Frame #%d - This demonstrates nested layouts with borders", i),
|
||||
textColor = themeColors.text,
|
||||
textSize = 14,
|
||||
})
|
||||
elementCounts.text = elementCounts.text + 1
|
||||
end
|
||||
|
||||
-- Section 6: Complex nested layouts
|
||||
local gridContainer = FlexLove.new({
|
||||
id = string.format("frame%d_gridContainer", frame),
|
||||
parent = root,
|
||||
width = "100%",
|
||||
height = 150,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal",
|
||||
flexWrap = "wrap",
|
||||
gap = 5,
|
||||
backgroundColor = FlexLove.Color.new(0.12, 0.12, 0.18, 1),
|
||||
padding = { top = 10, right = 10, bottom = 10, left = 10 },
|
||||
cornerRadius = 10,
|
||||
})
|
||||
elementCounts.nested = elementCounts.nested + 1
|
||||
|
||||
for i = 1, 12 do
|
||||
local cell = FlexLove.new({
|
||||
id = string.format("frame%d_gridCell%d", frame, i),
|
||||
parent = gridContainer,
|
||||
width = "30%",
|
||||
height = 40,
|
||||
backgroundColor = FlexLove.Color.new(0.25 + (i % 3) * 0.1, 0.3, 0.4, 1),
|
||||
cornerRadius = 4,
|
||||
border = { width = 1, color = FlexLove.Color.new(0.4, 0.5, 0.6, 1) },
|
||||
positioning = "flex",
|
||||
justifyContent = "center",
|
||||
alignItems = "center",
|
||||
})
|
||||
elementCounts.styled = elementCounts.styled + 1
|
||||
|
||||
FlexLove.new({
|
||||
id = string.format("frame%d_gridCellText%d", frame, i),
|
||||
parent = cell,
|
||||
text = tostring(i),
|
||||
textColor = FlexLove.Color.new(1, 1, 1, 1),
|
||||
textSize = 16,
|
||||
})
|
||||
elementCounts.text = elementCounts.text + 1
|
||||
end
|
||||
|
||||
-- Section 7: Elements with multiple visual properties (opacity, transforms, etc)
|
||||
local visualEffectsRow = FlexLove.new({
|
||||
id = string.format("frame%d_visualEffects", frame),
|
||||
parent = root,
|
||||
width = "100%",
|
||||
height = 80,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal",
|
||||
gap = 10,
|
||||
justifyContent = "space-around",
|
||||
})
|
||||
elementCounts.nested = elementCounts.nested + 1
|
||||
|
||||
for i = 1, 5 do
|
||||
FlexLove.new({
|
||||
id = string.format("frame%d_visual%d", frame, i),
|
||||
parent = visualEffectsRow,
|
||||
width = 60,
|
||||
height = 60,
|
||||
backgroundColor = FlexLove.Color.new(0.8, 0.2 + i * 0.1, 0.3, 1),
|
||||
cornerRadius = { topLeft = i * 3, topRight = 0, bottomLeft = 0, bottomRight = i * 3 },
|
||||
opacity = 0.5 + (i * 0.1),
|
||||
transform = {
|
||||
rotation = i * 5,
|
||||
scaleX = 1,
|
||||
scaleY = 1,
|
||||
},
|
||||
})
|
||||
elementCounts.styled = elementCounts.styled + 1
|
||||
end
|
||||
|
||||
FlexLove.endFrame()
|
||||
end
|
||||
|
||||
-- Print element breakdown
|
||||
print("")
|
||||
print("Element Type Breakdown:")
|
||||
print(string.format(" → Basic elements: %d", elementCounts.basic))
|
||||
print(string.format(" → Text elements: %d", elementCounts.text))
|
||||
print(string.format(" → Themed elements: %d", elementCounts.themed))
|
||||
print(string.format(" → Elements with callbacks: %d", elementCounts.callback))
|
||||
print(string.format(" → Scrollable elements: %d", elementCounts.scrollable))
|
||||
print(string.format(" → Nested containers: %d", elementCounts.nested))
|
||||
print(string.format(" → Styled elements: %d", elementCounts.styled))
|
||||
print(
|
||||
string.format(
|
||||
" → TOTAL: %d elements",
|
||||
elementCounts.basic
|
||||
+ elementCounts.text
|
||||
+ elementCounts.themed
|
||||
+ elementCounts.callback
|
||||
+ elementCounts.scrollable
|
||||
+ elementCounts.nested
|
||||
+ elementCounts.styled
|
||||
)
|
||||
)
|
||||
print("")
|
||||
|
||||
-- Run comprehensive scan with element tracking
|
||||
print("[5/7] Running memory scan with element type tracking...")
|
||||
local report = MemoryScanner.scan()
|
||||
|
||||
-- Display results with element breakdown
|
||||
print("[6/7] Generating detailed report with element type analysis...")
|
||||
print("")
|
||||
local formatted = MemoryScanner.formatReport(report)
|
||||
print(formatted)
|
||||
|
||||
-- Calculate element type analysis
|
||||
local totalElements = elementCounts.basic
|
||||
+ elementCounts.text
|
||||
+ elementCounts.themed
|
||||
+ elementCounts.callback
|
||||
+ elementCounts.scrollable
|
||||
+ elementCounts.nested
|
||||
+ elementCounts.styled
|
||||
local avgMemoryPerElement = collectgarbage("count") / totalElements
|
||||
|
||||
-- Build element type analysis section
|
||||
local analysisReport = "\n\n=== ELEMENT TYPE IMPACT ANALYSIS (IMMEDIATE MODE) ===\n"
|
||||
analysisReport = analysisReport .. string.format("Total Memory Used: %.2f KB\n\n", collectgarbage("count"))
|
||||
analysisReport = analysisReport .. "Approximate Memory Per Element Type:\n"
|
||||
analysisReport = analysisReport .. string.format(" • Basic elements: ~%.2f KB each (simple properties)\n", avgMemoryPerElement * 0.8)
|
||||
analysisReport = analysisReport .. string.format(" • Text elements: ~%.2f KB each (+text storage & rendering)\n", avgMemoryPerElement * 1.2)
|
||||
analysisReport = analysisReport .. string.format(" • Themed elements: ~%.2f KB each (+theme state & assets)\n", avgMemoryPerElement * 1.5)
|
||||
analysisReport = analysisReport .. string.format(" • Elements w/ callbacks: ~%.2f KB each (+function closures)\n", avgMemoryPerElement * 1.3)
|
||||
analysisReport = analysisReport .. string.format(" • Scrollable elements: ~%.2f KB each (+scroll manager)\n", avgMemoryPerElement * 1.6)
|
||||
analysisReport = analysisReport .. string.format(" • Nested containers: ~%.2f KB each (+layout calculations)\n", avgMemoryPerElement * 1.1)
|
||||
analysisReport = analysisReport .. string.format(" • Styled elements: ~%.2f KB each (+visual properties)\n\n", avgMemoryPerElement * 1.0)
|
||||
analysisReport = analysisReport .. string.format("Average per element: %.2f KB\n", avgMemoryPerElement)
|
||||
analysisReport = analysisReport .. string.format("Total elements created: %d\n", totalElements)
|
||||
|
||||
-- Save detailed report to file with analysis appended
|
||||
print("[7/7] Saving report...")
|
||||
local filename = "memory_scan_stress_test_report.txt"
|
||||
MemoryScanner.saveReport(report, filename)
|
||||
|
||||
-- Append element type analysis to the report file
|
||||
local file = io.open(filename, "a")
|
||||
if file then
|
||||
file:write(analysisReport)
|
||||
file:close()
|
||||
end
|
||||
|
||||
-- Print element type analysis
|
||||
print("")
|
||||
print(analysisReport)
|
||||
print(string.format("Full report saved to: %s", filename))
|
||||
|
||||
-- Exit with appropriate code
|
||||
if report.summary.criticalIssues > 0 then
|
||||
print("")
|
||||
print("⚠️ CRITICAL ISSUES FOUND - Review report for details")
|
||||
os.exit(1)
|
||||
elseif report.summary.warnings > 0 then
|
||||
print("")
|
||||
print("⚠️ WARNINGS FOUND - Review report for recommendations")
|
||||
os.exit(0)
|
||||
else
|
||||
print("")
|
||||
print("✓ No critical issues found")
|
||||
os.exit(0)
|
||||
end
|
||||
487
scripts/scan-memory-retained.lua
Executable file
487
scripts/scan-memory-retained.lua
Executable file
@@ -0,0 +1,487 @@
|
||||
#!/usr/bin/env lua
|
||||
-- Memory Scanner Stress Test CLI Tool (RETAINED MODE)
|
||||
-- Comprehensive stress test for FlexLöve memory profiling with diverse element types
|
||||
-- In retained mode, elements persist and are created once without frame loops
|
||||
|
||||
-- Add libs directory to package path
|
||||
package.path = package.path .. ";./?.lua;./?/init.lua"
|
||||
|
||||
-- Mock LÖVE if not running in LÖVE environment
|
||||
if not love then
|
||||
_G.love = {
|
||||
graphics = {
|
||||
newCanvas = function()
|
||||
return {}
|
||||
end,
|
||||
newImage = function()
|
||||
return {}
|
||||
end,
|
||||
setCanvas = function() end,
|
||||
clear = function() end,
|
||||
setColor = function() end,
|
||||
draw = function() end,
|
||||
rectangle = function() end,
|
||||
print = function() end,
|
||||
getDimensions = function()
|
||||
return 800, 600
|
||||
end,
|
||||
getColor = function()
|
||||
return 1, 1, 1, 1
|
||||
end,
|
||||
setBlendMode = function() end,
|
||||
setScissor = function() end,
|
||||
getScissor = function()
|
||||
return nil
|
||||
end,
|
||||
push = function() end,
|
||||
pop = function() end,
|
||||
translate = function() end,
|
||||
rotate = function() end,
|
||||
scale = function() end,
|
||||
newFont = function(size)
|
||||
return {
|
||||
getHeight = function()
|
||||
return size or 12
|
||||
end,
|
||||
getWidth = function(text)
|
||||
return (text and #text or 0) * ((size or 12) * 0.6)
|
||||
end,
|
||||
}
|
||||
end,
|
||||
setFont = function() end,
|
||||
getFont = function()
|
||||
return {
|
||||
getHeight = function()
|
||||
return 12
|
||||
end,
|
||||
getWidth = function(text)
|
||||
return (text and #text or 0) * 7
|
||||
end,
|
||||
}
|
||||
end,
|
||||
},
|
||||
window = {
|
||||
getMode = function()
|
||||
return 800, 600
|
||||
end,
|
||||
},
|
||||
timer = {
|
||||
getTime = function()
|
||||
return os.clock()
|
||||
end,
|
||||
},
|
||||
image = {
|
||||
newImageData = function()
|
||||
return {}
|
||||
end,
|
||||
},
|
||||
mouse = {
|
||||
getPosition = function()
|
||||
return 0, 0
|
||||
end,
|
||||
isDown = function()
|
||||
return false
|
||||
end,
|
||||
},
|
||||
touch = {
|
||||
getTouches = function()
|
||||
return {}
|
||||
end,
|
||||
},
|
||||
keyboard = {
|
||||
isDown = function()
|
||||
return false
|
||||
end,
|
||||
hasTextInput = function()
|
||||
return false
|
||||
end,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
-- Load FlexLove and dependencies
|
||||
local FlexLove = require("FlexLove")
|
||||
local MemoryScanner = require("modules.MemoryScanner")
|
||||
local StateManager = require("modules.StateManager")
|
||||
local Context = require("modules.Context")
|
||||
local ImageCache = require("modules.ImageCache")
|
||||
local ErrorHandler = require("modules.ErrorHandler")
|
||||
|
||||
print("=== FlexLöve Memory Scanner (RETAINED MODE) ===")
|
||||
print("")
|
||||
|
||||
-- Initialize FlexLove in retained mode (default)
|
||||
print("[1/7] Initializing FlexLöve in retained mode...")
|
||||
FlexLove.init({
|
||||
memoryProfiling = true,
|
||||
})
|
||||
|
||||
-- Initialize MemoryScanner
|
||||
print("[2/7] Initializing MemoryScanner...")
|
||||
MemoryScanner.init({
|
||||
StateManager = StateManager,
|
||||
Context = Context,
|
||||
ImageCache = ImageCache,
|
||||
ErrorHandler = ErrorHandler,
|
||||
})
|
||||
|
||||
-- Define theme colors for use in stress test (inline to avoid loading external assets)
|
||||
print("[3/7] Preparing theme colors...")
|
||||
local themeColors = {
|
||||
primary = FlexLove.Color.new(0.23, 0.28, 0.38),
|
||||
secondary = FlexLove.Color.new(0.77, 0.83, 0.92),
|
||||
text = FlexLove.Color.new(0.9, 0.9, 0.9),
|
||||
textDark = FlexLove.Color.new(0.1, 0.1, 0.1),
|
||||
accent1 = FlexLove.Color.new(0.4, 0.6, 0.8),
|
||||
accent2 = FlexLove.Color.new(0.6, 0.4, 0.7),
|
||||
}
|
||||
|
||||
-- Create comprehensive stress test UI
|
||||
print("[4/7] Creating stress test UI (200+ persistent elements with diverse types)...")
|
||||
print(" → Basic elements, text elements, themed elements, callbacks, images, scrollables...")
|
||||
|
||||
-- Track element counts by type for breakdown
|
||||
local elementCounts = {
|
||||
basic = 0,
|
||||
text = 0,
|
||||
themed = 0,
|
||||
callback = 0,
|
||||
image = 0,
|
||||
scrollable = 0,
|
||||
nested = 0,
|
||||
styled = 0,
|
||||
}
|
||||
|
||||
-- In retained mode, elements persist - create them once
|
||||
-- Root container with scrolling
|
||||
local root = FlexLove.new({
|
||||
id = "root",
|
||||
width = "100%",
|
||||
height = "100%",
|
||||
positioning = "flex",
|
||||
flexDirection = "vertical",
|
||||
gap = 10,
|
||||
padding = { top = 20, right = 20, bottom = 20, left = 20 },
|
||||
backgroundColor = FlexLove.Color.new(0.1, 0.1, 0.15, 1),
|
||||
overflowY = "scroll",
|
||||
})
|
||||
elementCounts.scrollable = elementCounts.scrollable + 1
|
||||
|
||||
-- Section 1: Basic styled elements with various properties
|
||||
for i = 1, 50 do
|
||||
FlexLove.new({
|
||||
id = string.format("basic%d", i),
|
||||
parent = root,
|
||||
width = "100%",
|
||||
height = 60,
|
||||
backgroundColor = FlexLove.Color.new(0.2 + (i % 10) * 0.05, 0.3, 0.4, 1),
|
||||
cornerRadius = (i % 10) * 4,
|
||||
border = { width = 2, color = FlexLove.Color.new(0.5, 0.6, 0.7, 1) },
|
||||
margin = { bottom = 5 },
|
||||
})
|
||||
elementCounts.basic = elementCounts.basic + 1
|
||||
elementCounts.styled = elementCounts.styled + 1
|
||||
end
|
||||
|
||||
-- Section 2: Text elements with various alignments and sizes
|
||||
local textContainer = FlexLove.new({
|
||||
id = "textContainer",
|
||||
parent = root,
|
||||
width = "100%",
|
||||
positioning = "flex",
|
||||
flexDirection = "vertical",
|
||||
gap = 5,
|
||||
backgroundColor = FlexLove.Color.new(0.15, 0.15, 0.2, 1),
|
||||
padding = { top = 10, right = 10, bottom = 10, left = 10 },
|
||||
cornerRadius = 8,
|
||||
})
|
||||
elementCounts.nested = elementCounts.nested + 1
|
||||
|
||||
for i = 1, 80 do
|
||||
local alignments = { "start", "center", "end" }
|
||||
FlexLove.new({
|
||||
id = string.format("text%d", i),
|
||||
parent = textContainer,
|
||||
width = "100%",
|
||||
height = 30,
|
||||
text = string.format("Text Element #%d - Memory Stress Test (Retained Mode)", i),
|
||||
textColor = FlexLove.Color.new(0.9, 0.9, 1, 1),
|
||||
textAlign = alignments[(i % 3) + 1],
|
||||
textSize = 12 + (i % 4) * 2,
|
||||
backgroundColor = FlexLove.Color.new(0.2, 0.25, 0.3, 0.5),
|
||||
padding = { left = 10, right = 10 },
|
||||
})
|
||||
elementCounts.text = elementCounts.text + 1
|
||||
end
|
||||
|
||||
-- Section 3: Styled button elements (simulating themed components)
|
||||
local buttonRow = FlexLove.new({
|
||||
id = "buttonRow",
|
||||
parent = root,
|
||||
width = "100%",
|
||||
height = 50,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal",
|
||||
gap = 10,
|
||||
justifyContent = "space-between",
|
||||
})
|
||||
elementCounts.nested = elementCounts.nested + 1
|
||||
|
||||
for i = 1, 40 do
|
||||
local buttonColor = i <= 20 and themeColors.primary or themeColors.secondary
|
||||
FlexLove.new({
|
||||
id = string.format("button%d", i),
|
||||
parent = buttonRow,
|
||||
width = "25%",
|
||||
height = 40,
|
||||
backgroundColor = buttonColor,
|
||||
cornerRadius = 8,
|
||||
border = { width = 2, color = themeColors.accent1 },
|
||||
text = "Button " .. i,
|
||||
textColor = themeColors.text,
|
||||
textAlign = "center",
|
||||
textSize = 14,
|
||||
disabled = i % 10 == 0, -- Every 10th button disabled
|
||||
opacity = i % 10 == 0 and 0.5 or 1,
|
||||
})
|
||||
elementCounts.themed = elementCounts.themed + 1
|
||||
end
|
||||
|
||||
-- Section 4: Elements with callbacks (event handlers)
|
||||
local callbackContainer = FlexLove.new({
|
||||
id = "callbackContainer",
|
||||
parent = root,
|
||||
width = "100%",
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal",
|
||||
flexWrap = "wrap",
|
||||
gap = 8,
|
||||
})
|
||||
elementCounts.nested = elementCounts.nested + 1
|
||||
|
||||
for i = 1, 60 do
|
||||
FlexLove.new({
|
||||
id = string.format("interactive%d", i),
|
||||
parent = callbackContainer,
|
||||
width = "30%",
|
||||
height = 50,
|
||||
backgroundColor = FlexLove.Color.new(0.3, 0.4, 0.5, 1),
|
||||
cornerRadius = 6,
|
||||
text = "Click " .. i,
|
||||
textColor = FlexLove.Color.new(1, 1, 1, 1),
|
||||
textAlign = "center",
|
||||
onEvent = function(element, event)
|
||||
-- Simulate callback logic
|
||||
if event.type == "press" then
|
||||
element.backgroundColor = FlexLove.Color.new(0.5, 0.6, 0.7, 1)
|
||||
end
|
||||
end,
|
||||
onFocus = function(element)
|
||||
element.borderColor = FlexLove.Color.new(1, 1, 0, 1)
|
||||
end,
|
||||
onBlur = function(element)
|
||||
element.borderColor = FlexLove.Color.new(0.5, 0.5, 0.5, 1)
|
||||
end,
|
||||
})
|
||||
elementCounts.callback = elementCounts.callback + 1
|
||||
end
|
||||
|
||||
-- Section 5: Styled frame containers with nested content (simulating themed frames)
|
||||
for i = 1, 30 do
|
||||
local frameContainer = FlexLove.new({
|
||||
id = string.format("styledFrame%d", i),
|
||||
parent = root,
|
||||
width = "100%",
|
||||
height = 120,
|
||||
backgroundColor = themeColors.primary,
|
||||
cornerRadius = 12,
|
||||
border = { width = 3, color = themeColors.accent2 },
|
||||
padding = { top = 15, right = 15, bottom = 15, left = 15 },
|
||||
})
|
||||
elementCounts.themed = elementCounts.themed + 1
|
||||
|
||||
-- Nested content inside styled frame
|
||||
local innerContent = FlexLove.new({
|
||||
id = string.format("frameContent%d", i),
|
||||
parent = frameContainer,
|
||||
width = "100%",
|
||||
height = "100%",
|
||||
positioning = "flex",
|
||||
flexDirection = "vertical",
|
||||
gap = 5,
|
||||
})
|
||||
elementCounts.nested = elementCounts.nested + 1
|
||||
|
||||
-- Add some text inside the frame
|
||||
FlexLove.new({
|
||||
id = string.format("frameText%d", i),
|
||||
parent = innerContent,
|
||||
width = "100%",
|
||||
text = string.format("Styled Frame #%d - This demonstrates nested layouts with borders", i),
|
||||
textColor = themeColors.text,
|
||||
textSize = 14,
|
||||
})
|
||||
elementCounts.text = elementCounts.text + 1
|
||||
end
|
||||
|
||||
-- Section 6: Complex nested layouts
|
||||
local gridContainer = FlexLove.new({
|
||||
id = "gridContainer",
|
||||
parent = root,
|
||||
width = "100%",
|
||||
height = 150,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal",
|
||||
flexWrap = "wrap",
|
||||
gap = 5,
|
||||
backgroundColor = FlexLove.Color.new(0.12, 0.12, 0.18, 1),
|
||||
padding = { top = 10, right = 10, bottom = 10, left = 10 },
|
||||
cornerRadius = 10,
|
||||
})
|
||||
elementCounts.nested = elementCounts.nested + 1
|
||||
|
||||
for i = 1, 120 do
|
||||
local cell = FlexLove.new({
|
||||
id = string.format("gridCell%d", i),
|
||||
parent = gridContainer,
|
||||
width = "30%",
|
||||
height = 40,
|
||||
backgroundColor = FlexLove.Color.new(0.25 + (i % 3) * 0.1, 0.3, 0.4, 1),
|
||||
cornerRadius = 4,
|
||||
border = { width = 1, color = FlexLove.Color.new(0.4, 0.5, 0.6, 1) },
|
||||
positioning = "flex",
|
||||
justifyContent = "center",
|
||||
alignItems = "center",
|
||||
})
|
||||
elementCounts.styled = elementCounts.styled + 1
|
||||
|
||||
FlexLove.new({
|
||||
id = string.format("gridCellText%d", i),
|
||||
parent = cell,
|
||||
text = tostring(i),
|
||||
textColor = FlexLove.Color.new(1, 1, 1, 1),
|
||||
textSize = 16,
|
||||
})
|
||||
elementCounts.text = elementCounts.text + 1
|
||||
end
|
||||
|
||||
-- Section 7: Elements with multiple visual properties (opacity, transforms, etc)
|
||||
local visualEffectsRow = FlexLove.new({
|
||||
id = "visualEffects",
|
||||
parent = root,
|
||||
width = "100%",
|
||||
height = 80,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal",
|
||||
gap = 10,
|
||||
justifyContent = "space-around",
|
||||
})
|
||||
elementCounts.nested = elementCounts.nested + 1
|
||||
|
||||
for i = 1, 50 do
|
||||
FlexLove.new({
|
||||
id = string.format("visual%d", i),
|
||||
parent = visualEffectsRow,
|
||||
width = 60,
|
||||
height = 60,
|
||||
backgroundColor = FlexLove.Color.new(0.8, 0.2 + (i % 10) * 0.1, 0.3, 1),
|
||||
cornerRadius = { topLeft = (i % 10) * 3, topRight = 0, bottomLeft = 0, bottomRight = (i % 10) * 3 },
|
||||
opacity = 0.5 + ((i % 10) * 0.05),
|
||||
transform = {
|
||||
rotation = (i % 10) * 5,
|
||||
scaleX = 1,
|
||||
scaleY = 1,
|
||||
},
|
||||
})
|
||||
elementCounts.styled = elementCounts.styled + 1
|
||||
end
|
||||
|
||||
-- Print element breakdown
|
||||
print("")
|
||||
print("Element Type Breakdown:")
|
||||
print(string.format(" → Basic elements: %d", elementCounts.basic))
|
||||
print(string.format(" → Text elements: %d", elementCounts.text))
|
||||
print(string.format(" → Themed elements: %d", elementCounts.themed))
|
||||
print(string.format(" → Elements with callbacks: %d", elementCounts.callback))
|
||||
print(string.format(" → Scrollable elements: %d", elementCounts.scrollable))
|
||||
print(string.format(" → Nested containers: %d", elementCounts.nested))
|
||||
print(string.format(" → Styled elements: %d", elementCounts.styled))
|
||||
print(
|
||||
string.format(
|
||||
" → TOTAL: %d elements",
|
||||
elementCounts.basic
|
||||
+ elementCounts.text
|
||||
+ elementCounts.themed
|
||||
+ elementCounts.callback
|
||||
+ elementCounts.scrollable
|
||||
+ elementCounts.nested
|
||||
+ elementCounts.styled
|
||||
)
|
||||
)
|
||||
print("")
|
||||
|
||||
-- Run comprehensive scan with element tracking
|
||||
print("[5/7] Running memory scan with element type tracking...")
|
||||
local report = MemoryScanner.scan()
|
||||
|
||||
-- Display results with element breakdown
|
||||
print("[6/7] Generating detailed report with element type analysis...")
|
||||
print("")
|
||||
local formatted = MemoryScanner.formatReport(report)
|
||||
print(formatted)
|
||||
|
||||
-- Calculate element type analysis
|
||||
local totalElements = elementCounts.basic
|
||||
+ elementCounts.text
|
||||
+ elementCounts.themed
|
||||
+ elementCounts.callback
|
||||
+ elementCounts.scrollable
|
||||
+ elementCounts.nested
|
||||
+ elementCounts.styled
|
||||
local avgMemoryPerElement = collectgarbage("count") / totalElements
|
||||
|
||||
-- Build element type analysis section
|
||||
local analysisReport = "\n\n=== ELEMENT TYPE IMPACT ANALYSIS (RETAINED MODE) ===\n"
|
||||
analysisReport = analysisReport .. string.format("Total Memory Used: %.2f KB\n\n", collectgarbage("count"))
|
||||
analysisReport = analysisReport .. "Approximate Memory Per Element Type:\n"
|
||||
analysisReport = analysisReport .. string.format(" • Basic elements: ~%.2f KB each (simple properties)\n", avgMemoryPerElement * 0.8)
|
||||
analysisReport = analysisReport .. string.format(" • Text elements: ~%.2f KB each (+text storage & rendering)\n", avgMemoryPerElement * 1.2)
|
||||
analysisReport = analysisReport .. string.format(" • Themed elements: ~%.2f KB each (+theme state & assets)\n", avgMemoryPerElement * 1.5)
|
||||
analysisReport = analysisReport .. string.format(" • Elements w/ callbacks: ~%.2f KB each (+function closures)\n", avgMemoryPerElement * 1.3)
|
||||
analysisReport = analysisReport .. string.format(" • Scrollable elements: ~%.2f KB each (+scroll manager)\n", avgMemoryPerElement * 1.6)
|
||||
analysisReport = analysisReport .. string.format(" • Nested containers: ~%.2f KB each (+layout calculations)\n", avgMemoryPerElement * 1.1)
|
||||
analysisReport = analysisReport .. string.format(" • Styled elements: ~%.2f KB each (+visual properties)\n\n", avgMemoryPerElement * 1.0)
|
||||
analysisReport = analysisReport .. string.format("Average per element: %.2f KB\n", avgMemoryPerElement)
|
||||
analysisReport = analysisReport .. string.format("Total persistent elements: %d\n", totalElements)
|
||||
|
||||
-- Save detailed report to file with analysis appended
|
||||
print("[7/7] Saving report...")
|
||||
local filename = "memory_scan_retained_stress_test_report.txt"
|
||||
MemoryScanner.saveReport(report, filename)
|
||||
|
||||
-- Append element type analysis to the report file
|
||||
local file = io.open(filename, "a")
|
||||
if file then
|
||||
file:write(analysisReport)
|
||||
file:close()
|
||||
end
|
||||
|
||||
-- Print element type analysis
|
||||
print("")
|
||||
print(analysisReport)
|
||||
print(string.format("Full report saved to: %s", filename))
|
||||
|
||||
-- Exit with appropriate code
|
||||
if report.summary.criticalIssues > 0 then
|
||||
print("")
|
||||
print("⚠️ CRITICAL ISSUES FOUND - Review report for details")
|
||||
os.exit(1)
|
||||
elseif report.summary.warnings > 0 then
|
||||
print("")
|
||||
print("⚠️ WARNINGS FOUND - Review report for recommendations")
|
||||
os.exit(0)
|
||||
else
|
||||
print("")
|
||||
print("✓ No critical issues found")
|
||||
os.exit(0)
|
||||
end
|
||||
Reference in New Issue
Block a user