HU/Debugging: Difference between revisions
Line 257: | Line 257: | ||
==Teljesítménybeli problémák elhárítása== | ==Teljesítménybeli problémák elhárítása== | ||
Ha a szervere több erőforrást használ el, mint kelle, vagy csak azt szeretné, hogy a scriptjei hatékonyak legyenek, megtalálhatja a probléma forrását egy nagyszerű eszköz használatával, amely az alapértelmezett resource csomaggal együtt érkezik: [[Resource:Performancebrowser|performancebrowser]]. Elindíthatja a ''''start performancebrowser''''-el. Ha ez nem létezik, akkor letöltheti a legfrissebb resourcet a [https://github.com/multitheftauto/mtasa-resources GitHub repository]-ról. Ez az eszköz hihetetlen mennyiségű információt nyújt a teljesítménybeli hibakereséshez. | Ha a szervere több erőforrást használ el, mint kelle, vagy csak azt szeretné, hogy a scriptjei hatékonyak legyenek, megtalálhatja a probléma forrását egy nagyszerű eszköz használatával, amely az alapértelmezett resource csomaggal együtt érkezik: [[Resource:Performancebrowser|performancebrowser]]. Elindíthatja a ''''start performancebrowser''''-el. Ha ez nem létezik, akkor letöltheti a legfrissebb resourcet a [https://github.com/multitheftauto/mtasa-resources GitHub repository]-ról. Ez az eszköz hihetetlen mennyiségű információt nyújt a teljesítménybeli hibakereséshez. Memória szivárgások, elem szivárgások, CPU igényes scriptek könnyedén megtalálhatóak a teljesítményböngészőn keresztül. Ha a ''''d'''' opciót használja a Lua időzítőben, akkor láthatja, hogy mely function-ök használja a CPU-t. | ||
A teljesítményböngésző eléréséhez megkell nyitni a web böngészőt, és beírni a következő címet: http://serverIPHere:serverHTTPPortHere/performancebrowser/ Vegye figyelembe, hogy a vége az szükséges. Például: http://127.0.0.1:22005/performancebrowser/ Be kell jelentkeznie egy játékbeli admin felhasználóval, vagy egy olyan felhasználóval mely jogosult a ''''resource.performancebrowser.http'''' és ''''resource.ajax.http'''' belépéshez. | |||
A legtöbb szükséges információ a Lua időzítőben és a Lua memória kategóriákban található, olyan értékeket keressen, amelyeik sokkal magasabbak, mint a többi érték. | |||
===Examples of scripts that could cause performance problems=== | ===Examples of scripts that could cause performance problems=== |
Revision as of 15:58, 5 November 2018
Scriptelés során gyakran találkozhat olyan hibákkal, amelyek nem azonnal nyilvánvalóak. Ez az oldal megpróbál rámutatni néhány alapvető stratégiára, hogy megtalálja a hibát.
Hibakereső konzol
Az MTA tartalmaz egy beépített hibakereső konzolt, mely a scriptekből, vagy az MTA-ból származó hibákat jelenítni meg. Vegye figyelembe, hogy Admin hozzáféréssel kell, hogy rendelkezzen (hacsak nem módosítja ezt az ACL-ben), megnyithatja a hibakereső konzolt a debugscript *x* beírásával, ahol az x a hibakeresési szintet jelöli:
- 1: csak hibákat
- 2: hibákat és figyelmeztetéseket
- 3: hibákat, figyelmeztetéseket és információs üzeneteket
A debugscript 3 beírásával az összes üzenet látható. A 3-as és a 2-es szint a legtöbb helyzetben ajánlott. A debugscript-nek engedélyezettnek kell lennie, amikor teszteli a scriptjeit, ez segíteni fog, hogy könnyedén észrevegye és megoldja az esetleges gépelési hibákat, vagy más problémákat.
Példa
Ez a példa tartalmaz két hibát:
function SayHello(message, player) if (getPlayerName(player) == "Fedor") outputChatbox("Hello Fedor") end end addEventHandler("onChatMessage", root, SayHello)
Ha ezt a kódot lefuttatjuk akkor ezt a hibaüzenetet fogjuk kapni:
- INFO: Loading script failed: myResource\script.lua:2: 'then' expected near ´outputChatbox'
Ez azt jelenti, hogy a scriptet nem lehet elemezni, mert szintaktikai hiba volt benne. Ez megmutatja a script elérési útját a resource könyvtárból, így azonosítani tudja azt a scriptet, ahonnan a hiba származik. A fájlnév után egy kettőspontot és egy számot jelenít meg, ez a szám azt a sorszámot jeleníti meg, ahol megtalálhatja hol van a hiba a script-ben. Azután pedig a hibaüzenet, mely a létrejövő hibától függően változik. Ha megnézzük a hibaüzenetet, akkor könnyen megállapíthatjuk, hogy a biba a myResource könyvtárból származik, és a script.lua fájljának a második sorából. A hibaüzenet tisztán mutatja, hogy elfelejtettük a "then"-t! De ezt könnyedén kijavíthatjuk!
function SayHello(message, player) if (getPlayerName(player) == "Fedor") then outputChatbox("Hello Fedor") end end addEventHandler("onChatMessage", root, SayHello)
Most a script szépen be fog töltődni és semmilyen hibaüzenetet nem fog visszaadni mindaddig, míg egy 'Fedor' nevű játékos nem ír ki valamit a chat-ba, ebben a pillanatban a hibakereső konzól ezt fogja kiírni:
- ERROR: myResource\script.lua:2: attempt to call global 'outputChatbox' (a nil value)
Ez az error azt jelenti, hogy a outputChatbox egy nil érték, az-az, nem létezik. Ez azért van, mert a funkciót outputChatBox-nak hivják, és nem outputChatbox-nak. Figyeljen a nagy 'B' betűre:
function SayHello(message, player) if (getPlayerName(player) == "Fedor") then outputChatBox("Hello Fedor") end end addEventHandler("onChatMessage", root, SayHello)
Természetesen ez csak egy példa, és csak a hibaüzeneteknek a felszínét érintettem. Nagyon sok más üzenet és eshetőség létezik, de egy általános elképzelést kell kapnia.
Szerver & Cliens hibakeresési napló
Szerver
Menjen a: (MTA mappa)>server>mods>deathmatch mappába
Van itt két majdnem azonos fájl.
- A local.conf fájl a "host game" menü item-re vonatkozó beállításokat tartalmazza, mely az MTA főmenüjében van. Ez egy gyors és egyszerű módja az ideiglenes szerver indításának a client oldaláról. Ha a client (host) leáll, akkor a server is leáll.
- Az mtaserver.conf akkor van használatban, amikor az "MTA Server.exe" végrehajtódott az (MTA fő mappa)>server-ből. Ez futtatja a szervert a saját konzolablakában, mely nem igényli a client meglétét vagy futtatását. Ez akkor lehet hasznos, ha hosszabb időre szeretne szervert futtatni.
Attól függen, hogy milyen módon futtat, aszerint kell szerkeszteni ezeket a fájlokat. A kérdéses beállítások a következők:
<!-- Megadhatja a debugscript log fájl helyét és nevét. Ha üresen hagyja, akkor a server nem fogja elmenteni ezt a fájlt --> <scriptdebuglogfile>logs/scripts.log</scriptdebuglogfile> <!-- Megadhatja a log fájl debug szintjét! Lehetséges értékek: 0, 1, 2, 3. Ha nincs beállítva, akkor automatikusan 0. --> <scriptdebugloglevel>0</scriptdebugloglevel>
Bizonyosodjon meg arról, hogy megvan-e adva a log név. Azt is meg tudja határozni, hogy a hibák melyik szintje legyen naplózva. Ha ez 0, akkor semmi nem lesz naplózva. A szintek magyarázata a cikk tetején található. Ha a naplózási szint megváltozott 3-ra, akkor ebben az esetben minden szerver oldali script hibát a (MTA fő mappa)>server>mods>deathmatch>logs>scripts.log fájlba fogja naplózni.
Kliens
Menjen a: (MTA mappa)>server>clientscript.log
Ez a file az összes kliensoldali script hibákat naplózza. Ez alapértelmezetten naplózásra van beállítva, nincs szükség beállításra.
Hibakeresési stratégiák
Számos stratégia létezik, amelyek támogatják a hibakeresést. Legtöbbjük közé tartozik a kimenő Hibaüzenetek, a különböző információktól függően helyezkednek el.
Hasznos funkciók
Vannak olyan funkciók, amelyek jól jöhetnek a hibakereséshez.
- outputDebugString vagy outputChatBox bármilyen információ kiírásához (használja az outputDebugString a technikai kiíráshoz)
- tostring() egy változó, amely az értéket egy string-re változtatja. Hasznos, ha az érték nem egy szám vagy egy string.
- inspect visszaad egy az ember számára olvasható stringet bármilyen adattípusról, beleértve a táblákat, nem elemi felhasználói adatokat, mint az ACL groupok, felhasználók, elemeke, stb.
- iprint hasonló az ellenőrzéshez, kivéve, hogy annyi paramétert vesz igénybe, amennyi szökséges, majd kiírja őket a debugscript-be.
- getElementType ellenőrzni az MTA elem típusát.
- isElement ellenőrzi, hogy az MTA elem létezik-e.
Adjon meg debugmessage-t annak ellenőrzéséhez, hogy mikor, illetve milyen gyakran hajtja végre a kód egy részét
Egy tipikus példa igazolja, hogy az if-rész az végrehajtódott-e, vagy sem. Ehhez csak adjon hozzá egy olyan üzenetet, amit majd később fel fog ismerni az if -részben.
if (variable1 == variable2) then outputDebugString("variable1 is the same as variable2!") -- do anything end
Egy másik használata a változó értékek módosításának ellenőrzése lenne. Először keresse meg a szerkeszteni kívánt változó összes előfordulását, majd addjon hozzá egy üzenetet.
Adjon hozzá debugmessage-t egy változó értékének ellenőrzéséhez
Tegyük fel, hogy létre szeretne hozni egy markert, de nem abban a pozícióban jelenik meg, mint amire számított. Az első dolog, amit érdemes lehet ellenőrizni, hogy a createMarker függvény lefutott-e. De amíg ezt használja, közben azokat az értékeket is tudja ellenőrizni, amik a createMarker funkcióban vannak használva.
outputChatBox("posX is: "..x.." posY is: "..y.." posZ is: "..z) createMarker(x,y,z)
Ez mind a három változót kiírja, melyek a marker kordinátájaként lettek használva. Feltételezve, hogy kiolvassa ezeket a map fájlból, most már össze tudja hasonlítani a debug kiírást a kívánt értékekkel. A tostring() gondoskodni fog arról, hogy a változók értékei összefűzhetőek legyenek stringként, még akkor is, ha ez egy logikai érték.
Példa
Képzelje el, hogy valahol létrehozott egy collision shape-t, és szeretné, ha valami végrehajtódna miután a játékos tíz másodpercig benne marad:
function colShapeHit(player) -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit", root, colShapeHit) function colShapeLeave(player) -- kill the timer when the player leaves the colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave", root, colShapeLeave)
Amikor a játékos belép a colshape-be, a debugscript a következő üzenetet fogja kiírni:
- ERROR: ..[path]: attempt to index global 'colshapeTimer' (a nil value)
Ez azt jelenti, hogy megpróbált rámutatni egy olyan táblára, ami nem létezik (mert a tábla egy nik értékű, nem létezik). Ez akkor történik, amikor a timer id-jét a táblába akarja helyezi. Hozzá kell adnunk egy ellenőrzést, hogy a tábla létezik-e, és ha nem, akkor hozzon létre egyet.
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit", root, colShapeHit) function colShapeLeave(player) -- kill the timer when the player leaves the colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",root,colShapeLeave)
Még mindig figyelmeztetést fogunk kapni, amikor a játékos belép a colshape-be, várjon az üzenetre, majd lépjen ki belőle (colshape-ből):
- WARNING: [..]: Bad argument @ 'killTimer' Line: ..
Ettől eltekintve (erről majd később beszélünk) minden jónak tűnik. A játékos belép a colshape-be, a timer elindul, ha ott marad az üzenet megjelenik, ha kilép, a timer megsemmisül.
Egy kevésbé észrevehető hiba
De valami oknál fogva az üzenet kétszer jelenik meg amikor a colkörben marad egy autóban ülve. Úgy tűnik, hogy valamennyi kód kétszer hajtódik végre, ezért debug üzenetet adunk hozzá, hogy ezt ellenőrizze.
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- add a debug message outputDebugString("colShapeHit") -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- add a debug message outputDebugString("colShapeLeave") -- kill the timer when the player leaves the colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
Most észre vesszük, hogy mindkét handler functions kétszer hajtódik végre, amikor egy járműben vagyunk, de csak egyszer, ha gyalogosok vagyunk. Úgy tűnik, hogy a jármű meghívja a colshape-t is. Az elmélet megerősítése érdekében biztosítjuk, hogy a player változó valójában egy játékoselemre hivatkozzon.
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- add a debug message, with the element type outputDebugString("colShapeHit "..getElementType(player)) -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- add a debug message, with the element type outputDebugString("colShapeLeave "..getElementType(player)) -- kill the timer when the player leaves the colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
A debug elmondja nekünk, hogy az egyik player változó az egy játékos, a másik pedig egy jármű elem. Mivel csak arra akarunk reagálni, ha egy játékos lép be a colshape-be, ezért hozzáadunk egy if-et, mely megakadájozza, hogy a function lefusson, ha ez nem egy játékos elem.
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- add a check for the element type if (getElementType(player) ~= "player") then return end -- add a debug message, with the element type outputDebugString("colShapeHit "..getElementType(player)) -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- add a check for the element type if (getElementType(player) ~= "player") then return end -- add a debug message, with the element type outputDebugString("colShapeLeave "..getElementType(player)) -- kill the timer when the player leaves the colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
Most a scriptnek a kívánt módon kell működnie, de továbbra is kiírja a fent említett hibaüzenetet. Ez azért van, mert az időzítő, melyet ki akarunk kapcsolni mikor egy játékos elhagyja a colshape-et, már nem létezik mikor elérte a 10 másodpercet (és ezáltal lefutott a tizedik másodperc után). Különböző módokon tudunk megszabadulni ezektől a figyelmeztetésektől (mivel tudjunk, hogy a timer már nem létezik, és csak akkor akarjuk megsemmisíteni, ha létezik). Az egyik mód az, hogy ellenőrizzük, tényleg létezik-e a táblázatban szereplő timer. Ehhez használnunk kell az isTimer-t, amit akkor fogunk használni, amikor megsemmisítettük a timert:
if (isTimer(colshapeTimer[player])) then killTimer(colshapeTimer[player]) end
Tehát a teljes működő kód így nézne ki:
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- add a check for the element type if (getElementType(player) ~= "player") then return end -- add a debug message, with the element type outputDebugString("colShapeHit "..getElementType(player)) -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- add a check for the element type if (getElementType(player) ~= "player") then return end -- add a debug message, with the element type outputDebugString("colShapeLeave "..getElementType(player)) -- kill the timer when the player leaves the colshape if (isTimer(colshapeTimer[player])) then killTimer(colshapeTimer[player]) end end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
Teljesítménybeli problémák elhárítása
Ha a szervere több erőforrást használ el, mint kelle, vagy csak azt szeretné, hogy a scriptjei hatékonyak legyenek, megtalálhatja a probléma forrását egy nagyszerű eszköz használatával, amely az alapértelmezett resource csomaggal együtt érkezik: performancebrowser. Elindíthatja a 'start performancebrowser'-el. Ha ez nem létezik, akkor letöltheti a legfrissebb resourcet a GitHub repository-ról. Ez az eszköz hihetetlen mennyiségű információt nyújt a teljesítménybeli hibakereséshez. Memória szivárgások, elem szivárgások, CPU igényes scriptek könnyedén megtalálhatóak a teljesítményböngészőn keresztül. Ha a 'd' opciót használja a Lua időzítőben, akkor láthatja, hogy mely function-ök használja a CPU-t.
A teljesítményböngésző eléréséhez megkell nyitni a web böngészőt, és beírni a következő címet: http://serverIPHere:serverHTTPPortHere/performancebrowser/ Vegye figyelembe, hogy a vége az szükséges. Például: http://127.0.0.1:22005/performancebrowser/ Be kell jelentkeznie egy játékbeli admin felhasználóval, vagy egy olyan felhasználóval mely jogosult a 'resource.performancebrowser.http' és 'resource.ajax.http' belépéshez. A legtöbb szükséges információ a Lua időzítőben és a Lua memória kategóriákban található, olyan értékeket keressen, amelyeik sokkal magasabbak, mint a többi érték.
Examples of scripts that could cause performance problems
Adding data to a table but never removing it. This would take months/years before it causes a problem though.
local someData = {} function storeData() someData[source] = true -- There is no handling for when a player quits, this is considered a memory leak -- Using the Lua timing tab you can detect the RAM usage of each resource. end addEventHandler("onPlayerJoin", root, storeData)
Element leaking is possible if you use temporary colshapes for whatever reason and may not destroy them. This would cause bandwidth, CPU and memory performance issues over time.
function useTemporaryCol() local col = createColCircle(some code here) if (normally this should happen) then destroyElement(col) end -- But sometimes it didn't so the script ended but the collision area remained and over time -- you may end up with hundreds to thousands of pointless collision areas. -- The Lua timing tab allows you to see the amount of elements each script has created. end
High CPU usage resulting in the server FPS dropping so much that the server is unplayable. In under 24 hours this can create havoc on a very busy server. The amount of "refs" in the Lua timing detect this type of build up, surprisingly the Lua timing tab didn't help in this case but Lua memory did.
addEventHandler("onPlayerJoin", root, function() -- Code for joiner addEventHandler("onPlayerQuit", root, function() -- Code for when they have quit -- See the problem? It's bound to root which the event handler is being added again and again and again end) end)
A function uses up a lot of your CPU because whatever it does takes a long time. This is just some function that takes a long time to complete. Without performancebrowser you'd have no idea its the cause but with performancebrowser you can see that a resource is using lots of CPU in the Lua timing tab. If you then enter: 'd' into the options edit box it will even tell you what file name and first line of the function that is using up so much CPU.
function someDodgyCode() for i=1, 100000 do -- some code end end
Fordította
- Kevin
- Surge