Printf () neden gömülü sistemlerde hata ayıklamak için kötü?


16

Mikrodenetleyici tabanlı bir projede hata ayıklamaya çalışmak kötü bir şey printf().

Çıktı için önceden tanımlanmış bir yeriniz olmadığını ve değerli pimleri tüketebileceğini anlayabiliyorum. Aynı zamanda insanların özel bir DEBUG_PRINT()makro ile IDE terminaline çıkış için bir UART TX pin kullandığını gördüm .


12
Sana kötü olduğunu kim söyledi? "Genellikle en iyisi" niteliksiz bir "kötü" ile aynı değildir.
Spehro Pefhany

6
Ne kadar ek yükün olduğu hakkında tüm bu konuşma, yapmanız gereken tek şey "Ben buradayım" mesajlarını çıkarmaksa, printf'ye ihtiyacınız yok, sadece bir UART'a dize göndermek için bir rutin. Bu, artı UART'ı başlatmak için kullanılan kod muhtemelen 100 baytlık kodun altındadır. Birkaç onaltılık değer çıktısı eklemek, onu çok fazla artırmaz.
tcrosley

7
@ChetanBhargava - C başlık dosyaları genellikle yürütülebilir dosyaya kod eklemez. Beyanlar içerirler; kodun geri kalanı bildirilen şeyleri kullanmazsa, bu şeylerin kodu bağlanmaz. printfElbette, uygulamak için gereken tüm kod printfçalıştırılabilir dosyaya bağlanır. Ancak bunun nedeni, kodun üstbilgiden değil, onu kullanmasıdır.
Pete Becker

2
@ChetanBhargava Açıkladığım gibi bir dize çıkarmak için kendi basit rutininizi döndürürseniz <stdio.h> eklemenize bile gerek yoktur ('\ 0' görünceye kadar karakterleri
UART'a gönderin

2
@tcrosley Ben iyi bir modern derleyici varsa, bu durumda muhtemelen bir biçim dizesi gcc olmadan basit durumda printf kullanıyorsanız ve diğerlerinin çoğu açıkladığınız kadar çok daha verimli bir basit çağrı ile değiştirirseniz, bu tavsiyenin muhtemelen tartışmalı olduğunu düşünüyorum.
Vality

Yanıtlar:


24

Printf () kullanmanın birkaç dezavantajı ile karşılaşabilirim. "Gömülü sistem" in birkaç yüz bayt program hafızasına sahip bir şeyden, gigabayt RAM ve terabayt kalıcı belleğe sahip tam gelişmiş rafa monte edilmiş QNX RTOS tabanlı bir sisteme kadar değişebileceğini unutmayın.

  • Verileri göndermek için bir yer gerektirir. Belki sisteminizde zaten bir hata ayıklama veya programlama portunuz var, belki de yok. Eğer (ya da sahip olduğunuz) çalışmıyorsa çok kullanışlı değildir.

  • Tüm bağlamlarda hafif bir işlev değildir. Yalnızca birkaç K belleğe sahip bir mikro denetleyiciniz varsa bu büyük bir sorun olabilir, çünkü printf'de bağlantı tek başına 4K'yı yiyebilir. 32K veya 256K mikro denetleyiciniz varsa, büyük bir gömülü sisteminiz varsa, büyük olasılıkla bir sorun değildir.

  • Bellek ayırma veya kesmelerle ilgili belirli türde sorunları bulmak için çok az veya hiç kullanışsızdır ve ifadeler dahil edildiğinde veya dahil edilmediğinde programın davranışını değiştirebilir.

  • Zamanlamaya duyarlı şeylerle uğraşmak oldukça işe yaramaz. Bir mantık analizörü ve bir osiloskop veya bir protokol analizörü, hatta bir simülatörle daha iyi olur.

  • Büyük bir programınız varsa ve printf ifadelerini değiştirip değiştirdiğinizde birçok kez yeniden derlemeniz gerekiyorsa çok zaman kaybedebilirsiniz.

İyi olan- her C programcısının sıfır öğrenme eğrisini nasıl kullanacağını bilmesi için verileri önceden biçimlendirilmiş bir şekilde çıkarmanın hızlı bir yoludur. Hata ayıkladığınız Kalman filtresi için bir matris tükürmeniz gerekiyorsa, MATLAB'ın okuyabileceği bir formatta tükürmek güzel olabilir. Kesinlikle bir hata ayıklayıcı veya emülatörde RAM konumlarına tek tek bakmaktan daha iyi olabilir .

Titremede işe yaramaz bir ok olduğunu düşünmüyorum, ancak gdb veya diğer hata ayıklayıcılar, emülatörler, mantık analizörleri, osiloskoplar, statik kod analiz araçları, kod kapsama araçları vb.


3
Çoğu printf()uygulama, bir anlaşma katili olmayan, iş parçacığı için güvenli değildir (yani, yeniden girmeyen), ancak çok iş parçacıklı bir ortamda kullanırken akılda tutulması gereken bir şeydir.
JRobert

1
@JRobert iyi bir noktaya geliyor .. ve işletim sistemi olmayan bir ortamda bile, ISR'lerin doğrudan hata ayıklamasını yapmak çok zor. Elbette bir ISR'de printf () veya kayan nokta matematiği yapıyorsanız yaklaşım muhtemelen kapalıdır.
Spehro Pefhany

@JRobert Çok iş parçacıklı bir ortamda (Mantık Analizörleri ve osiloskopların kullanımının pratik olmadığı bir donanım ortamında) çalışan yazılım geliştiriciler hangi hata ayıklama araçlarına sahiptir?
Minh Tran

1
Geçmişte kendi iş parçacığı için güvenli printf (); bir terminale çok özlü veri vermek için yalınayak koyar () veya putchar () eşdeğerlerini kullanır; test çalıştırdıktan sonra dökümü ve yorumladığım bir dizi saklanan ikili veri; bir LED'i yanıp sönmek veya bir osiloskopla zamanlama ölçümleri yapmak üzere darbeler üretmek için bir G / Ç bağlantı noktası kullanıldı; D / A'ya bir sayı verin ve VOM ile ölçün ... Liste hayal gücünüz kadar uzun ve bütçeniz kadar ters büyük! :)
JRobert

19

Diğer bazı iyi yanıtlara ek olarak, seri baud hızlarında bir bağlantı noktasına veri gönderme eylemi, döngü sürenize göre düpedüz yavaş olabilir ve programınızın geri kalanının çalışması üzerinde bir etkisi olabilir (HERHANGİ bir hata ayıklama gibi) işlemi).

Diğer milletlerin size söylediği gibi, bu tekniği kullanmakla ilgili "kötü" hiçbir şey yoktur, ancak diğer birçok hata ayıklama tekniği gibi sınırlamaları vardır. Bildiğiniz ve bu sınırlamalarla başa çıkabildiğiniz sürece, kodunuzu doğru şekilde almanıza yardımcı olmak için son derece uygun bir ağaçlandırma olabilir.

Gömülü sistemler, genel olarak hata ayıklamayı biraz sorun haline getiren belirli bir opaklığa sahiptir.


8
"Gömülü sistemler belirli bir opaklığa sahiptir" için +1. Bu ifadenin yalnızca gömülü çalışma konusunda iyi deneyime sahip olanlar tarafından anlaşılabilir olmasına rağmen, durumun güzel ve özlü bir özetini yapar. Aslında "gömülü" tanımına yakındır.
njahnke

5

printfBir mikro denetleyicide kullanmaya çalışacağınız iki ana sorun vardır .

İlk olarak, çıkışı doğru porta borulamak bir ağrı olabilir. Her zaman değil. Ancak bazı platformlar diğerlerinden daha zordur. Yapılandırma dosyalarının bazıları yetersiz bir şekilde belgelenebilir ve çok fazla deneme yapılması gerekebilir.

İkincisi hafızadır. Tam bir printfkütüphane BÜYÜK olabilir. Bazen tüm format belirleyicilerine ihtiyaç duymazsınız ve özel sürümler mevcut olabilir. Örneğin, stdio.h AVR tarafından sağlananprintf , farklı boyutlarda ve işlevsellikte üç farklı içerir .

Bahsedilen tüm özelliklerin tam olarak uygulanması oldukça büyük hale geldiğinden, vfprintf()bağlayıcı seçenekleri kullanılarak üç farklı tat seçilebilir. Varsayılan vfprintf(), kayan nokta dönüşümleri hariç, belirtilen tüm işlevleri uygular. vfprintf()Yalnızca çok temel tamsayı ve dize dönüştürme olanaklarını uygulayan küçültülmüş bir sürümü mevcuttur, ancak #dönüştürme bayrakları kullanılarak yalnızca ek seçenek belirtilebilir (bu bayraklar biçim belirtiminden doğru şekilde ayrıştırılır, ancak basitçe yok sayılır).

Kütüphanenin olmadığı bir örneğim vardı ve çok az hafızam vardı. Bu yüzden özel bir makro kullanmaktan başka seçeneğim yoktu. Ancak kullanımı printfya da olmaması gerçekten gereksinimlerinize uyacak şeylerden biridir.


Downvoter, gelecek projelerdeki hatamdan kaçınabilmek için cevabımdaki neyin yanlış olduğunu açıklayabilir mi?
embedded.kyle

4

Spehro Pefhany'nin "zamanlamaya duyarlı şeyler" hakkında söylediklerine eklemek için: bir örnek verelim. Diyelim ki, gömülü sisteminizin saniyede 1.000 ölçüm aldığı bir jiroskopunuz var. Bu ölçümlerde hata ayıklamak istiyorsunuz, bu yüzden bunları yazdırmanız gerekiyor. Sorun: Bunları yazdırmak sistemin saniyede 1.000 ölçümü okumak için çok meşgul olmasına neden olur, bu da jiroskop tamponunun taşmasına neden olur, bu da bozuk verilerin okunmasına (ve yazdırılmasına) neden olur. Ve böylece, verileri yazdırarak, verileri bozmuş olursunuz, gerçekte olmadığında verileri okumada bir hata olduğunu düşündürürsünüz. Bir sözde heisenbug.


lol! "Heisenbug" gerçekten teknik bir terim midir? Sanırım parçacık devletinin ölçümü ve Heisenburg Prensibi ile ilgisi var ...
Zeta.Investigator

3

Printf () ile hata ayıklamamasının en büyük nedeni, genellikle verimsiz, yetersiz ve gereksiz olmasıdır.

Verimsiz: printf () ve kin, küçük bir mikro denetleyicide mevcut olana göre çok fazla flash ve RAM kullanır, ancak daha büyük verimsizlik gerçek hata ayıklamadadır. Günlüğe kaydedilen öğeyi değiştirmek, hedefin yeniden derlenmesini ve yeniden programlanmasını gerektirir, bu da işlemi yavaşlatır. Ayrıca, yararlı işler yapmak için kullanabileceğiniz bir UART kullanır.

Yetersiz: Seri bağlantı üzerinden verebileceğiniz çok fazla ayrıntı var. Program askıda kalırsa, tam olarak nerede olduğunu bilmiyorsunuz, sadece son çıktı çıktı.

Gereksiz: Birçok mikrodenetleyici uzaktan hata ayıklanabilir. JTAG veya tescilli protokoller, işlemciyi duraklatmak, kayıtlara ve RAM'e bakmak ve hatta derlemek zorunda kalmadan çalışan işlemcinin durumunu değiştirmek için kullanılabilir. Bu nedenle, hata ayıklayıcılar, tonlarca alan ve güç içeren bir bilgisayarda bile, genellikle yazdırma ifadelerinden daha iyi bir hata ayıklama yöntemidir.

Yeni başlayanlar için en yaygın mikrodenetleyici platformu Arduino'nun hata ayıklayıcı olmaması talihsiz bir durum. AVR uzaktan hata ayıklamayı destekler, ancak Atmel'in debugWIRE protokolü tescilli ve belgesizdir. GDB ile hata ayıklamak için resmi bir geliştirme panosu kullanabilirsiniz, ancak eğer sahipseniz, artık Arduino için çok fazla endişelenmiyorsanız.


Günlüğe kaydedilenlerle oynamak ve bir grup esneklik eklemek için işlev işaretçileri kullanamaz mısınız?
Scott Seidman

3

printf () tek başına çalışmaz. Diğer birçok işlevi çağırır ve çok az yığın alanınız varsa, yığın sınırınıza yakın sorunları ayıklamak için hiç kullanamayabilirsiniz. Derleyiciye ve mikrodenetleyiciye bağlı olarak, biçim dizgisi flaştan ziyade belleğe de yerleştirilebilir. Kodunuzu printf ifadeleriyle sıkıştırırsanız bu önemli ölçüde toplanabilir. Bu, Arduino ortamında büyük bir sorundur - düzinelerce veya yüzlerce printf ifadesi kullanan yeni başlayanlar, yığınlarıyla yığınlarının üzerine yazdıkları için aniden rastgele görünen problemlerle karşılaşırlar.


2
Downvote'un verdiği geribildirimi takdir etsem de, katılmayanlar bu cevapla ilgili sorunları açıklarsa bana ve diğerlerine daha yararlı olacaktır. Hepimiz bilgi öğrenmek ve paylaşmak için buradayız, sizinkileri paylaşmayı düşünün.
Adam Davis

3

Birisi günlük kayıt konsoluna bir şekilde tükürmek istese bile, printfişlev genellikle bunu yapmak için çok iyi bir yol değildir, çünkü geçirilen format dizesini incelemek ve çalışma zamanında ayrıştırmak gerekir; kod asla başka bir biçim belirteci kullanmasa bile %04X, denetleyicinin genellikle rasgele biçim dizelerini ayrıştırmak için gereken tüm kodu içermesi gerekir. Birinin kullandığı tam denetleyiciye bağlı olarak, aşağıdaki gibi bir kod kullanmak çok daha verimli olabilir:

void log_string(const char *st)
{
  int ch;
  do
  {
    ch = *st++;
    if (ch==0) break;
    log_char(ch);
  } while(1);
}
void log_hexdigit(unsigned char d)
{
  d&=15;
  if (d > 9) d+=7;
  log_char(d+'0');
}
void log_hexbyte(unsigned char b)
{ log_hexdigit(b >> 4); log_hexdigit(b); }
void log_hexi16(uint16_t s)
{ log_hexbyte(s >> 8); log_hexbyte(s); }
void log_hexi32(uint32_t i)
{ log_hexbyte(i >> 24); log_hexbyte(i >> 16); log_hexbyte(i >> 8); log_hexbyte(i); }
void log_hexi32p(uint32_t *p) // On a platform where pointers are less than 32 bits
{ log_hexi32(*p); }

Bazı PIC mikrodenetleyicilerinde, log_hexi32(l)muhtemelen 9 talimat alır ve 17 ( likinci bankadaysa) alırken log_hexi32p(&l)2 alır. log_hexi32pFonksiyonun kendisi yaklaşık 14 talimat uzunluğunda yazılabilir, bu yüzden iki kez çağrılırsa kendisi için ödeme yapar .


2

Diğer cevaplardan hiçbirinin bahsetmediği bir nokta: Temel bir mikroda (IE, sadece ana () döngü ve belki de herhangi bir anda çalışan, çok iş parçacıklı bir işletim sistemi değil, birkaç ISR vardır) bir döngüye sıkışmışsa, yazdırma işleviniz gerçekleşmez .

Ayrıca, insanlar "printf kullanmayın" veya "stdio.h çok yer kaplıyor" dediler ama çok fazla alternatif vermediler - embedded.kyle basitleştirilmiş alternatiflerden bahsediyor ve bu muhtemelen olması gereken bir şey tabii ki temel bir gömülü sistem üzerinde yapmak. UART'tan birkaç karakter fışkırtmak için temel bir rutin, birkaç bayt kod olabilir.


Printf'iniz gerçekleşmezse, kodunuzun nerede sorunlu olduğu hakkında çok şey öğrendiniz.
Scott Seidman

Olabilecek tek bir printf varsayarsak, evet. Ancak kesintiler, UART'tan bir şey almak için bir printf () çağrısı alırken yüzlerce kez ateş edebilir
John U
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.