Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Side by Side Diff: tools/graphviz-create.py

Issue 11363163: Utility Graphviz-create.py. It looks at hydrogen.cfg files and creates graphviz (dot) output of var… Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Removed hard-coded list of hydrogen phases. Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 #
3 # Copyright 2012 the V8 project authors. All rights reserved.
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following
12 # disclaimer in the documentation and/or other materials provided
13 # with the distribution.
14 # * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived
16 # from this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31 import optparse
32 import os
33 from os.path import join
34 import sys
35 import shlex
Jakob Kummerow 2012/11/16 17:31:57 nit: sort imports alphabetically please
mvstanton 2013/01/06 11:26:36 Done.
36 import collections
37 import tempfile
38 import subprocess
39 import glob
40
41
42 # Parse() returns:
43 # a map of compilations (functions)
44 # each compilation has a map of phases, and a name
45 # each phase has a map of blocks, and a name
46 # each block has a map of locals, and a map of statements
47 # functions, phases, blocks, statements
48 #
49 # The first statement of foo, in final phase
50 # data["foo"]["Z_Code generation"]["B0"][0]
51 #
52 # a phase is the typical area of work. Offers
53 # phase.visit_statements(visitor) - visit every statement in the phase
54 # phase.visit_blocks(visitor)
55 #
56 # of course,
57 # blocks.visit_statements(visitor)
58 # blocks.visit_locals(visitor)
59 #
60
61
62 USAGE="""usage: %prog [OPTION]...
63
64 Looks at a hydrogen cfg file and produces graphviz (ie, dot) output.
65 Output is directed to stdout for further customization before creating
66 a graph.
67
68 Examples:
69 # Run on the last function defined in the file, looking at the final
70 # HIR
71 $ %prog > output.dot
72
73 # Create the graphic too, this time with HIR, all on one line
74 $ %prog --displayHIR | dot -Tpng > out.png
75
76 # Examine a different phase of compilation for the last function
77 # Output to the screen
78 $ %prog --phase="H_Redundant phi elimination" --displayHIR
79
80 # Create an html report of the last function, with all HIR
81 $ %prog --command="report"
82 """
83
84
85 def OrderedDictionary():
86 return collections.OrderedDict()
87
88
89 def die(inputobj,expected):
Jakob Kummerow 2012/11/16 17:31:57 Looking at what this method does, shouldn't it be
mvstanton 2013/01/06 11:26:36 thx. I changed to use assert. I kept diemsg() as a
90 if inputobj != expected:
91 print "Expected: %s Got: %s" % (inputobj, expected)
92 sys.exit(1)
93
94
95 def diemsg(msg):
96 print "Unexpected error: %s" % msg
97 sys.exit(1)
98
99
100 class Printer(object):
101 def __init__(self):
102 self.stream = sys.stdout
103
104 def CreateFile(self,filename):
Jakob Kummerow 2012/11/16 17:31:57 Create/close pairs are brittle. The most robust wa
mvstanton 2013/01/06 11:26:36 Done.
105 self.stream = open(filename,"w+t")
106
107 def CloseFile(self):
108 self.stream.close()
109
110 def SetStream(self,newStream):
111 self.stream = newStream
112
113 def Print(self,string):
114 self.stream.write(string)
115
116 def PrintLine(self,string):
117 self.Print(string + "\n")
118
119
120 class Block(object):
121 """A compilation block."""
122
123 def __init__(self, name, predecessors, successors):
124 self.name = name
125 self.predecessors = predecessors
126 self.successors = successors
127 self.hir = OrderedDictionary()
128 self.lir = OrderedDictionary()
129 self.locals = OrderedDictionary()
130
131 def GetName(self):
132 return self.name
133
134 def GetPredecessors(self):
135 return self.predecessors
136
137 def GetSuccessors(self):
138 return self.successors
139
140 def AddHIR(self,statement):
141 self.hir[len(self.hir)] = statement
Jakob Kummerow 2012/11/16 17:31:57 self.hir.append(statement)
142
143 def GetHIRs(self):
144 return self.hir.values()
145
146 def AddLIR(self,statement):
147 self.lir[len(self.lir)] = statement
148
149 def GetLIRs(self):
150 return self.lir.values()
151
152 def visit(self,visitor):
153 """Visit every HIR statement in the block."""
154 for h in hir.values():
155 visitor(h)
156
157 def AddLocal(self,name,local):
158 self.locals[name] = local
159
160 def visit_locals(self,visitor):
161 for l in self.locals.values():
162 visitor(l)
163
164 def GetLocals(self):
165 return self.locals.values()
166
167 def __str__(self):
168 return "%s (pred: %d) (succ: %d)" % (
169 self.name,
Jakob Kummerow 2012/11/16 17:31:57 4-space indentation when you break inside ()
mvstanton 2013/01/06 11:26:36 Done.
170 len(self.predecessors),
171 len(self.successors))
172
173
174 class Phase(object):
175 """A compilation phase."""
176
177 def __init__(self, name):
178 self.name = name
179 self.blocks = OrderedDictionary()
180
181 def GetName(self):
182 return self.name
183
184 def AddBlock(self,name,block):
185 self.blocks[name] = block
186
187 def GetBlocks(self):
188 return self.blocks.values()
189
190 def GetBlock(self,block):
191 return self.blocks[block]
192
193 def visit_statements(self,visitor):
194 for block in self.blocks:
195 block.visit(visitor)
196
197 def visit_blocks(self,visitor):
198 for block in self.blocks.values():
199 visitor(block)
200
201 def Print(self,printer):
202 printer.Print(self)
203 for block in self.blocks.values():
204 printer.Print(block)
205
206 def __str__(self):
207 return "%s (%d blocks)" % (
208 self.name,
209 len(self.blocks))
210
211
212 class Compilation(object):
213 """A compilation unit."""
214
215 def __init__(self, name, method):
216 self.phases = OrderedDictionary()
217 self.name = name
218 self.method = method
219
220 def AddPhase(self, name, phase):
221 self.phases[name] = phase
222
223 def AllPhases(self):
224 for phase in self.phases.itervalues():
225 yield phase
226
227 def AllPhaseNames(self):
228 return self.phases.keys()
229
230 def GetName(self):
231 return self.name
232
233 def GetPhase(self,phase):
234 return self.phases[phase]
235
236 def GetPreviousPhase(self,phase):
237 # useful for diffs
238 index = self.phases.keys().index(phase)
239 if index == 0:
240 diemsg("No previous phase of " + phase)
241 return self.phases[self.phases.keys()[index - 1]]
242
243 def Print(self,printer):
244 printer.PrintLine('Compilation ' + self.name)
245 for phase in self.AllPhases():
246 phase.Print()
247
248 def __str__(self):
249 return "%s (%d phases)" % (
250 self.name,
251 len(self.phases))
252
253
254 def BuildOptions():
255 result = optparse.OptionParser(USAGE)
256 result.add_option("--input",
257 help="Input file (default hydrogen.cfg)",
Jakob Kummerow 2012/11/16 17:31:57 No need to type the default value twice: help="In
mvstanton 2013/01/06 11:26:36 Done.
258 default="hydrogen.cfg")
259 result.add_option("--command",
260 help="The command to run: 'diff','report','blocks' (default) ",
Jakob Kummerow 2012/11/16 17:31:57 nit: long line
mvstanton 2013/01/06 11:26:36 Done.
261 default="blocks")
262 result.add_option("--function",
263 help="The function to analyze (default @last)",
264 default="@last")
265 result.add_option("--phase",
266 help="The compilation phase to examine (default Z_Code generation)",
267 default="Z_Code generation")
268 result.add_option("--displayHIR",
269 help="Display HIR in blocks? (default false)",
270 default=False, action="store_true")
271 result.add_option("--displayLIR",
272 help="Display LIR in blocks? (default false)",
273 default=False, action="store_true")
274 result.add_option("-v", "--verbose", help="Verbose output",
275 default=False, action="store_true")
276 result.add_option("-d", "--debug", help="Debug output",
277 default=False, action="store_true")
278 return result
279
280
281 def ProcessOptions(options):
282 if not options.command in ["blocks","diff","report"]:
283 print "Unknown command %s" % options.command
284 return False
285 return True
286
287
288 def parseKeyString(line):
289 tokens = shlex.split(line.strip())
290 return tokens
291
292
293 class CFGReader(object):
294 """CFG file reader."""
295
296 def __init__(self, filename):
297 self.file = open(filename, "r")
298 self.savedline = None
299
300 def Dispose(self):
301 self.log.close()
302 self.log_file.close()
303
304 def pushline(self,line):
305 self.savedline = line
306
307 def nextline(self):
308 # we can push one line back
309 if self.savedline:
310 s = self.savedline
311 self.savedline = None
312 return s
313
314 return self.file.readline()
315
316 def Parse(self):
317 """Return a map of compilation units."""
318 compilations = OrderedDictionary()
319 while True:
320 compilation = self.ParseCompilation()
321 if not compilation:
322 break
323 compilations[compilation.name] = compilation
324 return compilations
325
326 def ParseCompilation(self):
327 line = self.nextline()
328 if not line:
329 return None
330 die("begin_compilation",line.strip())
331 pair = parseKeyString(self.nextline())
332 die("name",pair[0])
333 name = pair[1]
334 compilation = Compilation(name,name)
335 # now read into configs
336 while line.strip() != "end_compilation":
337 line = self.nextline()
338 if not line:
339 diemsg("Unexpected end of file")
340
341 while True:
342 phase = self.ParsePhase()
343 if not phase:
344 break
345 compilation.AddPhase(phase.name,phase)
346 return compilation
347
348 def ParseHIRs(self,block):
349 while True:
350 line = self.nextline().strip()
351 if line == "end_HIR":
352 break
353 # we don't need the first number ("0 ") and some symbols at the end
354 line = line[2:]
355 line = line.replace("<|@","")
356 block.AddHIR(line)
357
358 def ParseLIRs(self,block):
359 while True:
360 line = self.nextline().strip()
361 if line == "end_LIR":
362 break
363 # we don't need some symbols at the end
364 line = line.replace("<|@","")
365 block.AddLIR(line)
366
367 def ParseLocals(self,block):
368 while True:
369 line = self.nextline()
370 line = line.strip()
371 if line == "end_locals":
372 break
373 if len(line) > 0 and (line[0]>='0' and line[0]<='9'):
374 keys = parseKeyString(line)
375 block.AddLocal(keys[1],line)
376
377 def ParseBlock(self):
378 line = self.nextline()
379 if not line:
380 return None
381
382 if line.strip() != "begin_block":
383 self.pushline(line)
384 return None
385
386 die("begin_block",line.strip())
387 pair = parseKeyString(self.nextline())
388 die("name",pair[0])
389 name = pair[1]
390 # read pred and succ
391 while True:
392 line = self.nextline()
393 if line.strip() == "begin_states":
394 break;
395 pair = parseKeyString(line)
396 if pair[0] == "predecessors":
397 pred = pair[1:]
398 elif pair[0] == "successors":
399 succ = pair[1:]
400
401 block = Block(name,pred,succ)
402 # add hrs if found
403 while True:
404 line = self.nextline().strip()
405 if line == "end_block":
406 break;
407 elif line == "begin_locals":
408 self.ParseLocals(block)
409 elif line == "begin_LIR":
410 self.ParseLIRs(block)
411 elif line == "begin_HIR":
412 self.ParseHIRs(block)
413
414 return block
415
416 def ParsePhase(self):
417 line = self.nextline()
418 if not line:
419 return None
420
421 if line.strip() != "begin_cfg":
422 self.pushline(line)
423 return None
424
425 die("begin_cfg",line.strip())
426 pair = parseKeyString(self.nextline())
427 die("name",pair[0])
428 name = pair[1]
429 phase = Phase(name)
430
431 while True:
432 block = self.ParseBlock()
433 if not block:
434 break
435 phase.AddBlock(block.name,block)
436
437 # just consume blocks
438 # consume the "end_cfg" message
439 die("end_cfg", self.nextline().strip())
440 return phase
441
442
443 def DumpCompilations(compilations,printer):
444 printer.Print("len = %d" % len(compilations))
445 for comp in compilations.itervalues():
446 comp.Print(printer)
447
448
449 def WriteTempFile(lines):
450 temp = tempfile.NamedTemporaryFile(mode="w+t")
451 for line in lines:
452 temp.write(line)
453 temp.write("\n")
454 temp.seek(0)
455 return temp
456
457
458 def diffText(lines1,lines2):
459 """Takes two lists of text lines, creates a diff output of them as an array"""
460
461 try:
462 temp1 = WriteTempFile(lines1)
463 temp2 = WriteTempFile(lines2)
464 outputlines = []
465 cmdline = "diff -u %s %s" % (temp1.name, temp2.name)
466 process = subprocess.Popen(cmdline,
467 shell=True,
468 stdout=subprocess.PIPE,
469 stderr=subprocess.STDOUT)
470 pipe = process.stdout
471 try:
472 headerLines = 2
473 lineCount = 0
474 for line in pipe:
475 if lineCount > headerLines:
476 # strip newline
477 outputlines.append(line.strip())
478 lineCount = lineCount + 1
479 except Exception as e:
480 diemsg("error in diff = " + str(e))
481 finally:
482 pipe.close()
483
484 finally:
485 temp1.close()
486 temp2.close()
487
488 return outputlines
489
490
491 def Bold(line):
492 return "<b>%s</b>" % line
493
494
495 def Color(color,line):
496 return "<font color=\"%s\">%s</font>" % (color,line)
497
498
499 def LeftLineBreak():
500 return "<br align=\"left\"/>"
501
502
503 def HtmlLikeEscape(line):
504 return line.replace("<","&lt;").replace(">","&gt;")
505
506
507 def TableBegin():
508 return "<table border=\"0\" cellborder=\"1\" cellspacing=\"0\">\n"
509
510
511 def TableEnd():
512 return "</table>\n"
513
514
515 def NameRow(blockName):
516 return "<tr><td>%s</td></tr>\n" % Bold(blockName)
517
518
519 def FormatLine(line):
520 tesc = HtmlLikeEscape(line)
521 # Deal with diff output
522 if tesc.startswith("-"):
523 tesc = Color("red",tesc)
524 elif tesc.startswith("+"):
525 tesc = Color("blue",tesc)
526 return " %s%s\n" % (tesc, LeftLineBreak())
527
528
529 def FormatLines(lineList):
530 result = ""
531 if len(lineList) > 0:
532 result += "<tr><td>\n"
533 for l in lineList:
534 result = result + FormatLine(l)
535 result = result + "</td></tr>\n"
536 return result
537
538
539 def FormatLabel(blockName,localsList,hirList,lirList):
540 label = TableBegin()
541 label += NameRow(blockName)
542 label += FormatLines(localsList)
543 label += FormatLines(hirList)
544 label += FormatLines(lirList)
545 label += TableEnd()
546 return label
547
548
549 def DotBlockVisit(displayHIR,displayLIR,p,block):
550 localsList = []
551 hirList = []
552 lirList = []
553
Jakob Kummerow 2012/11/16 17:31:57 nit: one empty line is enough
mvstanton 2013/01/06 11:26:36 Done.
554
555 if displayHIR or displayLIR:
556 localsList = block.GetLocals()
557 if displayHIR:
558 hirList = block.GetHIRs()
559 if displayLIR:
560 lirList = block.GetLIRs()
561
562 label = FormatLabel(block.GetName(),localsList,hirList,lirList)
563 # extra newline for readability
564 label = "\n" + label
565 p.PrintLine(" block%s [shape=plaintext,label=<%s>];"
566 % (block.GetName(), label))
567 for succ in block.GetSuccessors():
568 p.PrintLine(" block%s -> block%s;" % (block.GetName(), succ))
569
570
571 def DotBlocks(functionName,phase,displayHIR,displayLIR,p):
572 """Diagram the blocks in the given function."""
573 p.PrintLine("digraph Blocks_%s {" % functionName)
574 p.PrintLine(" ratio = 1.0;")
575 phase.visit_blocks(lambda b: DotBlockVisit(displayHIR,displayLIR,p,b))
576 p.PrintLine("}")
577
578
579 def DotDiffVisit(diffs,p,block):
580 label = FormatLabel(block.GetName(),[],diffs[block.GetName()],[])
581 p.PrintLine(" block%s [shape=plaintext,label=<%s>];"
582 % (block.GetName(), label))
583 for succ in block.GetSuccessors():
584 p.PrintLine(" block%s -> block%s;" % (block.GetName(), succ))
585
586
587 def DotDiff(functionName, phasePrev, phase, changedBlocksOnly, p):
588 """Run a diff block by block between two phases in the given function."""
589 p.PrintLine("// comparing phase %s with next phase %s" % (phasePrev.GetName(),
Jakob Kummerow 2012/11/16 17:31:57 nit: trailing whitespace
590 phase.GetName()))
591 p.PrintLine("digraph Diff_%s {" % functionName)
592 p.PrintLine(" ratio = 1.0;")
593 # need to run diff -u phasePrev phase
594 # on the HIR output
595 # I think the # of blocks should be the same
596 diffResults = {}
597 blocks = phasePrev.GetBlocks()
598 for blockPrev in blocks:
599 block = phase.GetBlock(blockPrev.GetName())
600 diffResults[blockPrev.GetName()] = diffText(blockPrev.GetHIRs(),
601 block.GetHIRs())
602
603 phasePrev.visit_blocks(lambda b: DotDiffVisit(diffResults,p,b))
604 p.PrintLine("}")
605
606
607 reportText = """<html>
Jakob Kummerow 2012/11/16 17:31:57 Why are these (constant) variables when Bold() etc
mvstanton 2013/01/06 11:26:36 Good point. I got rid of the Bold() function, but
608 <title>Report on %s</title>
609 <body>
610 <h2>Block structure</h2>
611 <a href=blocks.svg><img width=800 src=blocks.svg></a>
612 <h2>All HIR</h2>
613 <a href=hirblocks.svg><img width=800 src=hirblocks.svg></a>
614 <h2>All LIR</h2>
615 <a href=lirblocks.svg><img width=800 src=lirblocks.svg></a>
616 <h2>Diffs</h2>
617 %s
618 </body>
619 </html>"""
620
621
622 diffsTemplate = """<h2>%s</h2>
623 <a href=%s><img width=800 src=%s></a>"""
624
625
626 def AsFilename(phaseName,ext):
627 phaseName = phaseName.replace(" ","_")
628 phaseName += ext
629 return phaseName
630
631
632 def DotReport(function):
633 """Make a report on the function."""
634 escapedFunctionName = function.GetName().replace(".","_")
635 dirname = "report_%s" % escapedFunctionName
636 if not os.path.exists(dirname):
637 os.mkdir(dirname)
Jakob Kummerow 2012/11/16 17:31:57 You probably want os.makedirs() here, which can al
mvstanton 2013/01/06 11:26:36 Done.
638
639 p = Printer()
640 htmlfilename = dirname + "/index.html"
641 print "Writing %s..." % htmlfilename
642 p.CreateFile(htmlfilename)
643 diffs = ""
644 phases = function.AllPhaseNames()
645 for ph in phases[1:]:
646 phfilename = AsFilename(ph,".svg")
647 diffs += diffsTemplate % (ph,phfilename,phfilename)
648 htmlText = reportText % (escapedFunctionName,diffs)
649 p.PrintLine(htmlText)
650 p.CloseFile()
651
652 # Write all dot files
653 # get last phase
654 phase = function.GetPhase(phases[-1])
655 p.CreateFile(dirname + "/blocks.dot")
656 DotBlocks(escapedFunctionName,phase,False,False,p)
657 p.CloseFile()
658
659 p.CreateFile(dirname + "/hirblocks.dot")
660 DotBlocks(escapedFunctionName,phase,True,False,p)
661 p.CloseFile()
662
663 p.CreateFile(dirname + "/lirblocks.dot")
664 DotBlocks(escapedFunctionName,phase,False,True,p)
665 p.CloseFile()
666
667 print "Computing diffs..."
668
669 for ph in phases[1:]:
670 phase = function.GetPhase(ph)
671 phasePrev = function.GetPreviousPhase(ph)
672 p.CreateFile(dirname + "/" + AsFilename(ph,".dot"))
673 DotDiff(escapedFunctionName, phasePrev, phase, True, p)
674 p.CloseFile()
675
676 # Compile the dot files
677 cmdline = "dot -Tsvg %s > %s"
678 # diff -u %s %s" % (temp1.name, temp2.name)
679 files = glob.glob(dirname + "/*.dot")
680 for f in files:
681 print "compiling " + f
682 process = subprocess.Popen(cmdline % (f,f.replace(".dot",".svg")),
683 shell=True)
684 process.wait()
685 print "done"
686
687
688 def Main():
689 p = Printer()
690
691 parser = BuildOptions()
692 (options, args) = parser.parse_args()
693 if not ProcessOptions(options):
694 parser.print_help()
695 return 1
696
697 exit_code = 0
Jakob Kummerow 2012/11/16 17:31:57 Did you mean to overwrite this when some error occ
mvstanton 2013/01/06 11:26:36 Done.
698
699 # Open input file and parse compilations, phases, blocks
700 reader = CFGReader(options.input)
701 compilations = reader.Parse()
702
703 if options.debug:
704 DumpCompilations(compilations, p)
705
706 function = options.function
707 if function == "@last":
708 function = compilations.keys()[-1]
709 elif function == "@first":
710 function = compilations.keys()[0]
711 if options.verbose:
712 print "// dump function %s" % function
713
714 phase = options.phase
715
716 # find the method
717 if not function in compilations:
718 diemsg("function " + function + " not found in input file")
719
720 function = compilations[function]
721 # find the phase
722 phase = function.GetPhase(phase)
723
724 # now what we do depends on the command
725
726 escapedFunctionName = function.GetName().replace(".","_")
727 if options.command == "blocks":
728 DotBlocks(escapedFunctionName, phase, options.displayHIR,
729 options.displayLIR, p)
730 elif options.command == "diff":
731 # take the phase and the preceding phase.
732 phasePrev = function.GetPreviousPhase(phase.GetName())
733 DotDiff(escapedFunctionName, phasePrev, phase, True, p)
734 elif options.command == "report":
735 DotReport(function)
736 return exit_code
737
738
739 if __name__ == "__main__":
740 sys.exit(Main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698