
Kaynak Kodun İzlenmesi, Hata Ayıklanması ve Yapısal Analizi
Tanıtım
Bu makale, yürütme sırasında bir çağrı yığını oluşturma yöntemlerinden birini anlatır. Aşağıdaki özellikler makalede açıklanmıştır:
- Kullanılan sınıfların, fonksiyonların ve dosyaların yapısının yapılması.
- Çağrı yığınını önceki tüm yığınları koruyarak yapmak. Onları çağırma sırası.
- Yürütme sırasında İzleme parametrelerinin durumunu görüntüleme.
- Kodun adım adım yürütülmesi.
- Elde edilen yığınları gruplama ve sıralama, "aşırı" bilgi alma.
Gelişimin Temel İlkeleri
Yapının temsili yöntemi olarak ortak bir yaklaşım seçilmiştir – bir ağaç şeklinde görüntüleme. Bunun için iki bilgi sınıfına ihtiyacımız var. CNode - bir yığınla ilgili tüm bilgileri yazmak için kullanılan bir "düğüm". CtreeCtrl - tüm düğümleri işleyen bir "ağaç". Ve izleyicinin kendisi - ağaçları işlemek için kullanılan CTraceCtrl.
CNodeBase ve CTreeBase sınıfları, düğümler ve ağaçlarla çalışmanın temel özelliklerini ve yöntemlerini açıklar.
Devralınan CNode sınıfı, CNodeBase'in temel işlevselliğini genişletir ve CTreeBase sınıfı, türetilmiş CNode sınıfıyla çalışır. CNodeBase sınıfının diğer standart düğümlerin ebeveyni olması nedeniyle yapılır ve hiyerarşi ve kalıtım kolaylığı için bağımsız bir sınıf olarak izole edilir.
Standart kitaplıktaki CTreeNode'dan farklı olarak, CNodeBase sınıfı düğümlere yönelik bir dizi işaretçi içerir, dolayısıyla bu düğümden çıkan "dalların" sayısı sınırsızdır.
CNodeBase ve CNode Sınıfları
class CNode; // forward declaration //------------------------------------------------------------------ class CNodeBase class CNodeBase { public: CNode *m_next[]; // list of nodes it points to CNode *m_prev; // parent node int m_id; // unique number string m_text; // text public: CNodeBase() { m_id=0; m_text=""; } // constructor ~CNodeBase(); // destructor }; //------------------------------------------------------------------ class CNode class CNode : public CNodeBase { public: bool m_expand; // expanded bool m_check; // marked with a dot bool m_select; // highlighted //--- run-time information int m_uses; // number of calls of the node long m_tick; // time spent in the node long m_tick0; // time of entering the node datetime m_last; // time of entering the node tagWatch m_watch[]; // list of name/value parameters bool m_break; // debug-pause //--- parameters of the call string m_file; // file name int m_line; // number of row in the file string m_class; // class name string m_func; // function name string m_prop; // add. information public: CNode(); // constructor ~CNode(); // destructor void AddWatch(string watch,string val); };
Tüm sınıfların uygulamasını ekteki dosyalarda bulabilirsiniz. Makalede sadece başlıklarını ve önemli işlevlerini göstereceğiz.
Kabul edilen sınıflandırmaya göre, CTreeBase döngüsel olmayan grafiği temsil eder ve yönlendirir. Türetilmiş sınıf CTreeCtrl, CNode'u kullanır ve tüm işlevselliğini yerine getirir: CNode düğümlerini ekleme, değiştirme ve silme.
CTreeCtrl ve CNode, biraz daha geniş bir işlevselliğe sahip olduklarından, standart kitaplığın karşılık gelen sınıflarını başarıyla değiştirebilir.
CTreeBase ve CTreeCtrl Sınıfları
//------------------------------------------------------------------ class CTreeBase class CTreeBase { public: CNode *m_root; // first node of the tree int m_maxid; // counter of ID //--- base functions public: CTreeBase(); // constructor ~CTreeBase(); // destructor void Clear(CNode *root=NULL); // deletion of all nodes after a specified one CNode *FindNode(int id,CNode *root=NULL); // search of a node by its ID starting from a specified node CNode *FindNode(string txt,CNode *root=NULL); // search of a node by txt starting from a specified node int GetID(string txt,CNode *root=NULL); // getting ID for a specified Text, the search starts from a specified node int GetMaxID(CNode *root=NULL); // getting maximal ID in the tree int AddNode(int id,string text,CNode *root=NULL); // adding a node to the list, search is performed by ID starting from a specified node int AddNode(string txt,string text,CNode *root=NULL); // adding a node to the list, search is performed by text starting from a specified node int AddNode(CNode *root,string text); // adding a node under root }; //------------------------------------------------------------------ class CTreeCtrl class CTreeCtrl : public CTreeBase { //--- base functions public: CTreeCtrl() { m_root.m_file="__base__"; m_root.m_line=0; m_root.m_func="__base__"; m_root.m_class="__base__"; } // constructor ~CTreeCtrl() { delete m_root; m_maxid=0; } // destructor void Reset(CNode *root=NULL); // reset the state of all nodes void SetDataBy(int mode,int id,string text,CNode *root=NULL); // changing text for a specified ID, search is started from a specified node string GetDataBy(int mode,int id,CNode *root=NULL); // getting text for a specified ID, search is started from a specified node //--- processing state public: bool IsExpand(int id,CNode *root=NULL); // getting the m_expand property for a specified ID, search is started from a specified node bool ExpandIt(int id,bool state,CNode *root=NULL); // change the m_expand state, search is started from a specified node void ExpandBy(int mode,CNode *node,bool state,CNode *root=NULL); // expand node of a specified node bool IsCheck(int id,CNode *root=NULL); // getting the m_check property for a specified ID, search is started from a specified node bool CheckIt(int id,bool state,CNode *root=NULL); // change the m_check state to a required one starting from a specified node void CheckBy(int mode,CNode *node,bool state,CNode *root=NULL); // mark the whole tree bool IsSelect(int id,CNode *root=NULL); // getting the m_select property for a specified ID, search is started from a specified node bool SelectIt(int id,bool state,CNode *root=NULL); // change the m_select state to a required one starting from a specified node void SelectBy(int mode,CNode *node,bool state,CNode *root=NULL); // highlight the whole tree bool IsBreak(int id,CNode *root=NULL); // getting the m_break property for a specified ID, search is started from a specified node bool BreakIt(int id,bool state,CNode *root=NULL); // change the m_break state, search is started from a specified node void BreakBy(int mode,CNode *node,bool state,CNode *root=NULL); // set only for a selected one //--- operations with nodes public: void SortBy(int mode,bool ascend,CNode *root=NULL); // sorting by a property void GroupBy(int mode,CTreeCtrl *atree,CNode *node=NULL); // grouping by a property };
Mimari iki sınıfla sona erer: CTraceCtrl - tek örneği doğrudan izleme için kullanılır, gerekli işlev yapısının oluşturulması için CTreeCtrl sınıfının üç örneğini içerir; ve geçici bir kapsayıcı - CIn sınıfı. Bu sadece CTraceCtrl'ye yeni düğümler eklemek için kullanılan bir yardımcı sınıftır.
CTraceCtrl ve CIn Sınıfları
class CTraceView; // provisional declaration //------------------------------------------------------------------ class CTraceCtrl class CTraceCtrl { public: CTreeCtrl *m_stack; // object of graph CTreeCtrl *m_info; // object of graph CTreeCtrl *m_file; // grouping by files CTreeCtrl *m_class; // grouping by classes CTraceView *m_traceview; // pointer to displaying of class CNode *m_cur; // pointer to the current node CTraceCtrl() { Create(); Reset(); } // tracer created ~CTraceCtrl() { delete m_stack; delete m_info; delete m_file; delete m_class; } // tracer deleted void Create(); // tracer created void In(string afile,int aline,string aname,int aid); // entering a specified node void Out(int aid); // exit from a specified node bool StepBack(); // exit from a node one step higher (going to the parent) void Reset() { m_cur=m_stack.m_root; m_stack.Reset(); m_file.Reset(); m_class.Reset(); } // resetting all nodes void Clear() { m_cur=m_stack.m_root; m_stack.Clear(); m_file.Clear(); m_class.Clear(); } // resetting all nodes public: void AddWatch(string name,string val); // checking the debug mode for a node void Break(); // pause for a node }; //------------------------------------------------------------------ CIn class CIn { public: void In(string afile,int aline,string afunc) { if(NIL(m_trace)) return; // exit if there is no graph if(NIL(m_trace.m_tree)) return; if(NIL(m_trace.m_tree.m_root)) return; if(NIL(m_trace.m_cur)) m_trace.m_cur=m_trace.m_tree.m_root; m_trace.In(afile,aline,afunc,-1); // entering the next one } void ~CIn() { if(!NIL(m_trace)) m_trace.Out(-1); } // exiting higher };
CIn Sınıfının Çalışma Modeli
Bu sınıf, yığın ağacının oluşturulmasından sorumludur.
Grafiğin oluşturulması, iki CTraceCtrl işlevi kullanılarak iki aşamada adım adım gerçekleştirilir:
void In(string afile, int aline, string aname, int aid); // entering a specified node void Out(int aid); // exit before a specified node
Başka bir deyişle, bir ağaç oluşturmak için sürekli Giriş-Çıkış-Giriş-Çıkış-Giriş-Giriş-Çıkış-Çıkış vb. çağrıları yapılır.
Giriş-Çıkış çifti şu şekilde çalışır:
1. Bir blok (fonksiyon, döngü, koşul, vb.) girme, yani "{" parantezinden hemen sonra.
Bloğu girerken, yeni bir CIn örneği oluşturulur, önceki bazı düğümlerle zaten başlatılmış olan mevcut CTraceCtrl'yi alır. CTraceCtrl::Giriş işlevi CIn'de çağrılır, yığında yeni bir düğüm oluşturur. Düğüm, geçerli CTraceCtrl::m_cur düğümü altında oluşturulur. Girmeyle ilgili tüm gerçek bilgiler içinde yazılıdır: dosya adı, satır numarası, sınıf adı, işlevler, geçerli saat vb.
2. Bir "}" ayracı ile karşılaşıldığında bloktan çıkılıyor.
Bloktan çıkarken, MQL otomatik olarak yıkıcı CIn::~CIn'i çağırır. Yıkıcıda CTraceCtrl::Çıkış çağrılır. Mevcut CTraceCtrl::-m_cur düğümünün işaretçisi ağaçta bir seviye yukarıya yükseltilir. Yıkıcı yeni düğüm için çağrılmaz, düğüm ağaçta kalır.
Yığın Oluşturma Şeması
Bir çağrı ile ilgili tüm bilgilerin doldurularak bir ağaç şeklinde çağrı yığınının oluşturulması CIn konteyneri kullanılarak gerçekleştirilir.
Aramaları Kolaylaştıran Makrolar
CIn nesnesini oluştururken ve kodunuza bir düğüm girerken uzun kod satırlarını yeniden yazmaktan kaçınmak için makronun çağrısı ile değiştirmek uygundur:#define _IN CIn _in; _in.In(__FILE__, __LINE__, __FUNCTION__)
Gördüğünüz gibi CIn nesnesi oluşturuluyor ve ardından düğüme giriyoruz.
MQL, yerel değişkenlerin adlarının global değişkenlerle aynı olması durumunda bir uyarı verdiği için aşağıdaki biçimde diğer değişken adlarıyla 3-4 benzer tanım oluşturmak daha iyidir (daha doğru ve net):
#define _IN1 CIn _in1; _in1.In(__FILE__, __LINE__, __FUNCTION__) #define _IN2 CIn _in2; _in2.In(__FILE__, __LINE__, __FUNCTION__) #define _IN3 CIn _in3; _in3.In(__FILE__, __LINE__, __FUNCTION__)Alt bloklarda daha derine indikçe, sonraki makroları _INx kullanın
bool CSampleExpert::InitCheckParameters(int digits_adjust) { _IN; //--- initial data checks if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel()) { _IN1; printf("Take Profit must be greater than %d",m_symbol.StopsLevel());
411 derlemesinde makroların görünmesiyle, #define kullanarak parametrelerin geçişini tam olarak kullanabilirsiniz.
Bu nedenle CTraceCtrl sınıfında aşağıdaki makro tanımını bulacaksınız:
#define NIL(p) (CheckPointer(p)==POINTER_INVALID)
İşaretçinin geçerlilik kontrolünün kısaltılmasına izin verir.
Örneğin, satır:
if (CheckPointer(m_tree))==POINTER_INVALID || CheckPointer(m_cur))==POINTER_INVALID) return;
daha kısa varyantla değiştirilir:
if (NIL(m_tree) || NIL(m_cur)) return;
Dosyalarınızı İzleme İçin Hazırlama
Yığını kontrol etmek ve almak için üç adım atmanız gerekir.
1. Gerekli dosyaları ekleyin#include <Trace.mqh>
Tüm standart kitaplık şu anda CObject sınıfına dayanmaktadır. Bu nedenle, dosyalarınızda temel sınıf olarak da kullanılıyorsa, Trace.mqh'yi yalnızca Object.mqh'ye eklemeniz yeterlidir.
2. _IN makrolarını gerekli bloklara yerleştirin (arama/değiştirme özelliğini kullanabilirsiniz)
_IN makrosunu kullanma örneği:bool CSampleExpert::InitCheckParameters(int digits_adjust) { _IN; //--- initial data checks if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel()) { _IN1; printf("Take Profit must be greater than %d",m_symbol.StopsLevel());
3. Programın ana modülünü oluşturan OnInit, OnTime ve OnDeinit fonksiyonlarında sırasıyla CTraceCtrl global nesnesinin oluşturulmasını, değiştirilmesini ve silinmesini ekleyin. Eklemek için hazır kodu aşağıda bulabilirsiniz:
İzleyiciyi ana koda gömmek
//------------------------------------------------------------------ OnInit int OnInit() { //**************** m_traceview= new CTraceView; // created displaying of the graph m_trace= new CTraceCtrl; // created the graph m_traceview.m_trace=m_trace; // attached the graph m_trace.m_traceview=m_traceview; // attached displaying of the graph m_traceview.Create(ChartID()); // created chart //**************** // remaining part of your code… return(0); } //------------------------------------------------------------------ OnDeinit void OnDeinit(const int reason) { //**************** delete m_traceview; delete m_trace; //**************** // remaining part of your code… } //------------------------------------------------------------------ OnTimer void OnTimer() { //**************** if (m_traceview.IsOpenView(m_traceview.m_chart)) m_traceview.OnTimer(); else { m_traceview.Deinit(); m_traceview.Create(ChartID()); } // if the window is accidentally closed //**************** // remaining part of your code… } //------------------------------------------------------------------ OnChartEvent void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) { //**************** m_traceview.OnChartEvent(id, lparam, dparam, sparam); //**************** // remaining part of your code… }
İz Görüntüleme Sınıfları
Böylece yığın düzenlendi. Şimdi elde edilen bilgilerin görüntülenmesini ele alalım.
Bunun için iki sınıf oluşturmalıyız. CTreeView – ağacın görüntülenmesi için ve CTraceView – ağaçların görüntülenmesinin kontrolü ve yığın hakkında ek bilgiler için. Her iki sınıf da CView temel sınıfından türetilmiştir.
CTreeView ve CTraceView Sınıfları
//------------------------------------------------------------------ class CTreeView class CTreeView: public CView { //--- basic functions public: CTreeView(); // constructor ~CTreeView(); // destructor void Attach(CTreeCtrl *atree); // attached the tree object for displaying it void Create(long chart,string name,int wnd,color clr,color bgclr,color selclr, int x,int y,int dx,int dy,int corn=0,int fontsize=8,string font="Arial"); //--- functions of processing of state public: CTreeCtrl *m_tree; // pointer to the tree object to be displayed int m_sid; // last selected object (for highlighting) int OnClick(string name); // processing the event of clicking on an object //--- functions of displaying public: int m_ndx, m_ndy; // size of margins from button for drawing int m_bdx, m_bdy; // size of button of nodes CScrollView m_scroll; bool m_bProperty; // show properties near the node void Draw(); // refresh the view void DrawTree(CNode *first,int xpos,int &ypos,int &up,int &dn); // redraw void DeleteView(CNode *root=NULL,bool delparent=true); // delete all displayed elements starting from a specified node }; //------------------------------------------------------------------ class CTreeView class CTraceView: public CView { //--- base functions public: CTraceView() { }; // constructor ~CTraceView() { Deinit(); } // destructor void Deinit(); // full deinitialization of representation void Create(long chart); // create and activate the representation //--- function of processing of state public: int m_hagent; // handler of the indicator-agent for sending messages CTraceCtrl *m_trace; // pointer to created tracer CTreeView *m_viewstack; // tree for displaying the stack CTreeView *m_viewinfo; // tree for displaying of node properties CTreeView *m_viewfile; // tree for displaying of the stack with grouping by files CTreeView *m_viewclass; // tree for displaying of stack with grouping by classes void OnTimer(); // handler of timer void OnChartEvent(const int,const long&,const double&,const string&); // handler of event //--- functions of displaying public: void Draw(); // refresh objects void DeleteView(); // delete the view void UpdateInfoTree(CNode *node,bool bclear); // displaying the window of detailed information about a node string TimeSeparate(long time); // special function for transformation of time into string };
En uygun değişken olarak yığını ayrı bir alt pencerede görüntülemeyi seçtik.
Başka bir deyişle, CTraceView::Create işlevinde CTraceView sınıfı oluşturulduğunda, CTraceView başka bir pencerede Uzman Danışman’da oluşturulup çalışmasına rağmen, grafik penceresi oluşturulur ve tüm nesneler bunun içine çizilir. Takip edilen programın kaynak kodunun çalışmasını ve kendi bilgilerinin büyük miktarda bilgi ile çizelge üzerinde görüntülenmesini engellemek için yapılır.
Ancak iki pencere arasındaki etkileşimi mümkün kılmak için, kullanıcının tüm olaylarını izlenen programla temel pencereye gönderecek olan pencereye bir gösterge eklememiz gerekiyor.
Gösterge, aynı CTraceView::Create işlevinde oluşturulur. Tüm olayları göndermesi gereken grafiğin kimliği olan yalnızca bir harici parametreye sahiptir.
TraceAgent Göstergesi
#property indicator_chart_window input long cid=0; // чарт получателя //------------------------------------------------------------------ OnCalculate int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double& price[]) { return(rates_total); } //------------------------------------------------------------------ OnChartEvent void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam) { EventChartCustom(cid, (ushort)id, lparam, dparam, sparam); }
Sonuç olarak, yığının oldukça yapılandırılmış bir temsiline sahibiz.
Solda görüntülenen TRACE ağacında ilk yığın görüntülenir.
Altında, seçilen düğüm hakkında ayrıntılı bilgi içeren INFO penceresi vardır (bu örnekte CTraceView::OnChartEvent). Ağaçları içeren iki bitişik pencere aynı yığını görüntüler, ancak sınıflara (ortadaki SINIF ağacı) ve dosyalara (sağdaki DOSYA ağacı) göre gruplandırılmıştır.
Sınıfların ve dosyaların ağaçları, uygun kontrol araçlarının yanı sıra yığının ana ağacıyla gömülü senkronizasyon mekanizmasına sahiptir. Örneğin, sınıf ağacında bir sınıf adına tıkladığınızda, bu sınıfın tüm işlevleri yığın ağacında ve dosya ağacında seçilir. Aynı şekilde bir dosya adına tıkladığınızda o dosyadaki tüm fonksiyonlar ve sınıflar seçilir.
Yığınla Çalışmanın Özellikleri
- İzleme Parametreleri Ekleme
Daha önce fark ettiğiniz gibi, CNode düğümünün parametreleri tagWatch yapı dizisini içerir. Sadece bilginin temsili kolaylığı için yaratılmıştır. Bir değişkenin veya ifadenin adlandırılmış değerini içerir.
İzleme Değerinin Yapısı
//------------------------------------------------------------------ struct tagWatch struct tagWatch { string m_name; // name string m_val; // value };
Geçerli düğüme yeni bir İzleme değeri eklemek için CTrace::AddWatch işlevini çağırmanız ve _WATCH makrosunu kullanmanız gerekir.
#define _WATCH(w, v) if (!NIL(m_trace) && !NIL(m_trace.m_cur)) m_trace.m_cur.AddWatch(w, string(v));
Eklenen değerler üzerindeki özel sınırlama (düğümlerle aynı) adların benzersizliğinin kontrolüdür. Bu, CNode::m_watch[] dizesine eklenmeden önce bir Watch değerinin adının benzersiz olup olmadığının kontrol edildiği anlamına gelir. Dize aynı ada sahip bir değer içeriyorsa, yenisi eklenmez, ancak mevcut olanın değeri güncellenir.
İzlenen tüm İzleme değerleri bilgi penceresinde görüntülenir.
- Kodun adım adım yürütülmesi.
MQL5 tarafından sağlanan bir başka kullanışlı özellik, yürütme sırasında kodda zorunlu bir kesintinin düzenlenmesidir.
Duraklatma, basit bir sonsuz döngü while (true) kullanılarak gerçekleştirilir. MQL5'in buradaki rahatlığı, bu döngüden çıkış olayının kontrol eden kırmızı düğmeye tıklanarak gerçekleştirilmesidir. Yürütme sırasında bir kesme noktası oluşturmak için CTrace::Break işlevini kullanın.
Kırılma Noktalarını Uygulama Fonksiyonu
//------------------------------------------------------------------ Break void CTraceCtrl::Break() // checking the debug mode of a node { if(NIL(m_traceview)) return; // check of validity m_stack.BreakBy(TG_ALL,NULL,false); // removed the m_break flags from all nodes m_cur.m_break=true; // activated only at the current one m_traceview.m_viewstack.m_sid=m_cur.m_id; // moved selection to it m_stack.ExpandBy(TG_UP,m_cur,true,m_cur); // expand parent node if they are closed m_traceview.Draw(); // drew everything string name=m_traceview.m_viewstack.m_name+string(m_cur.m_id)+".dbg"; // got name of the BREAK button bool state=ObjectGetInteger(m_traceview.m_chart,name,OBJPROP_STATE); while(!state) // button is not pressed, execute the loop { Sleep(1000); // made a pause state=ObjectGetInteger(m_traceview.m_chart,name,OBJPROP_STATE); // check its state if(!m_traceview.IsOpenView()) break; // if the window is closed, exit m_traceview.Draw(); // drew possible changes } m_cur.m_break=false; // removed the flag m_traceview.Draw(); // drew the update }
Böyle bir kırılma noktasıyla karşılaşıldığında, yığın ağaçları bu makroyu çağıran işlevi görüntülemek için senkronize edilir. Bir düğüm kapalıysa, üst düğüm onu gösterecek şekilde genişletilir. Ve gerekirse, düğümü görünür alana getirmek için ağaç yukarı veya aşağı kaydırılır.
CTraceCtrl::Break'ten çıkmak için, düğüm adının yanında bulunan kırmızı düğmeye tıklayın.
Sonuç
Şimdi ilginç bir "oyuncağımız" oldu. Makaleyi yazarken, CTraceCtrl ile çalışmanın birçok çeşidini denedim ve MQL5'in Uzman Danışmanları kontrol etme ve operasyonlarını organize etme konusunda benzersiz bakış açılarına sahip olduğundan emin oldum. İzleyiciyi geliştirmek için kullanılan tüm özellikler MQL4'te mevcut değildir, bu da MQL5'in avantajlarını ve geniş olanaklarını bir kez daha kanıtlamaktadır.
Ekli kodda, makalede açıklanan tüm sınıfları hizmet kitaplıkları ile birlikte bulabilirsiniz (amaç olmadığı için gereken minimum set). Ayrıca, _IN makrolarının yerleştirildiği standart kitaplığın hazır örnek - güncellenmiş dosyalarını ekledim. Tüm deneyler, MetaTrader 5 - MACD Sample.mq5'in standart teslimatına dahil olan Expert Advisor ile gerçekleştirilmiştir.
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/272





- Ücretsiz alım-satım uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz