Display only number of warnings and verbatim warning text
[warndiff.git] / warndiff.rb
blobad1192dc3d3b52b17701df84a66c813bc4eae4e1
1 #!/usr/bin/ruby
2 class WarnSplit
3   include Enumerable
4   def initialize log
5     @log = log
6   end
7   def each
8     msg = ""
9     seen_infu = false
10     seen_warn = false
11     @log.each_line{|l|
12       case l
13       when /modversion changed/
14         #trash
15       when /^In file included from/
16         yield msg if msg.length > 0
17         msg = l
18         seen_infu = false
19         seen_warn = false
20       when /: In function /
21         if seen_infu or seen_warn then
22           yield msg
23           msg = l
24           seen_warn = false
25         else
26           msg << l
27         end
28         seen_infu = true
29       when /:[0-9]+:[0-9]+: note:/
30         msg << l
31       when /:[0-9]+:([0-9]+:)? ?warning: /
32         if seen_warn then
33           yield msg
34           seen_infu = false
35           msg = l
36         else
37           msg << l
38         end
39         seen_warn = true
40       else
41         msg << l if l =~ /:[0-9]+:[0-9]+: / #junk lines that don't have line number or any other significant info
42       end
43     }
44     yield msg if msg.length > 0
45   end
46 end
48 class WarnParse
49   attr_accessor :file, :origin, :reason, :text, :object, :function
50   def hash
51     file.hash | reason.hash | object.hash | function.hash
52   end
53   def == other
54     (other.file == file) && (other.reason == reason) && (other.object == object) && (other.function == function)
55   end
56   def eql? other
57     other.class == self.class && self == other
58   end
59   def initialize warning
60     @text = warning
61     @text.lines.each {|l|
62       case l
63       when /: In function /
64         @file = l.sub(/:.*/,'').chomp
65         @function = (l.match /: In function ‘([^‘’]+)’/)[1] rescue (l.match /: In function '([^']+)'/)[1] rescue nil
66       when /:[0-9]+:([0-9]+:)? ?warning: /
67         file = l.sub(/:.*/,'').chomp
68         if @file and @file != file
69           @origin = file
70         else
71           @file = file
72         end
73         @reason = (l.match /\[-W([^\]]+)\]/)[1] rescue (l.match /warning: (.+)/)[1]
74         @object = (l.match /‘([^‘’]+)’/)[1] rescue (l.match /'([^']+)'/)[1] rescue nil
75       end
76     }
77   end
78   def short_s
79     s = "File: '#@file'"
80     s << " Origin: '#@origin'" if @origin
81     s << " Reason: '#@reason'" if @reason
82     s << " Object: '#@object'" if @reason
83     s << "\n"
84   end
85   def to_s
86     short_s << @text
87   end
88 end
90 class WarnSort
91   def initialize log=''
92     @hash = Hash.new {|hsh, key| hsh[key] = [] }
93     WarnSplit.new(log).each{|w|
94       w=WarnParse.new(w)
95       @hash[w.file] << w
96     }
97   end
98   def to_s short=false
99     return '' unless self.length > 0
100     s="***** #{self.length} warnings *****\n"
101     @hash.keys.each{|k|
102       #s << "*** file: " << k << " ***\n"
103       @hash[k].each{|w|
104         #s << "* " << w.reason if w.reason
105         #s << " (" << w.object << ")" if w.object
106         #s << " in " << w.function if w.function
107         #s << " (from " << w.origin << ")" if w.origin
108         #s << "\n" if w.origin or w.reason
109         s << w.text if ! short
110       }
111     }
112     s
113   end
114   def keys
115     @hash.keys
116   end
117   def [](key)
118     @hash[key]
119   end
120   def diff old
121     res = WarnSort.new
122     keys.each{|k|
123       ws = self[k] - old[k]
124       res[k].concat ws if ws.length > 0
125     }
126     res
127   end
128   def length
129     res = 0
130     keys.each{|k|
131       res += self[k].length
132     }
133     res
134   end
137 def printhelp error=0
138 STDOUT << "#{$0} oldlog newlog\n#{$0} --test\n"
139 exit error
142 @sampledata = "include/config/auto.conf:7381:warning: symbol value '/etc/keys/x509_ima.der' invalid for IMA_X509_PATH
143 include/config/auto.conf:7825:warning: symbol value 'm' invalid for BRIDGE_NETFILTER
144 include/config/auto.conf:8206:warning: symbol value 'm' invalid for SPI_FSL_SPI
145 include/config/auto.conf:8518:warning: symbol value 'm' invalid for DEVFREQ_GOV_USERSPACE
146 include/config/auto.conf:8757:warning: symbol value 'm' invalid for SND_HDA_CODEC_ANALOG
147 include/config/auto.conf:8819:warning: symbol value 'm' invalid for SND_HDA_CODEC_REALTEK
148 "+'In file included from drivers/ata/libata-core.c:65:0:
149 drivers/ata/libata-core.c: In function ‘ata_dev_configure’:
150 include/linux/libata.h:1318:16: warning: ‘native_sectors’ may be used uninitialized in this function [-Wmaybe-uninitialized]
151   ata_dev_printk(dev, KERN_INFO, fmt, ##__VA_ARGS__)
152                 ^
153 drivers/ata/libata-core.c:1324:6: note: ‘native_sectors’ was declared here
154   u64 native_sectors;
155       ^
156 drivers/atm/ambassador.c: In function ‘ucode_init’:
157 drivers/atm/ambassador.c:1971:19: warning: ‘fw’ may be used uninitialized in this function [-Wmaybe-uninitialized]
158    release_firmware(fw);
159                    ^
160 sound/drivers/serial-u16550.c: In function ‘snd_serial_probe’:
161 sound/drivers/serial-u16550.c:966:9: warning: ‘uart’ may be used uninitialized in this function [-Wmaybe-uninitialized]
162   sprintf(card->longname, "%s [%s] at %#lx, irq %d",
163          ^
164 In file included from include/linux/cache.h:4:0,
165                  from include/linux/time.h:7,
166                  from include/linux/stat.h:60,
167                  from include/linux/module.h:10,
168                  from drivers/block/floppy.c:167:
169 drivers/block/floppy.c: In function ‘fd_locked_ioctl’:
170 include/linux/kernel.h:625:26: warning: ‘size’ may be used uninitialized in this function [-Wmaybe-uninitialized]
171   __min1 < __min2 ? __min1: __min2; })
172                           ^
173 drivers/block/floppy.c:3379:6: note: ‘size’ was declared here
174   int size;
175       ^
176 sound/pci/atiixp_modem.c: In function ‘snd_atiixp_probe’:
177 sound/pci/atiixp_modem.c:299:20: warning: ‘chip2’ may be used uninitialized in this function [-Wmaybe-uninitialized]
178   writel(value, chip2->remap_addr + ATI_REG_##reg)
179                     ^
180 sound/pci/atiixp_modem.c:1290:23: note: ‘chip2’ was declared here
181   struct atiixp_modem *chip2;
182                        ^
183 sound/pci/atiixp_modem.c: In function ‘snd_atiixp_probe’:
184 sound/pci/atiixp_modem.c:299:20: warning: ‘chip’ may be used uninitialized in this function [-Wnonsense]
185   writel(value, chip->remap_addr + ATI_REG_##reg)
186                     ^
187 sound/pci/atiixp_modem.c:1290:23: note: ‘chip’ was declared here
188   struct atiixp_modem *chip;
189                        ^
190 sound/pci/atiixp.c: In function ‘snd_atiixp_probe’:
191 sound/pci/atiixp.c:1685:14: warning: ‘chip’ may be used uninitialized in this function [-Wmaybe-uninitialized]
192     chip->ac97[0] ? snd_ac97_get_short_name(chip->ac97[0]) : "?",
193               ^
196 @sampledata2 = "include/config/auto.conf:7381:warning: symbol value '/etc/keys/x509_ima.der' invalid for IMA_X509_PATH
197 include/config/auto.conf:7825:warning: symbol value 'm' invalid for BRIDGE_NETFILTER
198 include/config/auto.conf:8206:warning: symbol value 'm' invalid for SPI_FSL_SPI
199 include/config/auto.conf:8518:warning: symbol value 'm' invalid for DEVFREQ_GOV_FANTASY
200 include/config/auto.conf:8757:warning: symbol value 'm' invalid for SND_HDA_CODEC_ANALOG
201 include/config/auto.conf:8819:warning: symbol value 'm' invalid for SND_HDA_CODEC_REALTEK
202 "+'In file included from drivers/ata/libata-core.c:65:0:
203 drivers/ata/libata-core.c: In function ‘ata_dev_configure’:
204 include/linux/libata.h:1318:16: warning: ‘native_sectors’ may be used uninitialized in this function [-Wmaybe-uninitialized]
205   ata_dev_printk(dev, KERN_INFO, fmt, ##__VA_ARGS__)
206                 ^
207 drivers/ata/libata-core.c:1324:6: note: ‘native_sectors’ was declared here
208   u64 native_sectors;
209       ^
210 drivers/atm/ambassador.c: In function ‘ucode_init’:
211 drivers/atm/ambassador.c:1971:19: warning: ‘fw’ may be used uninitialized in this function [-Wmaybe-uninitialized]
212    release_firmware(fw);
213                    ^
214 sound/drivers/serial-u16550.c: In function ‘snd_serial_probe’:
215 sound/drivers/serial-u16550.c:966:9: warning: ‘uart’ may be used uninitialized in this function [-Wmaybe-uninitialized]
216   sprintf(card->longname, "%s [%s] at %#lx, irq %d",
217          ^
218 In file included from include/linux/cache.h:4:0,
219                  from include/linux/time.h:7,
220                  from include/linux/stat.h:60,
221                  from include/linux/module.h:10,
222                  from drivers/block/floppy.c:167:
223 sound/pci/atiixp_modem.c: In function ‘snd_atiixp_probe’:
224 sound/pci/atiixp_modem.c:299:20: warning: ‘chip2’ may be used uninitialized in this function [-Wmaybe-uninitialized]
225   writel(value, chip2->remap_addr + ATI_REG_##reg)
226                     ^
227 sound/pci/atiixp_modem.c:1290:23: note: ‘chip2’ was declared here
228   struct atiixp_modem *chip2;
229                        ^
230 sound/pci/atiixp_modem.c: In function ‘snd_atiixp_probe’:
231 sound/pci/atiixp_modem.c:299:20: warning: ‘chip3’ may be used uninitialized in this function [-Wnonsense]
232   writel(value, chip3->remap_addr + ATI_REG_##reg)
233                     ^
234 sound/pci/atiixp_modem.c:1290:23: note: ‘chip’ was declared here
235   struct atiixp_modem *chip;
236                        ^
237 sound/pci/atiixp.c: In function ‘snd_atiixp_probe’:
238 sound/pci/atiixp.c:1685:14: warning: ‘chip’ may be used uninitialized in this function [-Wmaybe-uninitialized]
239     chip->ac97[0] ? snd_ac97_get_short_name(chip->ac97[0]) : "?",
240               ^
241 sound/pci/atiixp.c: In function ‘snd_atiixp_probe’:
242 sound/pci/atiixp.c:1685:14: warning: ‘chip2’ may be used uninitialized in this function [-Wmaybe-uninitialized]
243     chip2->ac97[0] ? snd_ac97_get_short_name(chip2->ac97[0]) : "?",
244               ^
247 class AssertionError < RuntimeError
250 def assert &block
251   raise AssertionError unless yield
254 def diffstrings s1, s2
255   require 'tempfile'
256   dta1 = Tempfile.new(File.basename $0)
257   dta1.write s1
258   dta1.close
259   dta2 = Tempfile.new(File.basename $0)
260   dta2.write s2
261   dta2.close
262   diff = `diff -u #{dta1.path} #{dta2.path}`
263   dta1.unlink
264   dta2.unlink
265   diff
268 def runtest
269 #STDOUT << WarnSplit.new(@sampledata).map{|w| WarnParse.new(w)}.to_a.join("\n-----------------------------------------------\n")
270 t =  WarnSort.new(@sampledata)
272 assert { t['include/config/auto.conf'].length == 6 }
273 assert { t['sound/pci/atiixp.c'].length == 1 }
274 assert { t['sound/pci/atiixp_modem.c'].length == 2 }
275 assert { t['drivers/block/floppy.c'].length == 1 }
276 assert { t['sound/drivers/serial-u16550.c'].length == 1 }
277 assert { t['drivers/atm/ambassador.c'].length == 1 }
278 assert { t['drivers/ata/libata-core.c'].length == 1 }
280 assert { t['sound/pci/atiixp_modem.c'][1].reason == 'nonsense' }
281 assert { t['sound/pci/atiixp_modem.c'][0].object == 'chip2' }
282 assert { t['sound/pci/atiixp_modem.c'][1].object == 'chip' }
283 assert { t['sound/pci/atiixp_modem.c'][0].function == 'snd_atiixp_probe' }
284 assert { t['drivers/ata/libata-core.c'][0].function == 'ata_dev_configure' }
285 assert { t['drivers/block/floppy.c'][0].function == 'fd_locked_ioctl' }
287 t.keys.each{|k|
288   case k
289   when 'drivers/block/floppy.c'
290     assert { t[k][0].origin == 'include/linux/kernel.h' }
291   when 'drivers/ata/libata-core.c'
292     assert { t[k][0].origin == 'include/linux/libata.h' }
293   else
294     t[k].each{|w| assert { ! w.origin } }
295   end
298 t.keys.each{|k|
299   if t[k].length == 6 then
300     assert{ t[k][0].reason =~ /symbol value/ }
301   else
302     assert{ t[k][0].reason == 'maybe-uninitialized' }
303   end
306 STDOUT << (diffstrings @sampledata, @sampledata2)
307 STDOUT << "\n-----------------------------------------------\n"
308 t2 =  WarnSort.new(@sampledata2)
309 STDOUT << (t2.diff t).to_s(true)
310 STDOUT << "\n-----------------------------------------------\n"
311 STDOUT << (t2.diff t).to_s
313 exit 0
316 if ARGV.length == 1 then
317   runtest if ARGV[0] == '--test'
318   printhelp if ARGV[0] == '--help'
321 printhelp(-1) if ARGV.length != 2
323 oldfile = File.open(ARGV[0])
324 newfile = File.open(ARGV[1])
325 old = WarnSort.new oldfile
326 new = WarnSort.new newfile
327 diff = new.diff old
328 STDOUT << diff.to_s
329 #STDERR << (diffstrings old.to_s, new.to_s)
330 exit diff.length if diff.length < 100
331 exit 100