पिछले ट्यूटोरियल में, हमने सीखा कि एक प्रोग्राम मेमोरी से sBPF VM रजिस्टरों में कैसे पढ़ता है। अब, हम उस मॉडल को आगे बढ़ाते हुए यह दिखाएंगे कि प्रोग्राम syscalls के माध्यम से Solana रनटाइम कार्यक्षमता को कैसे लागू (invoke) करते हैं, जहाँ syscall के तर्क (arguments) रजिस्टरों के माध्यम से प्रदान किए जाते हैं।
एक syscall एक API है जिसे Solana रनटाइम द्वारा एक्सपोज़ और निष्पादित (execute) किया जाता है, जिसे प्रोग्राम उन ऑपरेशन्स को करने के लिए कॉल करते हैं जिन्हें वे स्वयं नहीं कर सकते, जैसे कि लॉगिंग और cross-program invocation।
यह इस प्रकार काम करता है:
- यदि syscall को तर्कों की आवश्यकता होती है, तो प्रोग्राम आर्गुमेंट रजिस्टरों (
r1सेr5तक) में वैल्यूज़ लोड करता है। - आपका प्रोग्राम
syscallनिर्देश (instruction) निष्पादित करता है और Solana रनटाइम को नियंत्रण (control) हस्तांतरित करता है।syscallहैंडलर उन रजिस्टरों से पढ़ता है जिनमें आपने वैल्यूज़ लोड की थीं। - रनटाइम अनुरोधित ऑपरेशन करता है और फिर नियंत्रण वापस प्रोग्राम को सौंप देता है।

Solana विभिन्न उद्देश्यों के लिए syscalls प्रदान करता है जैसे लॉगिंग, क्रिप्टोग्राफ़िक ऑपरेशन्स, cross-program invocation, sysvar एक्सेस, और मेमोरी ऑपरेशन्स। इस ट्यूटोरियल के लिए, हम लॉगिंग syscalls पर ध्यान केंद्रित करेंगे।
लॉगिंग के लिए syscalls
प्रोग्राम निष्पादन के दौरान वैल्यूज़ को प्रिंट करने के लिए लॉगिंग syscalls को कॉल करते हैं। पाँच लॉगिंग syscalls हैं, जो सभी नीचे सूचीबद्ध हैं। हम बाद के अनुभाग में उनमें से प्रत्येक पर विस्तार से चर्चा करेंगे।
fn sol_log_(message: *const u8, len: u64): यहsyscallप्रोग्राम लॉग में UTF-8 टेक्स्ट प्रिंट करता है।fn sol_log_data(data: *const u8, data_len: u64): यह प्रोग्राम लॉग में मनमाने बाइट डेटा (arbitrary byte data) को लॉग करता है।fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64): यहsyscallपाँच 64-बिट इंटीजर वैल्यूज़ को लॉग करता है।fn sol_log_pubkey(pubkey_addr: *const u8): यह प्रोग्राम लॉग में एक पब्लिक की (public key) को लॉग करता है।fn sol_log_compute_units_(): यहsyscallउस बिंदु पर शेष कंप्यूट यूनिट्स (compute units) की संख्या को लॉग करता है जहाँ इसे निष्पादित किया जाता है। यह कोई आर्गुमेंट नहीं लेता है।
इनमें से चार syscalls को आर्गुमेंट्स की आवश्यकता होती है। प्रोग्राम syscall को इनवोक करने और रनटाइम को नियंत्रण सौंपने से पहले उन आर्गुमेंट्स को रजिस्टरों में लोड करता है। sol_log_compute_units_ कोई आर्गुमेंट नहीं लेता है क्योंकि यह केवल रनटाइम की आंतरिक स्थिति (internal state) को क्वेरी करता है।
EVM में opcodes की तरह, हर syscall की एक कंप्यूट कॉस्ट (compute cost) होती है जो client source code में प्रलेखित (documented) है। जबकि sol_log_64_ जैसे syscalls की कंप्यूट यूनिट कॉस्ट निश्चित होती है, sol_log_ जैसे syscalls जो वेरिएबल-लेंथ डेटा को प्रोसेस करते हैं, उनकी कॉस्ट उनके इनपुट्स के आकार पर निर्भर करती है। डेवलपर्स उपभोग की गई (consumed) कंप्यूट यूनिट की मात्रा को मापने के लिए sol_log_compute_units_ syscall का उपयोग कर सकते हैं।
इसके बाद, हम मेमोरी से रजिस्टरों में डेटा लोड करने और syscalls के साथ इसे लॉग करने का प्रयोग (experiment) करने के लिए अपना वातावरण सेट करेंगे।
Setup
सुनिश्चित करें कि solana-test-validator चल रहा है। ये सेटअप चरण पूरे करें:
syscallsनाम का एक फोल्डर बनाएँ- असेंबली कोड के लिए एक फ़ाइल
syscalls/syscalls.asmबनाएँ - ट्रांज़ैक्शन डेटा के लिए एक फ़ाइल
syscalls/instructions.jsonबनाएँ और नीचे दिया गया JSON जोड़ें। हमारे प्रदर्शनों में, हमें अकाउंट्स की आवश्यकता नहीं होगी। हमारा मुख्य ध्यानinstruction_dataपर होगा। हम आगे बढ़ते हुएinstruction_dataकी सामग्री को अपडेट करेंगे:
{
"accounts": [],
"program_id": "HTpqQdG7f44su3QsV3HHurraR1ZNjHAdArCy3qHKyKBC",
"instruction_data": []
}
हम पिछले ट्यूटोरियल्स की समान कमांड का उपयोग करेंगे और केवल अपनी syscalls डायरेक्टरी के लिए फ़ाइल पथ (file paths) को संशोधित करेंगे।
agave-ledger-tool program run syscalls/syscalls.asm --limit 200000 --trace syscalls/trace.txt --ledger test-ledger --input syscalls/instructions.json
अब जब हमारा सेटअप पूरा हो गया है, आइए दिखाते हैं कि sBPF असेंबली में syscalls को कैसे निष्पादित (execute) किया जाता है।
sBPF असेंबली में syscalls कैसे निष्पादित करें
sBPF असेंबली में एक syscall नीचे दिए गए टेम्पलेट का पालन करता है। syscall को इनवोक करने से पहले आपको आर्गुमेंट वैल्यूज़ को रजिस्टरों में लोड करना होगा।
... ; instructions that copy arguments into registers
syscall <syscall_name>
हम इस पूरे लेख में अपने असेंबली कोड में इस टेम्पलेट का उपयोग करेंगे।
अगले अनुभाग में, हम sBPF असेंबली का उपयोग करके sol_log_ syscall को कॉल करेंगे।
sol_log_ के साथ स्ट्रिंग्स को लॉग करना 1/5
sol_log_ syscall मेमोरी में एक मैसेज स्ट्रिंग के पॉइंटर को पहले आर्गुमेंट के रूप में और मैसेज की लंबाई (length) को दूसरे आर्गुमेंट के रूप में लेता है और फिर मैसेज को प्रोग्राम लॉग में UTF-8 टेक्स्ट के रूप में लॉग करता है। sol_log_ syscall की Rust परिभाषा नीचे दिए गए कोड में है:
fn sol_log_(message: *const u8, len: u64)
आइए 3 चरणों में sol_log_ का उपयोग करके sBPF असेंबली में “Hello world” मैसेज को लॉग करने का प्रदर्शन करें:
- हम प्रोग्राम को निर्देश डेटा (instruction data) के रूप में “Hello world” का ASCII डेसिमल बाइट प्रतिनिधित्व (representation) पास करेंगे।
- रनटाइम इनपुट मेमोरी क्षेत्र (region) में निर्देश डेटा को कॉपी करेगा, और हमारा असेंबली कोड इसे बेस पॉइंटर के रूप में
r1का उपयोग करके पढ़ेगा क्योंकि इनपुट प्रोग्राम के प्रारंभ मेंr1में लोड हो जाते हैं। - इसके बाद, हम “Hello world” मैसेज को लॉग करेंगे।
सबसे पहले, instructions.json में instruction_data को “Hello world” के ASCII डेसिमल बाइट प्रतिनिधित्व के साथ अपडेट करें:
{
"accounts": [],
"program_id": "HTpqQdG7f44su3QsV3HHurraR1ZNjHAdArCy3qHKyKBC",
"instruction_data": [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
}
ध्यान दें कि sol_log_ syscall अपेक्षा करता है कि r1 में मेमोरी में मैसेज स्ट्रिंग का पॉइंटर हो और r2 में इसकी लंबाई हो (“Hello world” स्ट्रिंग 11 बाइट्स लंबी है)।
नीचे एक प्रोग्राम है जो sol_log_ syscall का उपयोग करके “Hello world” मैसेज प्रिंट करेगा। इसे syscalls/syscalls.asm फ़ाइल में कॉपी करें।
mov64 r3, r1 ; Copy r1 (0x400000000) to r3 to preserve base pointer
add64 r1, 16 ; r1 now points to first byte of "Hello world" (0x400000010)
ldxdw r2, [r3+8] ; Load instruction data length (11) from memory into r2
syscall sol_log_ ; Invoke syscall: r1=message pointer, r2=length
exit
आइए प्रत्येक निर्देश (instruction) को विस्तार से समझें:
पंक्ति 1/4, पहला निर्देश: mov64 r3, r1
यह निर्देश इनपुट मेमोरी क्षेत्र के प्रारंभिक पते (0x400000000) को r1 से r3 में सुरक्षित (preserve) रखने के लिए कॉपी करता है। हम इस वैल्यू को r3 में इसलिए सुरक्षित रखते हैं क्योंकि बाद में निर्देश डेटा की लंबाई पढ़ने के लिए हमें इसकी आवश्यकता होगी।
पंक्ति 2/4, दूसरा निर्देश: add64 r1, 16
इस निर्देश का उद्देश्य r1 में निर्देश डेटा के पॉइंटर को स्टोर करना है, जिसकी sol_log_ syscall अपेक्षा करता है।
यह निर्देश मेमोरी में अकाउंट काउंट और निर्देश डेटा की लंबाई से आगे r1 को बढ़ाता है ताकि यह निर्देश डेटा के पहले बाइट को इंगित करे, जो कि इस उदाहरण में “Hello world” स्ट्रिंग की शुरुआत है।
अकाउंट काउंट फ़ील्ड हमेशा मेमोरी में मौजूद होता है, भले ही निर्देश में कोई अकाउंट न हो:
- जब अकाउंट मौजूद होते हैं, तो अकाउंट काउंट के बाद अकाउंट मेटाडेटा, पब्लिक की (public keys), और अकाउंट डेटा आता है।
- जब कोई अकाउंट मौजूद नहीं होता है, तो अकाउंट काउंट के तुरंत बाद निर्देश डेटा की लंबाई (instruction data length) आती है।
बिना अकाउंट्स वाले हमारे मामले में, पहले 16 बाइट्स में केवल अकाउंट काउंट (8 बाइट्स) और निर्देश डेटा की लंबाई (8 बाइट्स) होती है।

इसलिए r1 मेमोरी में निर्देश डेटा के पहले बाइट को इंगित करता है और 0x400000000 + 16 (16 हेक्स में 0x10 है) = 0x400000010 हो जाता है। चूँकि हम लक्ष्य मेमोरी स्थान को पहले से जानते हैं, इसलिए lddw r1, 0x400000010 निर्देश का उपयोग करके r1 में “Hello world” मैसेज पॉइंटर लोड करना भी संभव है। हमने add64 निर्देश का उपयोग किया क्योंकि डायनामिक मेमोरी संरचनाओं को रिलेटिवली नेविगेट करना अधिक मुहावरेदार (idiomatic) है।
पंक्ति 3/4, तीसरा निर्देश: ldxdw r2, [r3+8]
इस निर्देश का उद्देश्य r2 में निर्देश डेटा की लंबाई लोड करना है, जो कि sol_log_ syscall अपने दूसरे आर्गुमेंट के रूप में अपेक्षा करता है।
याद रखें, हमने विशेष रूप से इसी उद्देश्य के लिए पहले निर्देश में बेस पॉइंटर को r3 में सुरक्षित रखा था।
ldxdw निर्देश मेमोरी से 8-बाइट की वैल्यू लोड करता है। निर्देश डेटा की लंबाई का फ़ील्ड बेस पॉइंटर के 8 बाइट बाद स्थित होता है, इसलिए इसका पता 0x400000000 + 8 = 0x400000008 है। इसलिए, ldxdw r2, [r3+8] 0x400000008 पर संग्रहीत 8-बाइट की वैल्यू को r2 में लोड करता है।
हमारे उदाहरण में, यह r2 में वैल्यू 11 (“Hello world” की लंबाई) लोड करता है।
पंक्ति 4/4, चौथा निर्देश: syscall sol_log_
यह sol_log_ syscall को इनवोक करता है और क्रमशः पहले और दूसरे आर्गुमेंट के रूप में इसे r1 और r2 पास करता है।
अब जब हम समझ गए हैं कि प्रोग्राम कैसे काम करता है, तो प्रोग्राम को agave-ledger-tool के साथ चलाएं। परिणाम में “Hello world” मैसेज लॉग होना चाहिए।

यदि आप नेटिव Rust या Anchor में Solana प्रोग्राम लिख रहे हैं, तो आप आमतौर पर सीधे sol_log_ को कॉल करने के बजाय msg! मैक्रो का उपयोग करेंगे। Solana का msg! मैक्रो sol_log_ syscall के चारों ओर एक रैपर है।
macro_rules! msg {
($msg:expr) => {
$crate::sol_log($msg)
};
($($arg:tt)*) => ($crate::sol_log(&format!($($arg)*)));
}
इसके अलावा, आप नीचे दिखाए गए अनुसार इसे सीधे solana_program क्रेट से इम्पोर्ट करके अपने Anchor Rust प्रोग्राम में sol_log syscall को कॉल कर सकते हैं:
use anchor_lang::solana_program::log::sol_log;
sol_log("Hello world");
sol_log_data के साथ बाइनरी डेटा को लॉग करना 2/5
यह syscall sol_log_ syscall के समान है। अंतर केवल इतना है कि यह UTF-8 टेक्स्ट के बजाय base64-एन्कोडेड बाइनरी डेटा को प्रोग्राम लॉग में लॉग करता है।
यहाँ बताया गया है कि sol_log_data syscall के इनवोक होने पर क्या होता है:
- रनटाइम मेमोरी से स्लाइस डिस्क्रिप्टर्स (slice descriptors) का एक एरे पढ़ता है। स्लाइस डिस्क्रिप्टर मेमोरी में एक रिकॉर्ड है जो रनटाइम को बताता है कि बाइट बफ़र कहाँ से शुरू होता है और उसमें कितने बाइट्स होते हैं।
- रनटाइम मेमोरी में बाइट बफ़र का पता लगाने के लिए प्रत्येक डिस्क्रिप्टर में संग्रहीत पॉइंटर का उपयोग करता है,
- फिर base64-एन्कोडेड आउटपुट के रूप में डिस्क्रिप्टर के लंबाई फ़ील्ड द्वारा निर्दिष्ट बाइट्स की संख्या को पढ़ता है और लॉग करता है।
प्रत्येक डिस्क्रिप्टर मेमोरी में 16 बाइट्स के रूप में संग्रहीत होता है: डेटा के लिए एक 8-बाइट का पॉइंटर और 8-बाइट का लंबाई फ़ील्ड।

sol_log_data फ़ंक्शन परिभाषा और रजिस्टर आवश्यकताएँ
यहाँ sol_log_data का सिग्नेचर है:
fn sol_log_data(data: *const u8, data_len: u64)
data पैरामीटर एक *const u8 है जिसे रनटाइम 16-बाइट स्लाइस डिस्क्रिप्टर्स (पॉइंटर, लंबाई) के एरे के लिए पॉइंटर के रूप में मानता है। data_len पैरामीटर निर्दिष्ट करता है कि कितने डिस्क्रिप्टर्स को पढ़ना है।
sol_log_data syscall अपेक्षा करता है:
r1में एक मेमोरी एड्रेस हो जो स्लाइस डिस्क्रिप्टर्स के एरे को इंगित करता होr2में एरे में स्लाइस की संख्या हो
बाइनरी डेटा के रूप में “Hello” को लॉग करना
आइए sol_log_data का उपयोग करके “Hello” स्ट्रिंग को बाइनरी डेटा के रूप में लॉग करने का प्रदर्शन करें।
सबसे पहले, instructions.json में instruction_data को “Hello” के ASCII डेसिमल बाइट प्रतिनिधित्व के साथ अपडेट करें:
{
"accounts": [],
"program_id": "HTpqQdG7f44su3QsV3HHurraR1ZNjHAdArCy3qHKyKBC",
"instruction_data": [72, 101, 108, 108, 111]
}
sol_log_data के साथ “Hello” को लॉग करने के लिए, हमें स्टैक पर एक स्लाइस डिस्क्रिप्टर की आवश्यकता है। हम पहले इनपुट मेमोरी से ‘Hello’ का पॉइंटर और उसकी लंबाई निकालेंगे, डिस्क्रिप्टर बनाने के लिए दोनों वैल्यूज़ को स्टैक पर स्टोर करेंगे, फिर डिस्क्रिप्टर के एड्रेस को r1 में और स्लाइस काउंट को r2 में लोड करेंगे जैसा कि नीचे दिए गए कोड में दिखाया गया है। कोड को syscalls/syscalls.asm फ़ाइल में कॉपी करें, कमेंट्स पर ध्यान दें:
ldxdw r2, [r1+8] ; Load instruction data length from memory
add r1, 16 ; Advance r1 to instruction data start
stxdw [r10-16], r1 ; Store pointer on stack (descriptor field 1)
stxdw [r10-8], r2 ; Store length on stack (descriptor field 2)
mov r1, r10 ; Copy stack pointer to r1
add r1, -16 ; Adjust r1 to descriptor address
mov r2, 1 ; Load immediate value 1 (slice count)
syscall sol_log_data ; invoking the sol_log_data syscall
exit
agave-ledger-tool के साथ प्रोग्राम चलाएं:

आउटपुट SGVsbG8= दिखाता है, जो “Hello” की base64 एन्कोडिंग है। यह दर्शाता है कि sol_log_data ने सफलतापूर्वक हमारे बाइनरी डेटा को लॉग किया है।
यहाँ बताया गया है कि आप Anchor में sol_log_data syscall का उपयोग कैसे कर सकते हैं।
use anchor_lang::solana_program::log::sol_log_data;
let a = b"hello";
let b = b"world";
sol_log_data(&[a, b]);
sol_log_64_ के साथ इंटीजर्स को लॉग करना 3/5
sol_log_64_ syscall प्रोग्राम निष्पादन के दौरान पाँच 64-बिट वैल्यूज़ को लॉग करता है। यह आवश्यक नहीं है कि आप sol_log_64_ syscall को सभी पाँच आर्गुमेंट्स पास करें। यदि आप किसी आर्गुमेंट रजिस्टर में कोई वैल्यू लोड नहीं करते हैं, तो sol_log_64_ syscall उस रजिस्टर में पहले से मौजूद किसी भी वैल्यू को लॉग कर देता है (जो कि 0 या बचा हुआ डेटा हो सकता है)। स्पष्ट होने के लिए, अप्रयुक्त (unused) रजिस्टरों को 0 पर सेट करें। sol_log_64 फ़ंक्शन सिग्नेचर Rust में इस तरह दिखता है:
fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64)
sBPF असेंबली में, हम आर्गुमेंट रजिस्टरों में r1 से r5 तक प्रत्येक आर्गुमेंट को लोड करेंगे। Syscalls केवल आर्गुमेंट रजिस्टरों से अपने आर्गुमेंट्स प्राप्त करते हैं।
ध्यान दें कि sol_log_64_ वैल्यूज़ को हेक्स (hex) में प्रिंट करता है।
यहाँ एक उदाहरण दिया गया है कि हम sol_log_64_ syscall को 5 u64 वैल्यूज़ कैसे पास करेंगे:
mov64 r1, 1
mov64 r2, 2
mov64 r3, 3
mov64 r4, 4
mov64 r5, 5
syscall sol_log_64_
exit
कम वैल्यूज़ को लॉग करने के लिए, स्पष्ट रूप से अप्रयुक्त रजिस्टरों को 0 पर सेट करें:
mov64 r1, 1
mov64 r2, 0
mov64 r3, 0
mov64 r4, 0
mov64 r5, 0
syscall sol_log_64_
exit
मेमोरी से रजिस्टर में सिंगल 64-बिट वैल्यू लोड करने और इसे लॉग करने का प्रदर्शन करने के लिए, हम उदाहरण के रूप में अपनी चल रही instructions.json फ़ाइल के instruction_data फ़ील्ड को 5 के 8-बाइट प्रतिनिधित्व के साथ अपडेट करेंगे:
{
"accounts": [],
"program_id": "HTpqQdG7f44su3QsV3HHurraR1ZNjHAdArCy3qHKyKBC",
"instruction_data": [5, 0, 0, 0, 0, 0, 0, 0]
}
फिर हम मेमोरी से निर्देश डेटा लोड करने के लिए r1 को अकाउंट काउंट (8 बाइट्स) और निर्देश डेटा की लंबाई (8 बाइट्स) से आगे बढ़ा सकते हैं, ठीक वैसे ही जैसे हमने पहले sol_log_ syscall के साथ किया था।
ldxdw r1, [r1 + 16]
syscall sol_log_64_
exit
जब हम इस कोड को चलाएंगे, तो हमें नीचे दिया गया परिणाम मिलेगा:

sol_log_ के विपरीत, Anchor solana_program क्रेट से sol_log_64_ को पुनः-निर्यात (re-export) नहीं करता है।
sol_log_pubkey के साथ पब्लिक कीज़ को लॉग करना 4/5
sol_log_pubkey syscall प्रोग्राम निष्पादन के दौरान एक Solana पब्लिक की को लॉग करता है। यह एक सिंगल आर्गुमेंट लेता है: मेमोरी में 32-बाइट पब्लिक की का पॉइंटर।
अन्य लॉगिंग syscalls के विपरीत, sol_log_pubkey लंबाई का आर्गुमेंट नहीं लेता है। रनटाइम r1 में दिए गए पते से शुरू करके ठीक 32 बाइट्स पढ़ता है और उन बाइट्स की व्याख्या (interpret) एक पब्लिक की के रूप में करता है।
sBPF असेंबली में यह कैसे काम करता है, यह प्रदर्शित करने के लिए, हम अपनी चल रही JSON इनपुट फ़ाइल से प्रोग्राम आईडी को लॉग करेंगे क्योंकि यह एक पब्लिक की है। instructions.json को अपडेट करें ताकि हमारे पास केवल program_id बचा रहे। जब हम प्रोग्राम चलाएंगे तो यह निर्देश इनपुट मेमोरी में लोड हो जाएगा:
{
"accounts": [],
"program_id": "HTpqQdG7f44su3QsV3HHurraR1ZNjHAdArCy3qHKyKBC",
"instruction_data": []
}
फिर नीचे दिए गए कोड को syscalls/syscalls.asm में कॉपी करें:
add64 r1, 16
syscall sol_log_pubkey
exit
जैसा कि हम पहले से ही जानते हैं, प्रोग्राम की प्रविष्टि (entry) पर, r1 इनपुट मेमोरी क्षेत्र की शुरुआत को इंगित करता है। उपरोक्त कोड में, हम इन्हें स्किप करने के लिए r1 में 16 जोड़ते हैं:
- अकाउंट काउंट के लिए 8 बाइट्स
- निर्देश डेटा की लंबाई के लिए 8 बाइट्स (निर्देश डेटा लंबाई फ़ील्ड हमेशा मेमोरी लेआउट में मौजूद होता है, चाहे निर्देश में कोई वास्तविक निर्देश डेटा बाइट्स हों या नहीं, अकाउंट काउंट फ़ील्ड के समान)
चूँकि कोई निर्देश डेटा नहीं है, निर्देश डेटा की लंबाई के बाद मेमोरी लेआउट में अगला आइटम प्रोग्राम आईडी है। r1 में मेमोरी में प्रोग्राम आईडी का पॉइंटर होता है।
प्रोग्राम चलाएं। आपका आउटपुट नीचे दिए गए स्क्रीनशॉट जैसा दिखना चाहिए। प्रोग्राम आईडी पब्लिक की हमारे इनपुट में दिए गए प्रोग्राम पब्लिक की से मेल खानी चाहिए।

sol_log_compute_units_ के साथ कंप्यूट उपयोग को ट्रैक करना 5/5
sol_log_compute_units_ syscall उस सटीक बिंदु पर शेष कंप्यूट यूनिट्स की संख्या को लॉग करता है जहाँ इसे निष्पादित किया जाता है। हमारे द्वारा चर्चा किए गए अन्य syscalls के विपरीत, यह कोई आर्गुमेंट नहीं लेता है। जब sol_log_compute_units_ syscall चलता है, तो रनटाइम वर्तमान कंप्यूट बजट को क्वेरी करता है और शेष बैलेंस को प्रोग्राम लॉग में प्रिंट करता है।
सभी कंप्यूट कॉस्ट रनटाइम द्वारा लागू (enforced) की जाती हैं। कुछ syscalls की लागत एक निश्चित मात्रा में कंप्यूट यूनिट्स होती है (उदाहरण के लिए, sol_log_64 syscall की लागत 100 CU है), जबकि अन्यों की लागत उनके इनपुट्स पर निर्भर करती है, इसका एक उदाहरण sol_log_ syscall है क्योंकि यह चर लंबाई (variable lengths) वाली स्ट्रिंग्स को स्वीकार करता है।
sol_log_compute_units_ syscall एक मापन लॉगिंग टूल है जो विकास के दौरान कंप्यूट कॉस्ट को देखने (observe) में आपकी सहायता करता है।
प्रोग्राम निष्पादन के दौरान उपयोग की गई कंप्यूट यूनिट को मैन्युअल रूप से लॉग करना
किसी विशिष्ट ट्रांज़ैक्शन की कंप्यूट कॉस्ट को लॉग करने के लिए, हमें ट्रांज़ैक्शन में शामिल प्रत्येक opcodes की लागत की गणना करने की आवश्यकता होगी। आइए sol_log_64_ syscall का उदाहरण के रूप में उपयोग करें:
ldxdw r1, [r1 + 16]
syscall sol_log_64_
exit
सबसे पहले हमें प्रत्येक opcodes की लागत निर्धारित करने की आवश्यकता है, भले ही असेंबली में सभी opcodes की कंप्यूट यूनिट प्रलेखित नहीं है, हम उन्हें जल्द ही निर्धारित करने में सक्षम होंगे। syscall, ldxdw, exit opcodes प्रत्येक में 1 कंप्यूट यूनिट की लागत आती है, जबकि sol_log_64 की लागत 100 CU है (defined in this struct)। इसलिए, इस ट्रांज़ैक्शन की कंप्यूट यूनिट कॉस्ट की गणना करने के लिए हम इस लागत को जोड़ेंगे जो 1 + 1 + 1 + 100 = 103 कंप्यूट यूनिट हो जाएगी।
सैकड़ों या हजारों opcodes वाले वास्तविक प्रोग्राम में, मैन्युअल रूप से कुल कंप्यूट कॉस्ट की गणना करना अव्यावहारिक (impractical) होगा। यहीं पर sol_log_compute_units_ काम आता है। हम यह लॉग करने के लिए sol_log_compute_units_ का उपयोग कर सकते हैं कि निष्पादन के बाद कितनी कंप्यूट यूनिट्स बची हैं।
sol_log_compute_units_ के साथ प्रोग्राम निष्पादन के दौरान उपयोग की गई कंप्यूट यूनिट्स को लॉग करना
चूँकि हम जानते हैं कि अधिकतम कंप्यूट यूनिट सीमा 1,400,000 है और sol_log_compute_units_ syscall शेष कंप्यूट यूनिट्स देता है। जब हम sol_log_compute_units_ syscall का उपयोग करके अपना प्रोग्राम चलाते हैं, तो हम अधिकतम कंप्यूट यूनिट्स (1,400,000) से शेष कंप्यूट यूनिट्स को घटा देते हैं। अंतर हमारे प्रोग्राम द्वारा उपभोग (consumed) की गई कंप्यूट यूनिट्स के बराबर होता है।
ध्यान दें कि sol_log_compute_units_ syscall स्वयं 100 कंप्यूट यूनिट्स का उपभोग करता है, इसलिए हमें अपनी गणना में इसका ध्यान रखना चाहिए।
आइए प्रदर्शित करें कि हम असेंबली का उपयोग करके इसे कैसे कर सकते हैं। नीचे दिए गए कोड को अपनी syscalls/syscalls.asm फ़ाइल में कॉपी करें।
ldxdw r1, [r1 + 16]
syscall sol_log_64_
syscall sol_log_compute_units_
exit
जब हम उपरोक्त प्रोग्राम को agave-ledger टूल के साथ चलाएंगे, तो हमें निम्नलिखित परिणाम मिलेगा:

स्क्रीनशॉट से, हम 1,399,797 शेष कंप्यूट यूनिट्स देखते हैं।
कुल उपभोग: 1,400,000 - 1,399,797 = 203 CU
लेकिन इस 203 में sol_log_compute_units_ की स्वयं की लागत (100 CU) और इसके syscall निर्देश (1 CU) शामिल हैं, जिन्हें हमें बाहर करने की आवश्यकता है। इसमें exit निर्देश (1 CU) भी शामिल नहीं है जो लॉग के बाद चलता है।
वास्तविक प्रोग्राम लागत = 203 - 100 (log syscall) - 1 (syscall instruction) + 1 (exit) = 103 CU
यह हमारी पहले की गई मैन्युअल गणना से मेल खाता है।
आप इसे solana_program क्रेट से इम्पोर्ट करके सीधे अपने Solana प्रोग्राम में भी इस syscall का उपयोग कर सकते हैं:
solana_program::log::sol_log_compute_units()
कंप्यूट यूनिट ऑप्टिमाइज़ेशन के बारे में अधिक जानने के लिए, कंप्यूट यूनिट ऑप्टिमाइज़ेशन पर हमारा पिछला ट्यूटोरियल देखें।
यहाँ एक टेबल है जो हमारे द्वारा चर्चा किए गए पाँच लॉगिंग syscalls को सारांशित करती है:
| Syscall | आर्गुमेंट्स (Arguments) | यह क्या लॉग करता है (What It Logs) | कंप्यूट कॉस्ट (Compute Cost) |
|---|---|---|---|
sol_log_ |
r1: मेमोरी में UTF-8 स्ट्रिंग का पॉइंटर, r2: बाइट्स में लंबाई |
पठनीय स्ट्रिंग (readable string) के रूप में UTF-8 टेक्स्ट | वेरिएबल (स्ट्रिंग की लंबाई पर निर्भर करता है) |
sol_log_data |
r1: स्लाइस डिस्क्रिप्टर्स के एरे का पॉइंटर, r2: स्लाइस की संख्या |
base64 एन्कोडिंग के रूप में बाइनरी डेटा | वेरिएबल (डेटा पर निर्भर करता है) |
sol_log_64_ |
r1 से r5: पाँच 64-बिट इंटीजर्स |
हेक्साडेसिमल प्रारूप (hexadecimal format) में पाँच तक इंटीजर्स | 100 CU (निश्चित) |
sol_log_pubkey |
r1: मेमोरी में 32-बाइट पब्लिक की का पॉइंटर |
मानक Solana base58 प्रारूप में पब्लिक की | 100 CU (निश्चित) |
sol_log_compute_units_ |
कोई नहीं | निष्पादन बिंदु पर शेष कंप्यूट यूनिट्स | 100 CU (निश्चित) |
यह लेख Solana डेवलपमेंट पर एक ट्यूटोरियल श्रृंखला का हिस्सा है