DEBUG LIBRARY
BAB 23 DEBUG LIBRARY
Library debug tidak memberi kami suatu debugger untuk Lua, tetapi menawarkan sesuatu yang primitif yang kami perlukan untuk penulisan suatu debugger untuk Lua. untuk alasan performance , penghubung official untuk primitif menyelesaikan C API. Debug library dalam Lua adalah suatu cara untuk mengakses fungsi ini secara langsung di dalam Lua kode. Library ini mengumumkan semua fungsi nya di dalam tabel debug.
Tidak sama dengan library yang lain, kami perlu menggunakan debug library dengan hemat. Pertama, sebagian dari kemampuan nya adalah tidak baik untuk performance. Ke dua, ia memecahkan beberapa kebenaran bahasa, seperti bahwa kami tidak bisa mengakses suatu variabel lokal dari luar fungsi yang menciptakannya. Sering, kami tidak bisa membuka library ini pada versi akhir dari suatu produk, atau kami menghapusnya : Tidak sama dengan library yang lain, kami perlu menggunakan debug library dengan hemat. Pertama, sebagian dari kemampuan nya adalah tidak baik untuk performance. Ke dua, ia memecahkan beberapa kebenaran bahasa, seperti bahwa kami tidak bisa mengakses suatu variabel lokal dari luar fungsi yang menciptakannya. Sering, kami tidak bisa membuka library ini pada versi akhir dari suatu produk, atau kami menghapusnya :
Debug library meliputi dua macam fungsi yaitu fungsi introspektif dan hooks. Fungsi introspektif mengijinkan kami untuk memeriksa beberapa aspek dari running program, seperti tumpukan dari fungsi aktif, baris yang sedang dieksekusi, nilai dan nama dari variabel lokal. Hooks mengijinkan kami untuk melacak pelaksanaan dari suatu program.
Suatu konsep penting dalam debug library adalah level stack. Suatu level stack adalah suatu jumlah yang mengacu pada fungsi tertentu yang aktif pada saat itu,yang telah dipanggil dan belum mengembalikan. Fungsi memanggil debug library mempunyai tingkatan 1, fungsi yang memanggilnya mempunyai tingkatan 2, dan seterusnya.
23.1 Fasilitas Introspeksi Fungsi introspektif utama dalam library debug adalah fungsi debug.getinfo. Parameter pertamanya bisa suatu fungsi atau suatu tumpukan. Ketika kami memanggil debug.getinfo(foo) untuk beberapa fungsi foo, kami mendapatkan suatu tabel dengan beberapa data fungsi tersebut. Tabel bisa mempunyai bidang berikut :
source--- Di mana fungsi digambarkan. Jika fungsi digambarkan dalam suatu string (
melalui loadstring), sumbernya adalah string tersebut. Jika fungsi digambarkan di dalam suatu file, sumbernya adalah nama file prefixed dengan `@´.
short_src --- sebuah versi sederhana dari sumber (diatas 60 karakter), digunakan untuk pesan
kesalahan. linedefined --- baris dari sumber dimana fungsi didefinisikan. what --- Apa adalah fungsi. Pilihan adalah : " Lua" jika foo adalah suatu fungsi Lua reguler ,
" C" jika ini merupakan suatu fungsi C, atau " main" jika merupakan bagian utama dari suatu kumpulan Lua .
name --- suatu nama layak untuk sebuah fungsi namewhat--- adalah bidang yang sebelumnya . Bidang ini mungkin " global", " local", "
method", " bidang", atau " " ( string yang kosong). string yang kosong berarti Lua tidak menemukan suatu nama untuk fungsi itu.
nups--- Jumlah upvalues dari fungsi itu . func--- Fungsi dirinya sendiri.
Ketika foo adalah suatu fungsi C , Lua tidak mempunyai banyak data tentang itu. Karena untuk fungsi seperti itu, hanya what, name, dan namewhat yang relevan.
Ketika kami memanggil debug.getinfo(n) untuk beberapa jumlah n, kami mendapatkan data tentang fungsi yang aktif pada tingkatan tumpukan itu. Sebagai contoh, jika n adalah 1, kami mendapatkan data tentang fungsi yang melakukan panggilan. ( ketika n adalah 0, kami mendapatkan data tentang getinfonya, suatu fungsi C.) Jika n adalah lebih besar dari banyaknya fungsi yang aktif dalam tumpukan itu, debug.getinfo mengembalikan nol. Manakala kami mengquery suatu fungsi aktif, memanggil debug.getinfo dengan suatu jumlah, tabel hasil mempunyai suatu bidang ekstra, currentline, dengan baris di mana fungsi pada saat itu. Lebih dari itu, func mempunyai fungsi yang aktif pada tingkatan itu.
Field name adalah rumit. Karena fungsi adalah nilai-nilai terbaik pada Lua, suatu fungsi boleh tidak mempunyai nama, atau mungkin punya beberapa nama. Lua mencoba untuk menemukan suatu nama untuk suatu fungsi dengan mencari suatu nilai variabel global, atau mempelajari kode yang memanggil fungsi, untuk melihat bagaimana ia dipanggil. Pilihan kedua ini bekerja hanya ketika kami memanggil getinfo dengan suatu bilangan, kami mendapatkan informasi tentang doa tertentu .
Fungsi Getinfo tidak efisien. Lua menyimpan informasi debug dalam suatu format yang tidak merusak pelaksanaan program, mendapatkan kembali efisien merupakan tujuan sekunder di Fungsi Getinfo tidak efisien. Lua menyimpan informasi debug dalam suatu format yang tidak merusak pelaksanaan program, mendapatkan kembali efisien merupakan tujuan sekunder di
Fungsi berikut menggambarkan penggunaan debug.getinfo. Mencetak suatu primitive traceback dari tumpukan yang aktif:
function traceback () local level = 1 while true do
local info = debug.getinfo(level, "Sl") if not info then break end if info.what == "C" then -- is a C function? print(level, "C function") else -- a Lua function print(string.format("[%s]:%d", info.short_src, info.currentline)) end level = level + 1
end end
Tidaklah sulit untuk mengembangkan fungsi ini, memasukan banyak data dari getinfo. Kenyataannya, debug library menawarkan versi yang telah dikembangkan, debug.traceback. Tidak sama dengan versi kami, debug.traceback tidak mencetak hasil nya, sebagai gantinya, ia mengembalikan suatu string.
23.1.1 Pengaksesan Variabel Lokal Kami dapat mengakses variabel lokal dari fungsi yang aktif dengan pemanggilan getlocal, dari debug library. getlocal mempunyai dua parameter yaitu tingkatan stack dari fungsi yang kami ragukan dan suatu index variabel. Getlocal mengembalikan dua nilai yaitu nama dan nilai yang sekarang dari variabel tersebut . Jika index variabel lebih besar dari banyaknya variabel aktif, maka getlocal akan mengmbalikan nol. Jika tingkatan stack cacat, akan menimbukan suatu kesalahan. ( Kami dapat menggunakan debug.getinfo untuk memeriksa kebenaran dari suatu tingkatan stack.)
Angka-angka variabel lokal Lua di dalam order yang ditampilkan dalam suatu fungsi, hanya menghitung variabel yang aktif dalam lingkup sekarang dari fungsi. Sebagai contoh, kode :
function foo (a,b) ] local x do local c = a - b end local a = 1 while true do function foo (a,b) ] local x do local c = a - b end local a = 1 while true do
end
foo(10, 20)
akan mencetak
Variabel dengan index 1 adalah a ( parameter yang pertama), 2 adalah b, 3 adalah x, dan 4 adalah a yang lain . Di titik di mana getlocal dipanggil, c telah ke luar dari lingkup, ketika nilai dan nama tidak berada di dalam lingkup. ( Ingat bahwa variabel lokal hanya terlihat setelah initialisasi kode mereka.)
Kami juga dapat merubah nilai-nilai dari variabel lokal, dengan debug.setlocal. Dua parameter pertama adalah suatu tingkatan stack dan suatu index variabel, seperti di dalam getlocal. Parameter Ketiga adalah nilai baru untuk variabel tersebut. Ia akan mengembalikan nama variabel, atau nol jika index variabel di luar dari lingkup.
23.1.2 Pengaksesan Upvalues
Debug library juga mengijinkan kami untuk mengakses upvalues yang digunakan oleh suatu fungsi Lua, dengan getupvalue. Tidak sama dengan variabel lokal, bagaimanapun suatu fungsi mempunyai upvalues bahkan ketika tidak aktif ( ini adalah apa dimaksud penutupan). Oleh karena itu, argumentasi yang pertama untuk getupvalue bukanlah suatu tingkatan stack, tetapi suatu fungsi ( suatu penutup). Argumentasi yang kedua adalah upvalue index. Angka-angka upvalues Lua dalam order yang pertama ditunjuk dalam suatu fungsi, tetapi order ini tidak relevan, sebab suatu fungsi tidak bisa mempunyai dua upvalues dengan nama yang sama.
Kami juga dapat membaharui upvalues dengan debug.setupvalue. Debug.setupvalue mempunyai tiga parameter yaitu suatu penutup, suatu upvalue index, dan nilai baru . Seperti setlocal, debug.setupvalue mengembalikan nama dari upvalue, atau nol jika upvalue index berada di luar dari cakupan.
Kode berikut menunjukkan bagaimana kami dapat mengakses nilai given variabel dari suatu pemanggilan fungsi, nama given variabel:
function getvarvalue (name) local value, found -- try local variables local i = 1 while true do local n, v = debug.getlocal(2, i)
if not n then break end if n == name then
value = v found = true
end i=i+1
end if found then return value end – end if found then return value end –
local n, v = debug.getupvalue(func, i) if not n then break end if n == name then return v end i = i + 1
end ]
-- not found; get global return getfenv(func)[name] end
Pertama, kami mencoba suatu variabel lokal. Jika ada lebih dari satu variabel dengan given name , kami harus mendapatkan satu dengan index yang paling tinggi. Kami harus selalu menyelesaikan keseluruhan pengulangan. Jika kami tidak bisa menemukan satupun nama variabel lokal maka kami mencoba upvalues. Pertama, kami mendapatkan fungsi pemanggilan, dengan debug.getinfo(2).func, dan kemudian kami menyilang upvalues nya. Akhirnya, jika kami tidak bisa menemukan suatu nama upvalue maka kami mendapatkan suatu variabel global.
Mencatat penggunaan dari argumentasi 2 di dalam panggilan ke debug.getlocal dan debug.getinfo untuk mengakses fungsi pemanggilan.
23.2 Hooks
Mekanisme hook dari debug library mengijinkan kami untuk mendaftarkan suatu fungsi yang akan dipanggil pada peristiwa spesifik ketika program kami berjalan. Ada empat macam peristiwa yang dapat menimbulkan suatu hook yaitu peristiwa call akan terjadi setiap kali Lua memanggil suatu fungsi, peristiwa return akan terjadi setiap kali suatu fungsi dikembalikan, peristiwa line akan terjadi ketika Lua mulai melaksanakan suatu garis kode baru, dan peristiwa count terjadi setelah pemberian nomor instruksi. Lua memanggil hook dengan argumentasi tunggal, suatu string menggambarkan peristiwa yang menghasilkan panggilan " call", " return", " line", atau " count". Lebih dari itu, untuk peristiwa line juga melewati suatu argumentasi kedua. Kami bisa menggunakan debug.getinfo untuk mendapatkan informasi lebih di dalam suatu hook.
Untuk mendaftarkan suatu hook, kami memanggil debug.sethook dengan dua atau tiga argumentasi. Argumentasi Yang pertama adalah fungsi hook, argumentasi yang kedua adalah suatu string yang menguraikan peristiwa yang ingin kami monitor, dan argumentasi ketiga yang opsional adalah suatu bilangan yang menguraikan frekwensi apa yang kami perlukan untuk mendapatkan peristiwa count . Untuk memonitor call, retun, dan peristiwa line, kami menambahkan huruf pertama mereka (` c´, ` r´, atau ` l´) di dalam string topeng. Untuk memonitor peristiwa count, kami menyediakan suatu konter seperti argumentasi yang ketiga. Untuk mematikan hook, kami memanggil sethook dengan tidak ada argumentasi.
Sebagai contoh sederhana, kode berikut menginstal suatu pengusut primitif, yang mencetak jumlah dari masing-masing garis baru yang dijalankan interpreter:
debug.sethook(print, "l")
Sederhananya menempatkan print sebagai fungsi hook dan menginstruksikan Lua untuk memanggil nya hanya pada peristiwa line. Suatu pengusut yang lebih ditekuni dapat menggunakan getinfo untuk menambahkan nama file yang sekarang kepada bekas:
function trace (event, line) local s = debug.getinfo(2).short_src function trace (event, line) local s = debug.getinfo(2).short_src
23.3 Profil Di samping namanya, debug library bermanfaat untuk tugas selain dari debugging. Suatu tugas umum adalah profiling. Untuk suatu profil dengan pemilihan waktu, lebih baik menggunakan
C interface. Overhead dari suatu panggilan Lua untuk masing-masing hook terlalu tinggi dan pada umumnya membuat tidak berlakunya ukuran manapun. Bagaimanapun, untuk profil, Lua mengerjakan suatu pekerjaan pantas. dalam bagian ini, kami akan mengembangkan suatu profiler yang belum sempurna, yang mendaftarkan jumlah waktu dari masing-masing fungsi dalam program yang dipanggil dalam perjalanan.
Struktur data utama dari program kami adalah tabel yang menghubungkan fungsi kepada konter panggilan mereka dan suatu tabel yang menghubungkan fungsi kepada nama mereka. Indeks untuk tabel ini adalah fungsi diri mereka.
local Counters = {} local Names = {}
Kami bisa mendapat kembali data nama setelah profiling, tetapi ingat bahwa kami mendapatkan hasil yang lebih baik jika kami mendapatkan nama dari suatu fungsi ketika dalam keadaan aktif, sebab itu Lua dapat melihat kode yang sedang memanggil fungsi untuk menemukan namanya.
Sekarang kami menggambarkan fungsi hook. Pekerjaan nya adalah mendapatkan fungsi yang sedang dipanggil dan meningkatkan korespondensi konter; dan juga mengumpulkan nama fungsi:
local function hook () local f = debug.getinfo(2, "f").func if Counters[f] == nil then -- first time `f' is called? Counters[f] = 1 Names[f] = debug.getinfo(2, "Sn") else -- only increment the counter Counters[f] = Counters[f] + 1 end
end
Langkah Yang berikutnya adalah menjalankan program dengan hook . Kami akan berasumsi bahwa kumpulan utama dari program adalah dalam suatu file dan pemakai memberi nama file ini sebagai suatu argumentasi kepada profiler:
prompt> lua profiler main-prog
Dengan rencana ini , kami mendapatkan nama file di dalam arg[1], menyalakan hook, dan menjalankan file:
local f = assert(loadfile(arg[1])) debug.sethook(hook, "c") - - turn on the hook f() -- run the main program debug.sethook() -- turn off the hook
Langkah terakhir adalah menunjukkan hasil. Fungsi berikutnya menghasilkan suatu nama untuk suatu fungsi. Karena fungsi penamaan pada Lua sangat tidak pasti, kami menambah penempatan Langkah terakhir adalah menunjukkan hasil. Fungsi berikutnya menghasilkan suatu nama untuk suatu fungsi. Karena fungsi penamaan pada Lua sangat tidak pasti, kami menambah penempatan
function getname (func) local n = Names[func] if n.what == "C" then
return n.name
end local loc = string.format("[%s]:%s", n.short_src, n.linedefined) if n.namewhat ~= "" then
return string.format("%s (%s)", loc, n.name) else
return string.format("%s", loc)
end end
Akhirnya, mencetak masing-masing fungsi dengan konter nya :
for func, count in pairs(Counters) do print(getname(func), count) end
Jika kami menerapkan profiler kepada contoh markov yang kami kembangkan pada Bagian 10.2, kami mendapatkan suatu hasil seperti ini:
[markov.lua]:4 884723 write 10000 [markov.lua]:0 (f) 1 read 31103 sub 884722 [markov.lua]:1 (allwords) 1 [markov.lua]:20 (prefix) 894723 find 915824 [markov.lua]:26 (insert) 884723 random 10000 sethook 1 insert 884723
Itu berarti bahwa fungsi tanpa nama pada baris 4 ( yang mana merupakan fungsi iterator yang digambarkan di dalam allwords) disebut 884,723 kali, write ( io.write) disebut 10,000 kali, dan seterusnya.
Ada beberapa peningkatan yang dapat kami buat pada profiler ini, seperti untuk mengurutkan keluaran, untuk mencetak nama fungsi dengan lebih baik, dan untuk meningkatkan format keluaran. Meskipun demikian, dasar profiler ini telah bermanfaat dan dapat digunakan sebagai suatu dasar untuk lebih mengedepankan tools.
Latihan
1. Tuliskan suatu fungsi GetVar yang mengambil suatu nama variable dan mengembalikan nilai variable di dalam lingkup dimana ia dipanggila. Hal tersebut mengikuti aturan lingkup yang sama seperti Lua itu sendiri (locals, then upvalues, then globals) kecualiI tidak diperlukan untuk mencari local eketernal yang tidak menaikkan nilai (upvalues) secara actual. Variabel D pada 1. Tuliskan suatu fungsi GetVar yang mengambil suatu nama variable dan mengembalikan nilai variable di dalam lingkup dimana ia dipanggila. Hal tersebut mengikuti aturan lingkup yang sama seperti Lua itu sendiri (locals, then upvalues, then globals) kecualiI tidak diperlukan untuk mencari local eketernal yang tidak menaikkan nilai (upvalues) secara actual. Variabel D pada
Gl = “global” Up = “upvalue” L = “local” OL = “outer local” IL = “inner local”
A, B, C, D, E = Gl, Gl, Gl, Gl, Gl function OuterFnc() local A, B, C, D = Up, Up, Up, Up local function InnerFnc(A) local A = IL local B = L local _ = C -- Without this, C, like D, would not be an -- upvalue of InnerFnc. for _, Name in ipairs({“A”, “B”, “C”, “D”, “E”}) do print(Name, GetVar(Name))
end end InnerFnc(OL) end OuterFnc()
Kode di atas harus mencetak hasil berikut: A inner local B local C upvalue D global E global