• Tidak ada hasil yang ditemukan

Pengaksesan Upvalues

BAB 23 DEBUG LIBRARY

23.1 Fasilitas Introspeks

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

try upvalues

local func = debug.getinfo(2).func i = 1

while true do

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)

print(s .. ":" .. line) end

debug.sethook(trace, "l")

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

masing-masing fungsi , given sebagai pasangan file:line. Jika suatu fungsi tidak punya nama, maka kami hanya menggunakan penempatannya. Jika suatu fungsi adalah suatu fungsi C , kami hanya menggunakan nama nya ( tidak mempunyai 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

kode uji berikut sebagai contohnya. Berdasarkan aturan lingkup Lua, nilai D pada InnerFnc harus “upvalue” dan bukan “global”, tetapi pencarian menjadi upvalues seperti D secara actual adalah tidak mungkin untuk dilakukan kecual pada kasus-kasus tertentu.

Ujilah fungsi kita, jalankan kode berikut:

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