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 Functions for text input, logging or text output.
30
31 @group Input:
32 HexInput
33 @group Output:
34 HexOutput
35 @group Logging:
36 HexDump,
37 CrashDump,
38 DebugLog
39 """
40
41 __revision__ = "$Id: textio.py 495 2009-12-05 06:54:05Z qvasimodo $"
42
43 __all__ = [
44 'HexDump',
45 'HexInput',
46 'HexOutput',
47 'Table',
48 'CrashDump',
49 'DebugLog',
50 'Logger',
51 ]
52
53 import win32
54
55 import time
56 import struct
57 import traceback
274
278 """
279 Static functions for user output parsing.
280 The counterparts for each method are in the L{HexInput} class.
281
282 @type integer_size: int
283 @cvar integer_size: Size in characters of an outputted integer.
284 This value is platform dependent.
285
286 @type address_size: int
287 @cvar address_size: Size in characters of an outputted address.
288 This value is platform dependent.
289 """
290
291 integer_size = len('%x' % (win32.DWORD(-1).value))+2
292 address_size = len('%x' % (win32.SIZE_T(-1).value))+2
293
294 @classmethod
296 """
297 @type integer: int
298 @param integer: Integer.
299
300 @rtype: str
301 @return: Text output.
302 """
303 return ('0x%%.%dx' % (cls.integer_size - 2)) % integer
304
305 @classmethod
307 """
308 @type address: int
309 @param address: Memory address.
310
311 @rtype: str
312 @return: Text output.
313 """
314 return ('0x%%.%dx' % (cls.address_size - 2)) % address
315
316 @staticmethod
318 """
319 Convert binary data to a string of hexadecimal numbers.
320
321 @type data: str
322 @param data: Binary data.
323
324 @rtype: str
325 @return: Hexadecimal representation.
326 """
327 return HexDump.hexadecimal(data, separator = '')
328
329 @classmethod
331 """
332 Write a list of integers to a file.
333 If a file of the same name exists, it's contents are replaced.
334
335 See L{HexInput.integer_list_file} for a description of the file format.
336
337 @type filename: str
338 @param filename: Name of the file to write.
339
340 @type values: list( int )
341 @param values: List of integers to write to the file.
342 """
343 fd = open(filename, 'w')
344 for integer in values:
345 print >> fd, cls.integer(integer)
346 fd.close()
347
348 @classmethod
350 """
351 Write a list of strings to a file.
352 If a file of the same name exists, it's contents are replaced.
353
354 See L{HexInput.string_list_file} for a description of the file format.
355
356 @type filename: str
357 @param filename: Name of the file to write.
358
359 @type values: list( int )
360 @param values: List of strings to write to the file.
361 """
362 fd = open(filename, 'w')
363 for string in values:
364 print >> fd, string
365 fd.close()
366
367 @classmethod
369 """
370 Write a list of mixed values to a file.
371 If a file of the same name exists, it's contents are replaced.
372
373 See L{HexInput.mixed_list_file} for a description of the file format.
374
375 @type filename: str
376 @param filename: Name of the file to write.
377
378 @type values: list( int )
379 @param values: List of mixed values to write to the file.
380 """
381 fd = open(filename, 'w')
382 for original in values:
383 try:
384 parsed = cls.integer(original)
385 except TypeError:
386 parsed = repr(original)
387 print >> fd, parsed
388 fd.close()
389
393 """
394 Static functions for hexadecimal dumps.
395
396 @type integer_size: int
397 @cvar integer_size: Size in characters of an outputted integer.
398 This value is platform dependent.
399
400 @type address_size: int
401 @cvar address_size: Size in characters of an outputted address.
402 This value is platform dependent.
403 """
404
405 integer_size = len('%x' % (win32.DWORD(-1).value))
406 address_size = len('%x' % (win32.SIZE_T(-1).value))
407
408 @classmethod
410 """
411 @type integer: int
412 @param integer: Integer.
413
414 @rtype: str
415 @return: Text output.
416 """
417 return ('%%.%dX' % cls.integer_size) % integer
418
419 @classmethod
421 """
422 @type address: int
423 @param address: Memory address.
424
425 @rtype: str
426 @return: Text output.
427 """
428 return ('%%.%dX' % cls.address_size) % address
429
430 @staticmethod
432 """
433 Replace unprintable characters with dots.
434
435 @type data: str
436 @param data: Binary data.
437
438 @rtype: str
439 @return: Printable text.
440 """
441 result = ''
442 for c in data:
443 if 32 < ord(c) < 128:
444 result += c
445 else:
446 result += '.'
447 return result
448
449 @staticmethod
451 """
452 Convert binary data to a string of hexadecimal numbers.
453
454 @type data: str
455 @param data: Binary data.
456
457 @type separator: str
458 @param separator:
459 Separator between the hexadecimal representation of each character.
460
461 @rtype: str
462 @return: Hexadecimal representation.
463 """
464 return separator.join( [ '%.2x' % ord(c) for c in data ] )
465
466 @staticmethod
468 """
469 Convert binary data to a string of hexadecimal WORDs.
470
471 @type data: str
472 @param data: Binary data.
473
474 @type separator: str
475 @param separator:
476 Separator between the hexadecimal representation of each WORD.
477
478 @rtype: str
479 @return: Hexadecimal representation.
480 """
481 if len(data) & 1 != 0:
482 data += '\0'
483 return separator.join( [ '%.4x' % struct.unpack('<H', data[i:i+2])[0] \
484 for i in xrange(0, len(data), 2) ] )
485
486 @staticmethod
488 """
489 Convert binary data to a string of hexadecimal DWORDs.
490
491 @type data: str
492 @param data: Binary data.
493
494 @type separator: str
495 @param separator:
496 Separator between the hexadecimal representation of each DWORD.
497
498 @rtype: str
499 @return: Hexadecimal representation.
500 """
501 if len(data) & 3 != 0:
502 data += '\0' * (4 - (len(data) & 3))
503 return separator.join( [ '%.8x' % struct.unpack('<L', data[i:i+4])[0] \
504 for i in xrange(0, len(data), 4) ] )
505
506 @staticmethod
508 """
509 Convert binary data to a string of hexadecimal QWORDs.
510
511 @type data: str
512 @param data: Binary data.
513
514 @type separator: str
515 @param separator:
516 Separator between the hexadecimal representation of each QWORD.
517
518 @rtype: str
519 @return: Hexadecimal representation.
520 """
521 if len(data) & 7 != 0:
522 data += '\0' * (8 - (len(data) & 7))
523 return separator.join( [ '%.16x' % struct.unpack('<Q', data[i:i+8])[0]\
524 for i in xrange(0, len(data), 8) ] )
525
526 @classmethod
527 - def hexline(cls, data, separator = ' ', width = None):
528 """
529 Dump a line of hexadecimal numbers from binary data.
530
531 @type data: str
532 @param data: Binary data.
533
534 @type separator: str
535 @param separator:
536 Separator between the hexadecimal representation of each character.
537
538 @type width: int
539 @param width:
540 (Optional) Maximum number of characters to convert per text line.
541 This value is also used for padding.
542
543 @rtype: str
544 @return: Multiline output text.
545 """
546 if width is None:
547 fmt = '%s %s'
548 else:
549 fmt = '%%-%ds %%-%ds' % ((len(separator)+2)*width-1, width)
550 return fmt % (cls.hexadecimal(data, separator), cls.printable(data))
551
552 @classmethod
553 - def hexblock(cls, data, address = None, separator = ' ', width = 8):
554 """
555 Dump a block of hexadecimal numbers from binary data.
556 Also show a printable text version of the data.
557
558 @type data: str
559 @param data: Binary data.
560
561 @type address: str
562 @param address: Memory address where the data was read from.
563
564 @type separator: str
565 @param separator:
566 Separator between the hexadecimal representation of each character.
567
568 @type width: int
569 @param width:
570 (Optional) Maximum number of characters to convert per text line.
571
572 @rtype: str
573 @return: Multiline output text.
574 """
575 return cls.hexblock_cb(cls.hexline, data, address, width,
576 cb_kwargs = {'width' : width, 'separator' : separator})
577
578 @classmethod
579 - def hexblock_cb(cls, callback, data, address = None, width = 16,
580 cb_args = (), cb_kwargs = {}):
581 """
582 Dump a block of binary data using a callback function to convert each
583 line of text.
584
585 @type callback: function
586 @param callback: Callback function to convert each line of data.
587
588 @type data: str
589 @param data: Binary data.
590
591 @type address: str
592 @param address:
593 (Optional) Memory address where the data was read from.
594
595 @type cb_args: str
596 @param cb_args:
597 (Optional) Arguments to pass to the callback function.
598
599 @type cb_kwargs: str
600 @param cb_kwargs:
601 (Optional) Keyword arguments to pass to the callback function.
602
603 @type width: int
604 @param width:
605 (Optional) Maximum number of bytes to convert per text line.
606
607 @rtype: str
608 @return: Multiline output text.
609 """
610 result = ''
611 if address is None:
612 for i in xrange(0, len(data), width):
613 result = '%s%s\n' % ( result, \
614 callback(data[i:i+width], *cb_args, **cb_kwargs) )
615 else:
616 for i in xrange(0, len(data), width):
617 result = '%s%s: %s\n' % ( result, cls.address(address), \
618 callback(data[i:i+width], *cb_args, **cb_kwargs) )
619 address += width
620 return result
621
622 @classmethod
623 - def hexblock_byte(cls, data, address = None, separator = ' ', width = 16):
624 """
625 Dump a block of hexadecimal BYTEs from binary data.
626
627 @type data: str
628 @param data: Binary data.
629
630 @type address: str
631 @param address: Memory address where the data was read from.
632
633 @type separator: str
634 @param separator:
635 Separator between the hexadecimal representation of each BYTE.
636
637 @type width: int
638 @param width:
639 (Optional) Maximum number of BYTEs to convert per text line.
640
641 @rtype: str
642 @return: Multiline output text.
643 """
644 return cls.hexblock_cb(cls.hexadecimal, data, address, width,
645 cb_kwargs = {'separator': separator})
646
647 @classmethod
648 - def hexblock_word(cls, data, address = None, separator = ' ', width = 8):
649 """
650 Dump a block of hexadecimal WORDs from binary data.
651
652 @type data: str
653 @param data: Binary data.
654
655 @type address: str
656 @param address: Memory address where the data was read from.
657
658 @type separator: str
659 @param separator:
660 Separator between the hexadecimal representation of each WORD.
661
662 @type width: int
663 @param width:
664 (Optional) Maximum number of WORDs to convert per text line.
665
666 @rtype: str
667 @return: Multiline output text.
668 """
669 return cls.hexblock_cb(cls.hexa_word, data, address, width * 2,
670 cb_kwargs = {'separator': separator})
671
672 @classmethod
673 - def hexblock_dword(cls, data, address = None, separator = ' ', width = 4):
674 """
675 Dump a block of hexadecimal DWORDs from binary data.
676
677 @type data: str
678 @param data: Binary data.
679
680 @type address: str
681 @param address: Memory address where the data was read from.
682
683 @type separator: str
684 @param separator:
685 Separator between the hexadecimal representation of each DWORD.
686
687 @type width: int
688 @param width:
689 (Optional) Maximum number of DWORDs to convert per text line.
690
691 @rtype: str
692 @return: Multiline output text.
693 """
694 return cls.hexblock_cb(cls.hexa_dword, data, address, width * 4,
695 cb_kwargs = {'separator': separator})
696
697 @classmethod
698 - def hexblock_qword(cls, data, address = None, separator = ' ', width = 2):
699 """
700 Dump a block of hexadecimal QWORDs from binary data.
701
702 @type data: str
703 @param data: Binary data.
704
705 @type address: str
706 @param address: Memory address where the data was read from.
707
708 @type separator: str
709 @param separator:
710 Separator between the hexadecimal representation of each QWORD.
711
712 @type width: int
713 @param width:
714 (Optional) Maximum number of QWORDs to convert per text line.
715
716 @rtype: str
717 @return: Multiline output text.
718 """
719 return cls.hexblock_cb(cls.hexa_qword, data, address, width * 8,
720 cb_kwargs = {'separator': separator})
721
722
723
724 -class Table (object):
725 """
726 Text based table. The number of columns and the width of each column
727 is automatically calculated.
728 """
729
731 """
732 @type sep: str
733 @param sep: Separator between cells in each row.
734 """
735 self.__cols = list()
736 self.__width = list()
737 self.__sep = sep
738
740 """
741 Add a row to the table. All items are converted to strings.
742
743 @type row: tuple
744 @keyword row: Each argument is a cell in the table.
745 """
746 row = [ str(item) for item in row ]
747 len_row = [ len(item) for item in row ]
748 width = self.__width
749 len_old = len(width)
750 len_new = len(row)
751 known = min(len_old, len_new)
752 missing = len_new - len_old
753 if missing > 0:
754 width.extend( len_row[ -missing : ] )
755 self.__width = [ max( width[i], len_row[i] ) for i in xrange(len_new) ]
756 self.__cols.append(row)
757
758 - def justify(self, column, direction):
759 """
760 Make the text in a column left or right justified.
761
762 @type column: int
763 @param column: Index of the column.
764
765 @type direction: int
766 @param direction:
767 C{1} to justify left,
768 C{-1} to justify right.
769
770 @raise IndexError: Bad column index.
771 @raise ValueError: Bad direction value.
772 """
773 if direction == -1:
774 self.__width[column] = abs(self.__width[column])
775 elif direction == 1:
776 self.__width[column] = - abs(self.__width[column])
777 else:
778 raise ValueError, "Bad direction value."
779
781 """
782 Get the text output for the table.
783
784 @rtype: str
785 @return: Text output.
786 """
787 return '%s\n' % '\n'.join( self.yieldOutput() )
788
790 """
791 Generate the text output for the table.
792
793 @rtype: generator of str
794 @return: Text output.
795 """
796 width = self.__width
797 if width:
798 num_cols = len(width)
799 fmt = ['%%%ds' % -w for w in width]
800 if width[-1] > 0:
801 fmt[-1] = '%s'
802 fmt = self.__sep.join(fmt)
803 for row in self.__cols:
804 row.extend( [''] * (num_cols - len(row)) )
805 yield fmt % tuple(row)
806
810 """
811 Static functions for crash dumps.
812
813 @type reg_template: str
814 @cvar reg_template: Template for the L{dump_registers} method.
815 """
816
817
818 reg_template = {
819 'i386' : (
820 'eax=%(Eax).8x ebx=%(Ebx).8x ecx=%(Ecx).8x edx=%(Edx).8x esi=%(Esi).8x edi=%(Edi).8x\n'
821 'eip=%(Eip).8x esp=%(Esp).8x ebp=%(Ebp).8x %(efl_dump)s\n'
822 'cs=%(SegCs).4x ss=%(SegSs).4x ds=%(SegDs).4x es=%(SegEs).4x fs=%(SegFs).4x gs=%(SegGs).4x efl=%(EFlags).8x\n'
823 ),
824 'amd64' : (
825 'rax=%(Rax).16x rbx=%(Rbx).16x rcx=%(Rcx).16x\n'
826 'rdx=%(Rdx).16x rsi=%(Rsi).16x rdi=%(Rdi).16x\n'
827 'rip=%(Rip).16x rsp=%(Rsp).16x rbp=%(Rbp).16x\n'
828 ' r8=%(R8).16x r9=%(R9).16x r10=%(R10).16x\n'
829 'r11=%(R11).16x r12=%(R12).16x r13=%(R13).16x\n'
830 'r14=%(R14).16x r15=%(R15).16x\n'
831 '%(efl_dump)s\n'
832 'cs=%(SegCs).4x ss=%(SegSs).4x ds=%(SegDs).4x es=%(SegEs).4x fs=%(SegFs).4x gs=%(SegGs).4x efl=%(EFlags).8x\n'
833 ),
834 }
835
836 @staticmethod
838 """
839 Dump the x86 processor flags.
840 The output mimics that of the WinDBG debugger.
841
842 @type efl: int
843 @param efl: Value of the eFlags register.
844
845 @rtype: str
846 @return: Text suitable for logging.
847 """
848 if efl is None:
849 return ''
850 if win32.CONTEXT.arch not in ('i386', 'amd64'):
851 raise NotImplementedError
852 efl_dump = 'iopl=%1d' % ((efl & 0x3000) >> 12)
853 if efl & 0x100000:
854 efl_dump += ' vip'
855 else:
856 efl_dump += ' '
857 if efl & 0x80000:
858 efl_dump += ' vif'
859 else:
860 efl_dump += ' '
861
862 if efl & 0x800:
863 efl_dump += ' ov'
864 else:
865 efl_dump += ' no'
866 if efl & 0x400:
867 efl_dump += ' dn'
868 else:
869 efl_dump += ' up'
870 if efl & 0x200:
871 efl_dump += ' ei'
872 else:
873 efl_dump += ' di'
874
875 if efl & 0x80:
876 efl_dump += ' ng'
877 else:
878 efl_dump += ' pl'
879 if efl & 0x40:
880 efl_dump += ' zr'
881 else:
882 efl_dump += ' nz'
883 if efl & 0x10:
884 efl_dump += ' ac'
885 else:
886 efl_dump += ' na'
887
888 if efl & 0x4:
889 efl_dump += ' pe'
890 else:
891 efl_dump += ' po'
892
893 if efl & 0x1:
894 efl_dump += ' cy'
895 else:
896 efl_dump += ' nc'
897 return efl_dump
898
899 @classmethod
901 """
902 Dump the x86 processor register values.
903 The output mimics that of the WinDBG debugger.
904
905 @type registers: dict( str S{->} int )
906 @param registers: Dictionary mapping register names to their values.
907
908 @rtype: str
909 @return: Text suitable for logging.
910 """
911 if registers is None:
912 return ''
913 arch = win32.CONTEXT.arch
914 if arch not in ('i386', 'amd64'):
915 raise NotImplementedError
916 registers = registers.copy()
917 registers['efl_dump'] = cls.dump_flags( registers['EFlags'] )
918 return cls.reg_template[arch] % registers
919
920 @staticmethod
922 """
923 Dump data pointed to by the given registers, if any.
924
925 @type registers: dict( str S{->} int )
926 @param registers: Dictionary mapping register names to their values.
927 This value is returned by L{Thread.get_context}.
928
929 @type data: dict( str S{->} str )
930 @param data: Dictionary mapping register names to the data they point to.
931 This value is returned by L{Thread.peek_pointers_in_registers}.
932
933 @rtype: str
934 @return: Text suitable for logging.
935 """
936 if None in (registers, data):
937 return ''
938 names = data.keys()
939 names.sort()
940 result = ''
941 for reg_name in names:
942 tag = reg_name.lower()
943 dumped = HexDump.hexline(data[reg_name], separator, width)
944 result += '%s -> %s\n' % (tag, dumped)
945 return result
946
947 @staticmethod
949 """
950 Dump data from pointers guessed within the given binary data.
951
952 @type data: str
953 @param data: Dictionary mapping offsets to the data they point to.
954
955 @type base: int
956 @param base: Base offset.
957
958 @rtype: str
959 @return: Text suitable for logging.
960 """
961 if data is None:
962 return ''
963 pointers = data.keys()
964 pointers.sort()
965 result = ''
966 for offset in pointers:
967 dumped = HexDump.hexline(data[offset], separator, width)
968 result += '%s -> %s' % (HexDump.address(base + offset), dumped)
969 return result
970
971 @staticmethod
973 """
974 Dump data from pointers guessed within the given stack dump.
975
976 @type data: str
977 @param data: Dictionary mapping stack offsets to the data they point to.
978
979 @rtype: str
980 @return: Text suitable for logging.
981 """
982 if data is None:
983 return ''
984 pointers = data.keys()
985 pointers.sort()
986 result = ''
987 if pointers:
988 if win32.CONTEXT.arch == 'i386':
989 spreg = 'esp'
990 elif win32.CONTEXT.arch == 'i386':
991 spreg = 'rsp'
992 else:
993 spreg = 'STACK'
994 tag_fmt = '[%s+0x%%.%dx]' % (spreg, len( '%x' % pointers[-1] ) )
995 for offset in pointers:
996 dumped = HexDump.hexline(data[offset], separator, width)
997 tag = tag_fmt % offset
998 result += '%s -> %s\n' % (tag, dumped)
999 return result
1000
1001 @staticmethod
1003 """
1004 Dump a stack trace, as returned by L{Thread.get_stack_trace} with the
1005 C{bUseLabels} parameter set to C{False}.
1006
1007 @type stack_trace: list( int, int, str )
1008 @param stack_trace: Stack trace as a list of tuples of
1009 ( return address, frame pointer, module filename )
1010
1011 @rtype: str
1012 @return: Text suitable for logging.
1013 """
1014 if stack_trace is None:
1015 return ''
1016 table = Table()
1017 table.addRow('Frame', 'Origin', 'Module')
1018 for (fp, ra, mod) in stack_trace:
1019 table.addRow( HexDump.address(fp), HexDump.address(ra), mod )
1020 return table.getOutput()
1021
1022 @staticmethod
1024 """
1025 Dump a stack trace,
1026 as returned by L{Thread.get_stack_trace_with_labels}.
1027
1028 @type stack_trace: list( int, int, str )
1029 @param stack_trace: Stack trace as a list of tuples of
1030 ( return address, frame pointer, module filename )
1031
1032 @rtype: str
1033 @return: Text suitable for logging.
1034 """
1035 if stack_trace is None:
1036 return ''
1037 table = Table()
1038 table.addRow('Frame', 'Origin')
1039 for (fp, label) in stack_trace:
1040 table.addRow( HexDump.address(fp), label )
1041 return table.getOutput()
1042
1043
1044
1045
1046
1047
1048
1049 @staticmethod
1050 - def dump_code(disassembly, pc = None, bLowercase = True):
1051 """
1052 Dump a disassembly. Optionally mark where the program counter is.
1053
1054 @type disassembly: list of tuple( int, int, str, str )
1055 @param disassembly: Disassembly dump as returned by
1056 L{Process.disassemble} or L{Thread.disassemble_around_pc}.
1057
1058 @type pc: int
1059 @param pc: (Optional) Program counter.
1060
1061 @type bLowercase: bool
1062 @param bLowercase: (Optional) If C{True} convert the code to lowercase.
1063
1064 @rtype: str
1065 @return: Text suitable for logging.
1066 """
1067 if disassembly is None:
1068 return ''
1069 table = Table(sep = ' | ')
1070 for (addr, size, code, dump) in disassembly:
1071 if bLowercase:
1072 code = code.lower()
1073 if addr == pc:
1074 addr = ' * %s' % HexDump.address(addr)
1075 else:
1076 addr = ' %s' % HexDump.address(addr)
1077 table.addRow(addr, dump, code)
1078 table.justify(1, 1)
1079 return table.getOutput()
1080
1081 @staticmethod
1082 - def dump_code_line(disassembly_line, bShowAddress = True,
1083 bShowDump = True,
1084 bLowercase = True,
1085 dwDumpWidth = None,
1086 dwCodeWidth = None):
1087 """
1088 Dump a single line of code. To dump a block of code use L{dump_code}.
1089
1090 @type disassembly_line: tuple( int, int, str, str )
1091 @param disassembly_line: Single item of the list returned by
1092 L{Process.disassemble} or L{Thread.disassemble_around_pc}.
1093
1094 @type bShowAddress: bool
1095 @param bShowAddress: (Optional) If C{True} show the memory address.
1096
1097 @type bShowDump: bool
1098 @param bShowDump: (Optional) If C{True} show the hexadecimal dump.
1099
1100 @type bLowercase: bool
1101 @param bLowercase: (Optional) If C{True} convert the code to lowercase.
1102
1103 @type dwDumpWidth: int or None
1104 @param dwDumpWidth: (Optional) Width in characters of the hex dump.
1105
1106 @type dwCodeWidth: int or None
1107 @param dwCodeWidth: (Optional) Width in characters of the code.
1108
1109 @rtype: str
1110 @return: Text suitable for logging.
1111 """
1112 (addr, size, code, dump) = disassembly_line
1113 dump = dump.replace(' ', '')
1114 result = list()
1115 fmt = ''
1116 if bShowAddress:
1117 result.append( HexDump.address(addr) )
1118 fmt += '%%%ds:' % HexDump.address_size
1119 if bShowDump:
1120 result.append(dump)
1121 if dwDumpWidth:
1122 fmt += ' %%-%ds' % dwDumpWidth
1123 else:
1124 fmt += ' %s'
1125 if bLowercase:
1126 code = code.lower()
1127 result.append(code)
1128 if dwCodeWidth:
1129 fmt += ' %%-%ds' % dwCodeWidth
1130 else:
1131 fmt += ' %s'
1132 return fmt % tuple(result)
1133
1134 @staticmethod
1136 """
1137 Dump the memory map of a process. Optionally show the filenames for
1138 memory mapped files as well.
1139
1140 @type memoryMap: list( L{win32.MemoryBasicInformation} )
1141 @param memoryMap: Memory map returned by L{Process.get_memory_map}.
1142
1143 @type mappedFilenames: dict( int S{->} str )
1144 @param mappedFilenames: (Optional) Memory mapped filenames
1145 returned by L{Process.get_mapped_filenames}.
1146
1147 @rtype: str
1148 @return: Text suitable for logging.
1149 """
1150 table = Table()
1151 if mappedFilenames:
1152 table.addRow("Address", "Size", "State", "Access", "Type", "File")
1153 else:
1154 table.addRow("Address", "Size", "State", "Access", "Type")
1155
1156
1157 for mbi in memoryMap:
1158
1159
1160 BaseAddress = HexDump.address(mbi.BaseAddress)
1161 RegionSize = HexDump.address(mbi.RegionSize)
1162
1163
1164 mbiState = mbi.State
1165 if mbiState == win32.MEM_RESERVE:
1166 State = "Reserved"
1167 elif mbiState == win32.MEM_COMMIT:
1168 State = "Commited"
1169 elif mbiState == win32.MEM_FREE:
1170 State = "Free"
1171 else:
1172 State = "Unknown"
1173
1174
1175 if mbiState != win32.MEM_COMMIT:
1176 Protect = ""
1177 else:
1178 mbiProtect = mbi.Protect
1179 if mbiProtect & win32.PAGE_NOACCESS:
1180 Protect = "--- "
1181 elif mbiProtect & win32.PAGE_READONLY:
1182 Protect = "R-- "
1183 elif mbiProtect & win32.PAGE_READWRITE:
1184 Protect = "RW- "
1185 elif mbiProtect & win32.PAGE_WRITECOPY:
1186 Protect = "RC- "
1187 elif mbiProtect & win32.PAGE_EXECUTE:
1188 Protect = "--X "
1189 elif mbiProtect & win32.PAGE_EXECUTE_READ:
1190 Protect = "R-X "
1191 elif mbiProtect & win32.PAGE_EXECUTE_READWRITE:
1192 Protect = "RWX "
1193 elif mbiProtect & win32.PAGE_EXECUTE_WRITECOPY:
1194 Protect = "RCX "
1195 else:
1196 Protect = "??? "
1197 if mbiProtect & win32.PAGE_GUARD:
1198 Protect += "G"
1199 else:
1200 Protect += "-"
1201 if mbiProtect & win32.PAGE_NOCACHE:
1202 Protect += "N"
1203 else:
1204 Protect += "-"
1205 if mbiProtect & win32.PAGE_WRITECOMBINE:
1206 Protect += "W"
1207 else:
1208 Protect += "-"
1209
1210
1211 mbiType = mbi.Type
1212 if mbiType == win32.MEM_IMAGE:
1213 Type = "Image"
1214 elif mbiType == win32.MEM_MAPPED:
1215 Type = "Mapped"
1216 elif mbiType == win32.MEM_PRIVATE:
1217 Type = "Private"
1218 elif mbiType == 0:
1219 Type = ""
1220 else:
1221 Type = "Unknown"
1222
1223
1224 if mappedFilenames:
1225 FileName = mappedFilenames.get(mbi.BaseAddress, '')
1226 table.addRow( BaseAddress, RegionSize, State, Protect, Type, FileName )
1227 else:
1228 table.addRow( BaseAddress, RegionSize, State, Protect, Type )
1229
1230
1231 return table.getOutput()
1232
1236 'Static functions for debug logging.'
1237
1238 @staticmethod
1239 - def log_text(text):
1240 """
1241 Log lines of text, inserting a timestamp.
1242
1243 @type text: str
1244 @param text: Text to log.
1245
1246 @rtype: str
1247 @return: Log line.
1248 """
1249 if text.endswith('\n'):
1250 text = text[:-len('\n')]
1251
1252 ltime = time.strftime("%X")
1253 msecs = (time.time() % 1) * 1000
1254 return '[%s.%04d] %s' % (ltime, msecs, text)
1255
1256
1257 @classmethod
1259 """
1260 Log lines of text associated with a debug event.
1261
1262 @type event: L{Event}
1263 @param event: Event object.
1264
1265 @type text: str
1266 @param text: Text to log.
1267
1268 @rtype: str
1269 @return: Log line.
1270 """
1271 text = 'pid %d tid %d: %s' % (event.get_pid(), event.get_tid(), text)
1272
1273 return cls.log_text(text)
1274
1275
1276
1277 -class Logger(object):
1278 """
1279 Logs text to standard output and/or a text file.
1280
1281 @type logfile: str or None
1282 @ivar logfile: Append messahes to this text file.
1283
1284 @type verbose: bool
1285 @ivar verbose: C{True} to print messages to standard output.
1286
1287 @type fd: file
1288 @ivar fd: File object where log messages are printed to.
1289 C{None} if no log file is used.
1290 """
1291
1292 - def __init__(self, logfile = None, verbose = True):
1293 """
1294 @type logfile: str or None
1295 @param logfile: Append messahes to this text file.
1296
1297 @type verbose: bool
1298 @param verbose: C{True} to print messages to standard output.
1299 """
1300 self.verbose = verbose
1301 self.logfile = logfile
1302 if self.logfile:
1303 self.fd = open(self.logfile, 'a+')
1304
1306 msg = "Warning, error writing log file %s: %s"
1307 msg = msg % (self.logfile, str(e))
1308 print DebugLog.log_text(msg)
1309 self.logfile = None
1310 self.fd = None
1311
1313 if self.verbose:
1314 print text
1315 if self.logfile:
1316 try:
1317 self.fd.writelines('%s\n' % text)
1318 except IOError, e:
1319 self.__logfile_error(e)
1320
1321 - def log_text(self, text):
1322 """
1323 Log lines of text, inserting a timestamp.
1324
1325 @type text: str
1326 @param text: Text to log.
1327 """
1328 self.__do_log( DebugLog.log_text(text) )
1329
1331 """
1332 Log lines of text associated with a debug event.
1333
1334 @type event: L{Event}
1335 @param event: Event object.
1336
1337 @type text: str
1338 @param text: Text to log.
1339 """
1340 self.__do_log( DebugLog.log_event(event, text) )
1341
1343 """
1344 Log lines of text associated with the last Python exception.
1345 """
1346 self.__do_log( 'Exception raised: %s' % traceback.format_exc() )
1347