-
Notifications
You must be signed in to change notification settings - Fork 0
/
2008-01.html
1384 lines (1095 loc) · 71.9 KB
/
2008-01.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en-us" dir="ltr" itemscope itemtype="http://schema.org/Article">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Blogue do Caloni</title>
<meta name="author" content="Caloni" />
<meta name="generator" content="txt2blog 0.0.1">
<meta property="og:title" content="Blogue do Caloni"/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="http://www.caloni.com.br"/>
<meta property="og:image" content="/img/about-brand.png"/>
<meta property="og:description" content="Write for computers, people and food."/>
<link href="/index.xml" rel="feed" type="application/rss+xml" title="Blogue do Caloni"/>
<link rel="stylesheet" type="text/css" href="/css/custom.css"/>
<link rel="stylesheet" type="text/css" href="/css/jquery-ui.css"/>
<script src="/js/jquery-1.12.4.js"></script>
<script src="/js/jquery-ui.js"></script>
<script src="/js/copy_clipboard.js"></script>
<script>
var quick_search_posts = [
];
</script>
<script src="/js/quick_search.js"></script>
<script src="/js/list.js"></script>
<link rel="icon" href="/img/favicon.ico"/>
</head>
<body style="min-height:100vh;display:flex;flex-direction:column">
<nav class="navbar has-shadow is-white"
role="navigation" aria-label="main navigation">
<div class="container">
<div class="navbar-brand">
<a class="navbar-item" href="months.html">
<div class="is-4"><b>caloni::2008-01</b></div>
</a>
</div>
</div>
</nav>
<div class="container">
<div class="column">
<div style="min-height:56vh">
<div style="padding-bottom: 1em;"></div>
<ul style="list-style: none;">
<li><small><a href="2008-01.html#como_ser_um_melhor_desenvolvedor_em_2008">Como ser um melhor desenvolvedor em 2008</a></small></li>
<li><small><a href="2008-01.html#cppcon_iii">CppCon III</a></small></li>
<li><small><a href="2008-01.html#como_ter_um_melhor_blogue_em_2008">Como ter um melhor blogue em 2008</a></small></li>
<li><small><a href="2008-01.html#analisando_dumps_com_windbg_e_ida">Analisando Dumps com WinDbg e IDA</a></small></li>
<li><small><a href="2008-01.html#temas_no_windbg">Temas no WinDbg</a></small></li>
<li><small><a href="2008-01.html#encontrando_as_respostas_do_flash_pops">Encontrando as respostas do Flash Pops</a></small></li>
<li><small><a href="2008-01.html#otimizacao_em_funcoes_recursivas">Otimização em funções recursivas</a></small></li>
<li><small><a href="2008-01.html#terceiro_encontro_c">Terceiro encontro C++</a></small></li>
<li><small><a href="2008-01.html#keychanger_de_crianca">Keychanger de criança</a></small></li>
<li><small><a href="2008-01.html#rmthread_rode_codigo_em_processo_vizinho">RmThread: rode código em processo vizinho</a></small></li>
<li><small><a href="2008-01.html#compartilhando_variaveis_com_o_mundo">Compartilhando variáveis com o mundo</a></small></li>
</ul>
<span id="como_ser_um_melhor_desenvolvedor_em_2008" title="Como ser um melhor desenvolvedor em 2008"/></span>
<section id="section_como_ser_um_melhor_desenvolvedor_em_2008">
<p class="title"><a href="2008-01.html#como_ser_um_melhor_desenvolvedor_em_2008">#</a> Como ser um melhor desenvolvedor em 2008</p>
<span class="title-heading">Caloni, 2008-01-02<a href="2008-01.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_como_ser_um_melhor_desenvolvedor_em_2008')"><sup>[copy]</sup></a></span>
<p>Aproveitando que está se aproximando meu prazo final para minhas resoluções de seis meses atrás, e o <a href="http://dqsoft.blogspot.com/search?q=resolu%C3%A7%C3%B5es+de+ano+novo">DQ</a> já fez o checklist dele, vou dar uma espiada na minha lista de desejos atual e fazer uma nova lista para 2008.</p>
<h4>1. Fazer um curso de memorização</h4>
<p>Comecei, fiz vários exercícios, mas ainda não acabei todas as aulas. Descobri que a memória pode ser muito mais bem treinada do que realmente é, e existem técnicas bem pensadas que fazem isso sem muito mais esforço do que apenas tempo despendido. De fato todos nós já temos uma memória incrível, só precisamos treiná-la adequadamente.</p>
<p>Como comecei e ainda não parei considero esta tarefa realizada (começar e ganhar ritmo é o mais difícil) e estendido para 2008 inteiro.</p>
<h4>2. Fazer um curso de leitura dinâmica</h4>
<p>Comecei, fiz quase todos os exercícios e terminei. De fato melhorou em muito minha capacidade de concentração na hora de ler um texto rápido, embora eu ainda fique com muito sono quando faço isso. O importante agora é nunca deixar de treinar, e melhorar cada vez mais o poder dos movimentos oculares.</p>
<h4>3. Aprender o meu ritmo</h4>
<p>Não existe milagre, mas existem coisas que podemos fazer para ajudá-lo a acontecer. Foi isso que aprendi durante minhas inúmeras tentativas de dominar o tempo e o espaço no desenvolvimento de software. Aprendi muita coisa, inclusive que escritórios não foram criados para serem lugares produtivos, e quase sempre é necessário se defender dos riscos que a internet faz para a saúde.</p>
<p>Enfim, essa tarefa também terminou. Agora é só manutenção constante e disciplinada.</p>
<p>Por fim, considero se achar melhor depois de ter melhorado um ou dois itens da vida profissional uma "escrutinisse", tão inútil quanto achar-se já um desenvolvedor muito bom. Porque a qualquer hora podemos cometer novamente aquelas besteiras que fazíamos há cinco anos, e a qualquer hora podemos ter idéias brilhantes. O importante, na minha opinião, é aprender exatamente por que erramos e por que acertamos. Aprender exatamente, e lembrar-se disso, pode ser um enorme catalisador de anos de depuração aleatória.</p>
<blockquote>"Cada problema que eu resolvo se torna uma regra que serve mais tarde para resolver outros problemas." - Rene Descartes</blockquote>
<h4>4. Lista inusitada de tarefas</h4>
<p>Sem estar na lista previamente concebida comecei a fazer outras coisas de maneira mais eficiente, relacionado ao trabalho ou não:</p>
<ul><li>Aprender o leiaute do teclado Dvorak. Treino todo dia cinco minutos há três meses a digitação usando esse leiaute, porque é mais simples, mais rápido e dói menos os dedos.</li>
<li>Anotar todas as coisas importantes. Seja uma idéia nova, uma idéia sobre uma idéia, ou até mesmo melhoramentos em algum software que dou manutenção, é importante manter tudo anotado, porque sabe-se lá quando isso vai ser usado. Mas, quando for, quem vai se lembrar?</li>
<li>Bloguear constantemente. Apesar dos sacrifícios que isso às vezes causa, é edificante nunca "deixar a bola cair". Minha regra é sempre publicar um artigo dia sim, dia não durante a semana. Em uma semana começo na segunda, em outra na terça, e assim sucessivamente. Tem funcionado desde que reiniciei o blogue há seis meses, e espero que continue assim.</li>
<li>Usar novo controle de versão em casa. Há um mês mais ou menos conheci o Mercurial, que é um sistema de controle de versão muito leve e não-centralizado, duas coisas que fazem uns sininhos soarem em minha cabeça. Ele é baseado conjunto de modificações e merge, duas coisas a que não estou acostumado e me forcei a aprender.</li>
</ul>
<p>Não é muito difícil definir essa lista, pois ela na verdade são as mesmas duas listas que citei anteriormente. Comecei a fazer essas coisas seis meses atrás. Para um fumante de fato parar, uns cinco anos de abstinência é um bom indicador. Acredito que, para um hábito se enraizar, um ano e meio pode ser de bom tamanho.</p>
</section><hr/>
<span id="cppcon_iii" title="CppCon III"/></span>
<section id="section_cppcon_iii">
<p class="title"><a href="2008-01.html#cppcon_iii">#</a> CppCon III</p>
<span class="title-heading">Caloni, 2008-01-04 <a href="ccppbr.html">ccppbr</a><a href="2008-01.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_cppcon_iii')"><sup>[copy]</sup></a></span>
<p>O ano de 2008 promete. Pelo menos no começo.</p>
<p>Está marcado para dia 19 desse mês em São Paulo o terceiro encontro de programadores C++, cujas informações mais atualizadas você poderá acompanhar em nossa wiki. A grade de eventos, pelo menos por enquanto, é essa:</p>
<p> * 09:30 a 10:00 - Introdução e Apresentação dos Membros do Encontro</p>
<p> * 10:00 a 11:00 - C++ com WxWidgets por Ivo Nascimento</p>
<p> * 11:00 a 11:30 - Debate</p>
<p> * 11:30 a 11:45 - Coffee break</p>
<p> * 11:45 a 12:45 - C++0x - Novas características de suporte a projetos de bibliotecas genéricas por Pedro Lamarão</p>
<p> * 12:45 a 13:15 - Debate</p>
<p> * 13:15 a 14:30 - Almoço</p>
<p> * 14:30 a 15:30 - Threads no CPP ISO - Wanderley Caloni</p>
<p> * 15:30 a 16:00 - Debate</p>
<p> * 16:00 a 16:15 - Coffee break</p>
<p> * 16:1 a 17:00 - Fórum sobre a Organização do Grupo de Usuários e da C/C++ Conference Brasil</p>
<p> * 17:30 a 00:00 - C/C++ Beer Meeting!</p>
<p>Conto com a participação de todos que se interessam, usam ou aprendem sobre essas fabulosas linguagens de programação. Vamos levantar a moral de C++ no cenário brasileiro!</p>
<p>Errata: na verdade o que ocorreu dia 19 foi um encontro de C++ com direito a palestras e coffee break, o que de certa forma invalida o nome CppCon. Futuramente teremos o que poderemos chamar de conferência C++, no sentido amplo do termo. Te espero lá.</p>
</section><hr/>
<span id="como_ter_um_melhor_blogue_em_2008" title="Como ter um melhor blogue em 2008"/></span>
<section id="section_como_ter_um_melhor_blogue_em_2008">
<p class="title"><a href="2008-01.html#como_ter_um_melhor_blogue_em_2008">#</a> Como ter um melhor blogue em 2008</p>
<span class="title-heading">Caloni, 2008-01-08<a href="2008-01.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_como_ter_um_melhor_blogue_em_2008')"><sup>[copy]</sup></a></span>
<p>Não é exatamente uma receita de bolo, tampouco uma lista de regras imutáveis. Na verdade, apenas algumas dicas que o criador do termo (we)blog deu sobre como ele imagina que os blogueiros deveriam se comportar em relação aos seus blogues. Entre os toques, ele inicialmente comenta que o princípio de um weblog é ser um histórico dos sítios que navegamos, e que eventualmente podemos publicar conteúdo original. Bem, esse humilde blogue faz exatamente o oposto, acreditando que o conteúdo publicado aqui em português dificilmente será encontrado na web, além de que me sinto um inútil se não colaborar com o mundo usando o conhecimento que aprendi e aprendo no dia-a-dia.</p>
<p>Por isso mesmo, aqui vão as dicas traduzidas, que encontrei no blogue de Lino Resende, verbatim (com meus comentários ao final de cada item):</p>
<p> 1. Um blog verdadeiro é um log de todos os sítios que você gostaria de salvar ou dividir.</p>
<ul><li>Então, hoje, o del.icio.us é melhor para os bloggers do que o próprio Blogger. Isso seria como se os blogues fossem sítios de pesquisadores do google, o que não deixa de ser meia-verdade.</li>
</ul>
<p> 2. Você pode, é claro, colocar links sobre você fora do seu blog, mas se o blog tem mais posts originais do que links, recomendo aprender um pouco de humildade.</p>
<ul><li>É um golpe bem dado ao Caloni.com.br. Bom, espero ser mais humilde em 2008 =).</li>
</ul>
<p> 3. Se fizer uma pequena procura antes de postar, vai descobrir que alguém já falou do seu assunto e melhor do que você.</p>
<ul><li>Isso eu faço, mas, como já disse, conteúdo em português é mais escasso, o que compensa a publicação de artigos sobre assuntos já tratados em outras línguas.</li>
</ul>
<p> 4. Seja você mesmo, sem suprimir links que não o tratem favoravelmente. Seus leitores querem saber quem efetivamente você é.</p>
<ul><li>Essa dica é particularmente difícil para blogues técnicos, como o meu e de muita gente. No entanto, nós tentamos não parecer bots, acredite!</li>
</ul>
<p> 5. Você pode melhorar o título das páginas que sugere quando as descrever e dar o link. Assegure-se de sua descrição fará os leitores se lembrarem dela, reconhecendo páginas que já visitaram ou quando a visitarem novamente.</p>
<ul><li>Essa é mais fácil de fazer.</li>
</ul>
<p> 6. Use sempre algum adjetivo para descrever sua própria reação à página que recomenda (ótima, imaginativa, clara, útil).</p>
<ul><li>Essa dica foi clara e útil. Além de imaginativa e ótima, claro.</li>
</ul>
<p> 7. Dê os créditos à fonte que você usou. Assim, seus leitores podem conferi-la e "moverem-se para cima".</p>
<ul><li>Essencial, especialmente, mais uma vez, se tratando de blogues técnicos.</li>
</ul>
<p> 8. Cuidado com os problemas de formatação estranha, múltiplas páginas com histórias, textos muito longos, etc. Não esconda o link principal entre outros auxiliares, mal identificados ou pobres.</p>
<ul><li>Essa dica é mais para weblogs de fato. Eu passo.</li>
</ul>
<p> 9. Escolha alguns autores favoritos ou celebridades e crie um feed no Google News, acompanhando novas menções a eles. Assim, outros fãs podem segui-los através do seu blog.</p>
<ul><li>Você pode seguir os que sigo através da minha Home Page (update 2021-04-18: hoje em dia não sigo mais nada, mas links específicos existem nos posts; quem diria, acabei ficando mais blogger com o passar do tempo). Eventualmente compartilho posts através do Google Reader. Update 2021-04-18: não mais =(</li>
</ul>
<p> 10. Reindique seus links favoritos de tempos em tempos para quem os perdeu, esquece ou o está lendo pela primeira vez.</p>
<ul><li>Essa é uma coisa que está faltando aqui no Caloni.com.br, que é a manutenção dos artigos antigos. Prometo me esforçar mais em 2008. Promessa de ano-novo =).</li>
</ul>
<p>É isso. Concorda, discorda, sem corda? Imagino que a dica que mais me afetou foi aquela sobre humildade, lá no começo. Digo isso porque ainda está martelando na minha cabeça, pronta para transformar este blogue em algo mais democrático e transparente.</p>
</section><hr/>
<span id="analisando_dumps_com_windbg_e_ida" title="Analisando Dumps com WinDbg e IDA"/></span>
<section id="section_analisando_dumps_com_windbg_e_ida">
<p class="title"><a href="2008-01.html#analisando_dumps_com_windbg_e_ida">#</a> Analisando Dumps com WinDbg e IDA</p>
<span class="title-heading">Caloni, 2008-01-10 <a href="coding.html">coding</a><a href="2008-01.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_analisando_dumps_com_windbg_e_ida')"><sup>[copy]</sup></a></span>
<p>Apesar de ser recomendado que 100% dos componentes de um software esteja configurado corretamente para gerar símbolos na versão release, possibilitando assim a visualização do nome das funções internas através de um arquivo de dump (despejo) gerado na ocorrência de um crash, essa verdade só ocorre em 80% das vezes. Quis Murphy que dessa vez a única parte não "simbolizada" fosse a que gerou a tela azul em um Intel Quad Core que estou analisando esses dias.</p>
<p>Para incluir um programa novo em nosso leque de opções, vamos usar dessa vez uma ferramenta chamada IDA, um disassembler estático cujo nome é uma clara homenagem à nossa primeira programadora da história. E, é lógico, o WinDbg não poderá ficar de fora, já que ele será nosso analisador de dumps.</p>
<p>Tecnicamente falando, um dump nada mais é do que o conjunto de informações relevantes de um sistema em um determinado momento da execução, geralmente logo após um crash, onde tudo pára e morre. No caso do Windows, o crash é chamado de BSOD, Blue Screen Of Death, ou Tela Azul da Morte (bem macabro, não?). Do ponto de vista do usuário, é aquela simpática tela azul que aparece logo após o travamento da máquina.</p>
<p>Em algumas máquinas, essa tela nem mais é vista, pois o Windows XP é configurado automaticamente para exibir um simpático reboot que joga todos os seus dados não-salvos naquele momento para o limbo (ou, como diria meu amigo Thiago, para o "céu dos dados não-salvos antes de uma tela azul").</p>
<p>Dumps podem ser abertos por um depurador que entenda o tipo de dump gerado (Visual Studio, WinDbg, OllyDbg, IDA, sd, etc). Se estamos falando de aplicativos que travaram, o Visual Studio pode dar conta do recado. Se é realmente uma tela azul, o WinDbg é o mais indicado. Para abrir um dump no WinDbg, tudo que temos que fazer é usar o item do menu "File, Open Crash Dump" ou digitar direto da linha de comando: windbg -z meu-crash-dump-do-coracao.dmp.</p>
<p>Após alguns segundos, o WinDbg irá imprimir uma saída cheia de detalhes parecendo o terminal de um filme de raquerismo. Não se preocupe, com o tempo cada detalhe fará mais sentido (ou não). Geralmente a melhor idéia depois de abrir o dump é seguir o conselho do próprio WinDbg e usar o comando !analyze -v, e mais um monte de informações será plotada na tela. Se o arquivo aberto for um minidump ele irá conter apenas a pilha de chamada que causou a tela azul, o estados dos registradores e algumas informações sobre módulos carregados no kernel. A partir daí podemos extrair algumas informações úteis:</p>
<ul><li>O código do Bug Check. Esse é talvez o mais importante, pois pode resolver rapidamente o nosso problema. Procurando na ajuda do WinDbg pelo código do erro (obs: execute o link pelo explorer) conseguimos ter algumas dicas de como evitar esse erro: "The MAXIMUMWAITOBJECTSEXCEEDED bug check has a value of 0x0000000C. This indicates that the current thread exceeded the permitted number of wait objects". Mais sobre isso pra depois.</li>
<li>Os dados da pilha. Pela pilha de chamadas, podemos não apenas saber se nosso driver está no meio com cara de culpado, como, através dos _offsets_, descobrir em que função ele se enfiou para dar no que deu.</li>
<li>A última chamada do kernel antes do nosso driver pode indicar-nos que evento foi o responsável por iniciar todo o processo de cabum. Nesse caso, IopLoadDriver nos dá uma ótima dica: foi na hora de carregar o nosso driver.</li>
</ul>
<p>Com isso em mãos, mesmo sem símbolos e nomes de funções no código, conseguiríamos achar o código responsável pelo BSOD. Porém, vamos imaginar por um momento que não foi tão fácil assim e fazer entrar em cena outra ferramenta indispensável nessas horas: o Interactive Disassembler.</p>
<p>No site do IDA podemos encontrar o download para uma versão gratuita do IDA, isso se usado com objetivos não-comerciais. Ou seja, para você que está lendo esse blogue por aprendizado, não terá nenhum problema você baixar essa versão e fazer alguns testes com seu driver favorito.</p>
<p>O funcionamento básico do IDA é bem básico, mesmo. Simplesmente escolhemos um executável para ele destrinchar e nos mostrar um assembly bem amigável, com todos os nomes de funções que ele puder deduzir. Como não temos os símbolos do próprio executável, as funções internas ganham "apelidos", como sub6669, loc13F35 e por aí vai. Isso não importa, já que temos nomes amigáveis de APIs para pesquisar no código-fonte e tentar encontrar as funções originais em C.</p>
<p>Pois bem. Como manda o figurino, o primeiro ponto do assembly que temos que procurar é o ponto em que uma função interna é chamada logo após IopLoadDriver, mydriver+0x4058. Por coincidência (ou não, já que essa é a função do IopLoadDriver), se trata da função inicial do executável, ou seja, provavelmente a função DriverEntry no código-fonte (obs: estamos analisando um driver feito para plataforma NT).</p>
<p>No dump que analisei o ponto de retorno é logo após uma chamada à função sub113F0, que não sei qual é. No entanto, o que eu sei é que logo no início é chamada a função IoIsWdmVersionAvailable, o que já nos permite fazer uma correlação com o código-fonte original. Após a chamada à IoIsWdmVersionAvailable, a próxima e última chamada de uma função é o que procuramos. Dessa forma, podemos ir caminhando até o ponto onde o driver chama o sistema operacional.</p>
<pre>
mydriver+offset:
call ds:KeInitializeDpc
mov edx, dword_13000
...
push 1
call sub_11BE0
push eax
call sub_117D0.text:000114D7
add esi, 0D9Ch
push esi
...
mydriver+offset:
push 0
...
push ebx
push edi
call ds:KeWaitForMultipleObjects
mov eax, [esp+30h+var_14]
mov edi, ds:ExFreePoolWithTag
...
mov ecx, [esp+20h]
push 0
</pre>
<p>Por sorte o caminho não foi tão longo e cheguei rapidamente no ponto onde é chamada a função KeWaitForMultipleObject que, de acordo com o WinDbg e com a OSR, pode gerar uma tela azul se esperarmos por mais de três objetos e não especificarmos um buffer no parâmetro WaitBlockArray. Agora podemos olhar no fonte e ver por quantos objetos esperamos e tirar nossa própria conclusão do que está acontecendo.</p>
<pre>
//...
count = 0; // processors
mask = KeQueryActiveProcessors();
maskAux = mask;
while( maskAux )
{
if( maskAux & 1 )
count++;
maskAux >>= 1;
}
//...
KeWaitForMultipleObjects(count,
waitObjects,
WaitAll,
UserRequest,
KernelMode,
TRUE,
NULL,
NULL);
ExFreePool(...);
ExFreePool(...);
ExFreePool(...);
//...
</pre>
<p>Ora, ora. O número de processadores influencia no número de objetos que estaremos esperando na função de espera. Esse seria um bom motivo para gerar um MAXIMUMWAITOBJECTSEXCEEDED em máquinas onde existe mais de 3 processadores ativos, não? Talvez seja uma boa hora para atualizar esse código e torná-lo compatível com os novos Quad Core.</p>
<p>É importante, durantes os testes de desenvolvimento, sempre manter em dia uma versão debug (para o mundo kernel mode, versões checked) para que os primeiros problemas, geralmente os mais bestinhas, sejam pegos de forma rápida e eficiente. No entanto, um bom desenvolvedor não se limita a depurar com código-fonte. Ele deve estar sempre preparado para enfrentar problemas de falta da versão certa, informação pela metade, situação não-reproduzível. Para isso que servem as ferramentas maravilhosas que podemos usar no dia-a-dia. O IDA é mais uma das que deve estar sempre no cinto de utilidades do bom "debugador".</p>
</section><hr/>
<span id="temas_no_windbg" title="Temas no WinDbg"/></span>
<section id="section_temas_no_windbg">
<p class="title"><a href="2008-01.html#temas_no_windbg">#</a> Temas no WinDbg</p>
<span class="title-heading">Caloni, 2008-01-14 <a href="coding.html">coding</a><a href="2008-01.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_temas_no_windbg')"><sup>[copy]</sup></a></span>
<p>Desde a versão 6.4.7.2 que o WinDbg fornece uma subpasta chamada Themes, onde lá estão diversos workspaces configurados. Existe até um passo-a-passo de como organizar esses temas e escolher o seu favorito. Segue algumas dicas de como transformar corretamente sua área de trabalho para depuração (e mantê-la).</p>
<p>O WinDbg salva suas configurações no registro. Para apagar os valores previamente gravados, rode o seguinte comando:</p>
<pre>
reg delete HKCU\Software\Microsoft\WinDbg
</pre>
<p>Você pode gravar um tema, rodar o WinDbg (sem parâmetros), ver se gosta do que viu, e tentar novamente. Quando estiver satisfeito com a aparência, fique com ela e comece o próximo passo.</p>
<p>Nas depurações do dia-a-dia algumas configurações devem estar sempre muito bem configuradas, para que torne seus momentos de desespero porque nada está funcionando mais agradáveis. Por isso, assim que escolher seu tema preferido trate de configurar os seguintes itens:</p>
<ul><li>Diretórios de símbolos. Você pode começar com .symfix, que vai montar uma string padrão, e adicionar mais diretórios com .sympath+.</li>
<li>Diretórios de código-fonte. Coloque a raiz dos seus projetos principais. Com o tempo, se você mexe muito nos seus diretórios, é necessário fazer uma manutenção desse valor.</li>
<li>Diretórios de executáveis. Basicamente é o mesmo do diretório de símbolos.</li>
</ul>
<p>Depois de configurar tudo isso, ajuste as janelas na melhor maneira e proporção que achar mais agradável. Esse será o último passo, pois depois você irá fechar o WinDbg e salvar o workspace, que a partir daí será o padrão sempre que abrir o depurador.</p>
<p>Para que os arquivos fonte caiam no lugar que você escolheu, durante a configuração, abra um código-fonte e coloque no lugar que gostaria de ver todos os fontes listados, junto com um placeholder (um arquivo C usado como localizador, existem 5 dentro da pasta themes). Após isso, feche o código-fonte, mas mantenha o placeholder. Depois é só fechar o WinDbg salvando as configurações. Tudo deve funcionar como previsto (ou você esqueceu alguma coisa).</p>
<p>Como esses passos deram algum trabalho, trate de salvar as configurações, caso tenha que usá-las em outras máquinas ou restaurá-las caso algo de ruim aconteça com seu SO (como quando você depura seus drivers na mesma máquina em que desenvolve, por exemplo).</p>
<pre>
reg save HKCU\Software\Microsoft\WinDbg c:\Tools\DbgTools\Themes\MyTheme.reg
</pre>
<p>Leia a documentação do WinDbg sobre temas (dentro de Themes, Themes.doc). Foi de lá que eu fiz a tradução e adaptação dos passos mais importantes. E esqueça do Visual Studio =)</p>
</section><hr/>
<span id="encontrando_as_respostas_do_flash_pops" title="Encontrando as respostas do Flash Pops"/></span>
<section id="section_encontrando_as_respostas_do_flash_pops">
<p class="title"><a href="2008-01.html#encontrando_as_respostas_do_flash_pops">#</a> Encontrando as respostas do Flash Pops</p>
<span class="title-heading">Caloni, 2008-01-16 <a href="coding.html">coding</a><a href="2008-01.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_encontrando_as_respostas_do_flash_pops')"><sup>[copy]</sup></a></span>
<p>Existia uma série de jogos no sítio da UOL chamado Flash Pops onde você deve acertar o nome de filmes, programas de televisão, entre outros, que vão da década de 40 até a atualidade. É divertido e viciante fazer pesquisa na internet para encontrar os resultados, ainda mais quando já se é viciado em cinema. Ficamos jogando, eu e minha namorada, por semanas a fio. Quase chegamos a preencher tudo, e por um bom tempo ficamos travados para terminar. Então começamos a apelar para o Google e o IMDB até os limites do razoável. Nesse fim de semana, por exemplo, chegamos a assistir um filme de madrugada onde tocou rapidamente um trecho de uma das músicas que faltava no jogo sobre televisão. No dia seguinte procuramos a trilha sonora do filme, ouvimos faixa a faixa e procuramos o nome da música no Google, para finalmente encontrar o resultado.</p>
<p>Essa foi a última resposta "honesta". Depois resolvi apelar para o WinDbg =)</p>
<p>A primeira coisa que pensei a respeito desse jogo foi que ele não seria tão ingênuo a ponto de colocar as respostas em texto aberto, do contrário, qual seria a graça, certo? Errado! Bom, no final das contas, um passo-a-passo bem simples me levou a encontrar a lista de respostas.</p>
<p>A primeira coisa a fazer é carregar o jogo na memória do navegador. Em seguida, seguindo meu raciocínio inicial, digitei a primeira resposta do jogo.</p>
<img src="img/encontrando_as_respostas_do_flash_pops_flash_pops_jogo.png"/>
<p>A partir daí, podemos "atachar" o WinDbg no processo do navegador e rastrear a memória do processo.</p>
<pre>
windbg -pn firefox.exe
</pre>
<img src="img/encontrando_as_respostas_do_flash_pops_gpfnow.gif"/>
<p>Então, como eu dizia, não faça isso em casa enquanto estiver digitando um artigo de seu blogue dentro do navegador. Ele vai travar!</p>
<pre>
windbg %programfiles%\Mozilla Firefox\firefox.exe
</pre>
<p>OK. A primeira coisa é procurar pela string digitada, na esperança de achar a estrutura que escreve as respostas de acordo com a digitação. Isso pode ser feito facilmente graças ao WinDbg e ao <a href="http://voneinem-windbg.blogspot.com/2007/06/scan-full-process-memory-for-pattern.html">artigo de Volker von Einem</a> que ensina como procurar strings por toda a memória de um processo (mais tarde iremos também usar o comando-bônus do comentário de Roberto Farah).</p>
<pre>
0:017> s -a 0 0fffffff "caca fantasmas"
0575f458 63 61 63 61 20 66 61 6e-74 61 73 6d 61 73 00 63 caca fantasmas.c
057fb950 63 61 63 61 20 66 61 6e-74 61 73 6d 61 73 00 00 caca fantasmas..
</pre>
<p>Interessante. Dois resultados. Olhando o primeiro deles, vemos que encontramos o que queríamos sem nem mesmo tentar quebrar alguma chave de criptografia.</p>
<pre>
0:017> db 0575f458
0575f458 63 61 63 61 20 66 61 6e-74 61 73 6d 61 73 00 63 caca fantasmas.c
0575f468 61 63 61 2d 66 61 6e 74-61 73 6d 61 73 00 63 61 aca-fantasmas.ca
0575f478 c3 a7 61 20 66 61 6e 74-61 73 6d 61 73 00 63 61 ..a fantasmas.ca
0575f488 c3 a7 61 2d 66 61 6e 74-61 73 6d 61 73 00 67 68 ..a-fantasmas.gh
0575f498 6f 73 74 62 75 73 74 65-72 73 00 41 72 72 61 79 ostbusters.Array
0575f4a8 00 6d 75 73 31 00 6a 61-6d 65 73 20 62 6f 6e 64 .mus1.james bond
0575f4b8 00 30 30 37 00 6d 75 73-32 00 6d 69 73 73 69 6f .007.mus2.missio
0575f4c8 6e 20 69 6d 70 6f 73 73-69 62 6c 65 00 6d 69 73 n impossible.mis
</pre>
<p>O segundo, porém, não parece uma lista de respostas, mas sim a resposta que acabamos de digitar no navegador.</p>
<pre>
0:017> db 057fb950
057fb950 63 61 63 61 20 66 61 6e-74 61 73 6d 61 73 00 00 caca fantasmas..
057fb960 5f 6c 65 76 65 6c 30 2f-6d 75 73 36 32 3a 6d 00 _level0/mus62:m.
057fb970 00 00 00 00 24 44 82 05-20 40 82 05 32 3a 6d 00 ....$D.. @..2:m.
057fb980 00 00 00 00 6c 49 82 05-68 45 82 05 00 00 00 00 ....lI..hE......
057fb990 00 00 00 00 b4 4e 82 05-b0 4a 82 05 00 00 00 00 .....N...J......
057fb9a0 00 00 00 00 24 74 85 05-20 70 85 05 00 00 00 00 ....$t.. p......
057fb9b0 00 00 00 00 6c 79 85 05-68 75 85 05 00 00 00 00 ....ly..hu......
057fb9c0 70 6f 72 63 65 6e 74 6f-00 63 65 72 74 61 73 00 porcento.certas.
</pre>
<p>Para se certificar, rodamos novamente o navegador, apagamos a resposta e refazemos a busca.</p>
<pre>
0:017> g
(864.dc0): Break instruction exception - code 80000003 (first chance)
eax=7ffda000 ebx=00000001 ecx=00000002 edx=00000003 esi=00000004 edi=00000005
eip=7c901230 esp=03c3ffcc ebp=03c3fff4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246
ntdll!DbgBreakPoint:
7c901230 cc int 3
0:017> s -a 0 0fffffff "caca fantasmas"
0575f458 63 61 63 61 20 66 61 6e-74 61 73 6d 61 73 00 63 caca fantasmas.c
</pre>
<p>De fato, a lista de respostas é tudo que encontramos.</p>
<p>Assim como no <a href="2007-11.html#carregando_dlls_arbitrarias_pelo_windbg_parte_2">artigo sobre carregamento de DLLs arbitrárias</a>, vamos usar o muito útil comando .foreach, que caminha em uma lista de resultados de um comando para executar uma lista secundária de comandos. Apenas para relembrar, a sintaxe do foreach é a seguinte:</p>
<pre>
.foreach [Options] ( Variable { InCommands } ) { OutCommands }
</pre>
<p> * Variable. Um nome que usamos no OutCommands. Representa cada token do resultado de InCommands.</p>
<p> * InCommands. Um ou mais comandos que executamos para gerar uma saída na tela. Essa saída será usada em OutCommands, onde Variable é substituído por cada token da saída.</p>
<p> * OutCommands. Um ou mais comandos executados usando a saída na tela de InCommands.</p>
<p>Para o .foreach, um token é uma string separada por espaço(s). A saída dos comandos do WinDbg nem sempre vai gerar algo que podemos usar diretamente, como no caso da busca que fizemos inicialmente. Apenas para demonstração, vamos imprimir todos os tokens da saída de nosso comando.</p>
<pre>
.foreach ( answerList { s -a 0 0fffffff "caca fantasmas" } ) { .echo answerList }
0575f458
63
61
63
61
20
66
61
6e-74
61
73
6d
61
73
00
63
caca
fantasmas.c
</pre>
<p>Isso acontece porque ele utilizada cada palavra separada por espaços da saída da busca.</p>
<pre>
0575f458 63 61 63 61 20 66 61 6e-74 61 73 6d 61 73 00 63 caca fantasmas.c
</pre>
<p>Por isso usamos a flag `-<a href="posts.html?q=1">1</a>`, que faz com que o comando imprima apenas o endereço onde ele encontrou a string.</p>
<pre>
0:017> s -[1]a 0 0fffffff "caca fantasmas"
0x0575f458
</pre>
<p>Enfim, vamos ao que interessa. Para imprimir todas as strings que representam as respostas, podemos simplesmente, no OutCommands, fazer uma nova busca por string, só que dessa vez genérica, dentro de uma faixa razoável (digamos, 4KB).</p>
<pre>
0:006> .foreach ( answerList { s -[1]a 0 0fffffff "caca fantasmas" } ) { s -sa answerList L1000 }
059ff458 "caca fantasmas"
059ff467 "caca-fantasmas"
059ff47a "a fantasmas"
059ff48a "a-fantasmas"
059ff496 "ghostbusters"
059ff4a3 "Array"
059ff4a9 "mus1"
059ff4ae "james bond"
059ff4b9 "007"
059ff4bd "mus2"
059ff4c2 "mission impossible"
059ff4d5 "missao impossivel"
059ff4e7 "miss"
059ff4ed "o impossivel"
059ff4fa "missao imposs"
059ff509 "vel"
059ff50d "miss"
059ff513 "o imposs"
059ff51d "vel"
059ff521 "mus3"
059ff526 "carruagens de fogo"
059ff539 "charriots of fire"
059ff54b "chariots of fire"
...
</pre>
<p>Bom, vou parar o dump por aqui, já que, entre os leitores, pode haver quem queria se divertir primeiro do jeito certo =)</p>
<p>Vimos que o jogo é facilmente quebrável porque armazena as respostas em texto claro. Uma solução alternativa seria utilizar um hash com colisão próxima de zero. Com isso bastaria trocar as respostas possíveis por hashs possíveis e armazená-los no lugar. Quando o usuário digitasse, tudo que o programa precisaria mudar era gerar um hash a partir da resposta do usuário e comparar com o hashs das respostas válidas.</p>
<img src="img/encontrando_as_respostas_do_flash_pops_flash_pops.gif"/>
<p>Por uma incrível coincidência, esse truquezinho eu aprendi com meu amigo <a href="http://codebehind.wordpress.com/">Thiago</a> há poucos dias, que está lendo o livro Reversing. Simples, porém funcional.</p>
</section><hr/>
<span id="otimizacao_em_funcoes_recursivas" title="Otimização em funções recursivas"/></span>
<section id="section_otimizacao_em_funcoes_recursivas">
<p class="title"><a href="2008-01.html#otimizacao_em_funcoes_recursivas">#</a> Otimização em funções recursivas</p>
<span class="title-heading">Caloni, 2008-01-18 <a href="coding.html">coding</a><a href="2008-01.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_otimizacao_em_funcoes_recursivas')"><sup>[copy]</sup></a></span>
<p>O livro que estou lendo, Dominando Algoritmo com C, como o próprio nome diz, fala sobre algoritmos em C. Os primeiros capítulos são praticamente uma revisão para quem já programou em C, pois tratam de coisas que programadores com mais de cinco anos de casa devem ter na memória cachê (listas, pilhas, recursão, etc). Porém, tive uma agradável surpresa de achar um truque muito sabido que não conhecia, chamado de <a href="http://en.wikipedia.org/wiki/Tail_recursion">tail recursion</a>. Fiz questão de testar nos dois compiladores mais conhecidos e eis o resultado.</p>
<p>Imagine uma função recursiva que calcula o <a href="http://pt.wikipedia.org/wiki/Fatorial">fatorial</a> de um número. Apenas para lembrar, o fatorial de um número n é igual a n * n-1 * n-2 * n-3 até o número 1. Existem implementações iterativas (com um laço for, por exeplo) e recursivas, que no caso chamam a mesma função n vezes.</p>
<pre>
int factorial(int n)
{
if (n > 1)
return factorial(n - 1) * n;
else
return 1;
}
int main()
{
return factorial(1000);
}
</pre>
<p>Para ver o overhead de uma função dessas, compilamos com a opção de debug e depuramos no CDB.</p>
<pre>
>cl /Zi recursive-factorial1.c
>cdb recursive-factorial1.exe
Microsoft (R) Windows Debugger Version 6.8.0004.0 X86
Copyright (c) Microsoft Corporation. All rights reserved.
CommandLine: recursive-factorial1.exe
Symbol search path is: SRV*C:\Symbols*\\symbolserver\OSSYMBOLS
Executable search path is:
ModLoad: 00400000 0041e000 recursive-factorial1.exe
ModLoad: 7c900000 7c9b0000 ntdll.dll
ModLoad: 7c800000 7c8f5000 C:\WINDOWS\system32\kernel32.dll
(594.700): Break instruction exception - code 80000003 (first chance)
eax=00241eb4 ebx=7ffdb000 ecx=00000000 edx=00000001 esi=00241f48 edi=00241eb4
eip=7c901230 esp=0012fb20 ebp=0012fc94 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
ntdll!DbgBreakPoint:
7c901230 cc int 3
0:000> bp factorial
*** WARNING: Unable to verify checksum for recursive-factorial1.exe
0:000> l+*
WARNING: Line information loading disabled
Source options are ffffffff:
1/t - Step/trace by source line
2/l - List source line at prompt
4/s - List source code at prompt
8/o - Only show source code at prompt
0:000> g
Breakpoint 0 hit
> 2: {
0:000> p
> 3: if (n > 1)
0:000>
> 4: return factorial(n - 1) * n;
0:000>
Breakpoint 0 hit
> 2: {
0:000>
> 3: if (n > 1)
0:000>
> 4: return factorial(n - 1) * n;
0:000>
Breakpoint 0 hit
> 2: {
0:000>
> 3: if (n > 1)
0:000>
> 4: return factorial(n - 1) * n;
0:000>
Breakpoint 0 hit
> 2: {
0:000>
> 3: if (n > 1)
0:000>
> 4: return factorial(n - 1) * n;
0:000>
Breakpoint 0 hit
> 2: {
0:000>
> 3: if (n > 1)
0:000>
> 4: return factorial(n - 1) * n;
0:000>
Breakpoint 0 hit
> 2: {
0:000>
> 3: if (n > 1)
0:000> k
ChildEBP RetAddr
0012ff28 00401035 recursive_factorial1!factorial+0x3
0012ff34 00401035 recursive_factorial1!factorial+0x15
0012ff40 00401035 recursive_factorial1!factorial+0x15
0012ff4c 00401035 recursive_factorial1!factorial+0x15
0012ff58 00401035 recursive_factorial1!factorial+0x15
0012ff64 0040105d recursive_factorial1!factorial+0x15
0012ff70 00401268 recursive_factorial1!main+0xd
0012ffc0 7c816fd7 recursive_factorial1!__tmainCRTStartup+0x15f
0012fff0 00000000 kernel32!BaseProcessStart+0x23
0:000>
</pre>
<p>Ou seja, conforme chamamos a função recursivamente, a pilha tende a crescer. Agora imagine todo o overhead da execução, que precisa, a cada chamada, gerar um stack frame.</p>
<p>A mesma coisa podemos notar se compilarmos o mesmo fonte no GCC e depurarmos pelo GDB. Aliás, a primeira participação especial do GDB nesse blogue =)</p>
<pre>
$ gcc -g recursive-factorial1.c
$ gdb a.exe
GNU gdb 6.5.50.20060706-cvs (cygwin-special)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-cygwin"...
(gdb) break factorial
Breakpoint 1 at 0x401056: file recursive-factorial1.c, line 3.
(gdb) run
Starting program: /cygdrive/c/temp/a.exe
Loaded symbols for /cygdrive/c/WINDOWS/system32/ntdll.dll
Loaded symbols for /cygdrive/c/WINDOWS/system32/kernel32.dll
Loaded symbols for /usr/bin/cygwin1.dll
Loaded symbols for /cygdrive/c/WINDOWS/system32/advapi32.dll
Loaded symbols for /cygdrive/c/WINDOWS/system32/rpcrt4.dll
Breakpoint 1, factorial (n=1000) at recursive-factorial1.c:3
3 if (n > 1)
(gdb) step
4 return factorial(n - 1) * n;
(gdb)
Breakpoint 1, factorial (n=999) at recursive-factorial1.c:3
3 if (n > 1)
(gdb)
4 return factorial(n - 1) * n;
(gdb)
Breakpoint 1, factorial (n=998) at recursive-factorial1.c:3
3 if (n > 1)
(gdb)
4 return factorial(n - 1) * n;
(gdb)
Breakpoint 1, factorial (n=997) at recursive-factorial1.c:3
3 if (n > 1)
(gdb)
4 return factorial(n - 1) * n;
(gdb)
Breakpoint 1, factorial (n=996) at recursive-factorial1.c:3
3 if (n > 1)
(gdb)
4 return factorial(n - 1) * n;
(gdb)
Breakpoint 1, factorial (n=995) at recursive-factorial1.c:3
3 if (n > 1)
(gdb)
4 return factorial(n - 1) * n;
(gdb)
Breakpoint 1, factorial (n=994) at recursive-factorial1.c:3
3 if (n > 1)
(gdb)
4 return factorial(n - 1) * n;
(gdb)
Breakpoint 1, factorial (n=993) at recursive-factorial1.c:3
3 if (n > 1)
(gdb)
4 return factorial(n - 1) * n;
(gdb) backtrace
#0 factorial (n=993) at recursive-factorial1.c:4
#1 0x00401068 in factorial (n=994) at recursive-factorial1.c:4
#2 0x00401068 in factorial (n=995) at recursive-factorial1.c:4
#3 0x00401068 in factorial (n=996) at recursive-factorial1.c:4
#4 0x00401068 in factorial (n=997) at recursive-factorial1.c:4
#5 0x00401068 in factorial (n=998) at recursive-factorial1.c:4
#6 0x00401068 in factorial (n=999) at recursive-factorial1.c:4
#7 0x00401068 in factorial (n=1000) at recursive-factorial1.c:4
#8 0x004010b3 in main () at recursive-factorial1.c:11
(gdb)
</pre>
<p>Isso acontece porque o compilador é obrigado a montar um novo stack frame para cada chamada da mesma função, já que os valores locais precisam manter-se intactos até o retorno recursivo da função. Porém, existe uma otimização chamada de tail recursion, que ocorre se, e somente se (de acordo com meu livro):</p>
<ul><li>A chamada recursiva é a última instrução que será executada no corpo da função.</li>
<li>O valor de retorno da chamada não é parte de uma expressão.</li>
</ul>
<p>Note que ser a última instrução não implica em ser a última linha da função, o importante é que seja a última linha executada. No nosso exemplo, isso já é fato, só que usamos o retorno em uma expressão.</p>
<pre>
return factorial(n - 1) * n;
// o retorno da chamada recursiva
// é parte de uma expressão
</pre>
<p>Por isso é necessário desenvolver uma segunda versão do código, que utiliza dois parâmetros para que aconteça a situação de tail recursion.</p>
<pre>
int factorial(int n, int a)
{
if (n < 0)
return 0;
else if (n == 0)
return 1;
else if (n == 1)
return a;
else
return factorial(n - 1, n * a);
}
int main()
{
return factorial(1000, 1);
}
</pre>
<p>Nessa segunda versão, a chamada da função recursiva não mais é parte de uma expressão, e continua sendo a última instrução executada. Agora só temos que compilar com a opção de otimização certa em ambos os compiladores e testar.</p>
<p>Para o Visual Studio, podemos usar a flag /Og (otimização global).</p>
<pre>
>cl /Zi /Og recursive-factorial2.c
>cdb recursive-factorial2.exe
...
bp factorial
g
...
Breakpoint 0 hit
eax=003235f0 ebx=7c80abc1 ecx=00000001 edx=0041c560 esi=00000002 edi=00000a28
eip=00401020 esp=0012ff68 ebp=0012ffc0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
recursive_factorial2!factorial:
00401020 55 push ebp
0:000> l+*
WARNING: Line information loading disabled
Source options are ffffffff:
1/t - Step/trace by source line
2/l - List source line at prompt
4/s - List source code at prompt
8/o - Only show source code at prompt
0:000> p
> 3: if (n < 0)
0:000>
> 5: else if (n == 0)
0:000>
> 7: else if (n == 1)
0:000>
> 10: return factorial(n - 1, n * a);
0:000>
> 5: else if (n == 0)
0:000>
> 7: else if (n == 1)
0:000>
> 10: return factorial(n - 1, n * a);
0:000>
> 5: else if (n == 0)
0:000>
> 7: else if (n == 1)
0:000>
> 10: return factorial(n - 1, n * a);
0:000>
> 5: else if (n == 0)
0:000>
> 7: else if (n == 1)
0:000>
> 10: return factorial(n - 1, n * a);
0:000>
> 5: else if (n == 0)
0:000>
> 7: else if (n == 1)
0:000>
> 10: return factorial(n - 1, n * a);
0:000>
> 5: else if (n == 0)
0:000>
> 7: else if (n == 1)
0:000>
> 10: return factorial(n - 1, n * a);
0:000>
> 5: else if (n == 0)
0:000>
> 7: else if (n == 1)
0:000>
> 10: return factorial(n - 1, n * a);
0:000>
> 5: else if (n == 0)
0:000>
> 7: else if (n == 1)
0:000>
> 10: return factorial(n - 1, n * a);
0:000>
> 5: else if (n == 0)
0:000>
> 7: else if (n == 1)
0:000>
> 10: return factorial(n - 1, n * a);
0:000>
> 5: else if (n == 0)
0:000> k
ChildEBP RetAddr
0012ff64 0040105c recursive_factorial2!factorial+0x10
0012ff70 00401266 recursive_factorial2!main+0xc
0012ffc0 7c816fd7 recursive_factorial2!__tmainCRTStartup+0x15f
0012fff0 00000000 kernel32!BaseProcessStart+0x23
0:000>
</pre>
<p>Como podemos ver, após n chamadas, a pilha continua apenas com uma chamada a factorial.</p>
<p>Para o GCC, a opção é mais explítica, e funciona da mesma forma.</p>
<pre>
$ gcc -g -foptimize-sibling-calls recursive-factorial2.c
$ gdb a.exe
...
(gdb) break factorial
...
(gdb) run
...
Breakpoint 1, factorial (n=1000, a=0) at recursive-factorial2.c:3
3 if (n < 0)
(gdb) step
5 else if (n == 0)
(gdb)
7 else if (n == 1)
(gdb)
10 return factorial(n - 1, n * a);
(gdb)
11 }
(gdb)
factorial (n=1, a=6695656) at recursive-factorial2.c:10
10 return factorial(n - 1, n * a);
(gdb)
factorial (n=999, a=0) at recursive-factorial2.c:2
2 {
(gdb)
Breakpoint 1, factorial (n=999, a=0) at recursive-factorial2.c:3
3 if (n < 0)
(gdb)
5 else if (n == 0)
(gdb)
7 else if (n == 1)
(gdb)
10 return factorial(n - 1, n * a);
(gdb)
11 }
(gdb)
factorial (n=1, a=6695656) at recursive-factorial2.c:10
10 return factorial(n - 1, n * a);
(gdb)
factorial (n=998, a=0) at recursive-factorial2.c:2
2 {
(gdb)
Breakpoint 1, factorial (n=998, a=0) at recursive-factorial2.c:3
3 if (n < 0)
(gdb)
5 else if (n == 0)
(gdb)
7 else if (n == 1)
(gdb)
10 return factorial(n - 1, n * a);
(gdb)
11 }
(gdb)
factorial (n=1, a=6695656) at recursive-factorial2.c:10
10 return factorial(n - 1, n * a);
(gdb)
factorial (n=997, a=0) at recursive-factorial2.c:2
2 {
(gdb)
Breakpoint 1, factorial (n=997, a=0) at recursive-factorial2.c:3
3 if (n < 0)
(gdb)
5 else if (n == 0)
(gdb)
7 else if (n == 1)
(gdb)
10 return factorial(n - 1, n * a);
(gdb)
11 }
(gdb)
factorial (n=1, a=6695656) at recursive-factorial2.c:10
10 return factorial(n - 1, n * a);
(gdb)
factorial (n=996, a=0) at recursive-factorial2.c:2
2 {
(gdb)
Breakpoint 1, factorial (n=996, a=0) at recursive-factorial2.c:3
3 if (n < 0)
(gdb)
5 else if (n == 0)
(gdb)
7 else if (n == 1)
(gdb)
10 return factorial(n - 1, n * a);
(gdb)
11 }
(gdb)
factorial (n=1, a=6695656) at recursive-factorial2.c:10
10 return factorial(n - 1, n * a);
(gdb) backtrace
#0 factorial (n=1, a=6695656) at recursive-factorial2.c:10
#1 0x61006198 in dll_crt0_1 () from /usr/bin/cygwin1.dll
#2 0x61004416 in _cygtls::call2 () from /usr/bin/cygwin1.dll
#3 0x00000000 in ?? ()
(gdb)
</pre>
<p>Voilà!</p>
<p>PS: De brinde uma versão que permite passar o número via linha de comando para facilitar os testes (e você vai reparar que há um problema em calcular o fatorial de 1000: ele é estupidamente grande! Resolver isso fica como exercício =).</p>
<pre>
#include <stdio.h>
int factorial(int n, int a)
{
if (n < 0)
return 0;
else if (n == 0)
return 1;
else if (n == 1)
return a;
else
return factorial(n - 1, n * a);
}
int main(int argc, char* argv[])
{
if( argc == 2 )
{
int num = atoi(argv[1]);
int ret = factorial(num, 1);
printf("factorial %d = %d\n", num, ret);
return ret;
}
else
{
printf("how to use: %s <number>\n", argv[0]);
return 1;
}
}
</pre>
</section><hr/>
<span id="terceiro_encontro_c" title="Terceiro encontro C++"/></span>
<section id="section_terceiro_encontro_c">
<p class="title"><a href="2008-01.html#terceiro_encontro_c">#</a> Terceiro encontro C++</p>
<span class="title-heading">Caloni, 2008-01-22 <a href="ccppbr.html">ccppbr</a><a href="2008-01.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_terceiro_encontro_c')"><sup>[copy]</sup></a></span>
<p>Nesse último sábado aconteceu, como previsto, o terceiro encontro de usuários/programadores C++. Foi um sucesso bem maior que o esperado, pelo menos por mim e pelas pessoas com quem conversei. A organização foi fantástica, e o patrocínio muito importante, o que deu abertura para pensamentos mais ousados sobre o futuro de C++ no Brasil. Foi gerada uma lista de resoluções para o futuro (que começa hoje), onde pretendemos, inclusive, fazer reuniões no mesmo estilo trimestralmente.</p>
<p>Aqui segue um breve relato sobre as palestras que ocorreram no evento.</p>
<p>C++ com wxWidgets, de Ivo Nascimento. Inicialmente o palestrante focou o ponto muito pertinente da visão comercial do uso de um framework multiplataforma que possa rodar nos três sistemas operacionais mais usados no Brasil: Windows, Linux e MacOS. É um fato que programadores precisam se alimentar e alimentar seus filhos, então essa questão pode ser interessante para aqueles que precisam expandir seus mercados.</p>
<p>Como sempre deve rolar, houve demonstração por código de como um programa wxWidgets é estruturado. Basicamente temos inúmeras macros e um ambiente controlado por eventos, da mesma maneira que MFC e outros frameworks famosos.</p>
<p>Para mim foi uma imensa vantagem e economia de tempo ter assistido à palestra, já que faz um tempo que eu tento dar uma olhada nessa biblioteca. Para quem também gostou da idéia, dê uma olhada nos tutoriais disponíveis no sítio do projeto.</p>
<p>C++0x - novas características, de Pedro Lamarão. Para quem achava que as palestras iriam ser superficiais no quesito linguagem deve ter ficado espantado com o nível de abstração, formalidade e profundidade com que foi tratado o assunto das novas características da linguagem C++ que serão aprovadas pelo novo padrão e que irão tornar a programação genérica muito mais produtiva e eficiente.</p>
<p>O foco do palestrante foi no mais importante: quais os problemas que as novas mudanças irão resolver, e de que modo a linguagem irá se tornar mais poderosa para suportar programação genérica, paradigma que, de acordo com o debate que houve após a apresentação, ainda é muito novo, mas que poderá se tornar futuramente uma base sólida de programas mais simples de serem mantidos e especializados.</p>
<p>Para quem se interessou pelo tema e pretende estudar um pouco mais sobre as novidades na linguagem, aqui vão algumas expressões-chave para pesquisa:</p>
<ul><li>Proposed Wording for Variadic Templates</li>
<li>Proposed Wording for RValue Reference</li>
<li>Specifying C++ Concepts</li>
</ul>
<p>Threads em C++, por Wanderley Caloni. O foco principal desse tema foi dividido entre a interface, óbvia, para suportar programas multithreading em C++, incluindo abstrações de sincronismo e variáveis de condição, e a mudança significativa no padrão para definir um modelo de memória consistente com programas multithreading, a grande vantagem dessa biblioteca ter sido votada, pois tendo as bases para o que eles estão chamando de "execução consistente", a interface é mera conseqüência.</p>