1132718Skan#!/usr/bin/ksh 272562Sobrien# 3169689Skan# dexplorer - DTrace system explorer, runs a collection of scripts. 4169689Skan# Written using DTrace (Solaris 10 3/05). 550397Sobrien# 6132718Skan# This program automatically runs a collection of DTrace scripts to examine 750397Sobrien# many areas of the system, and places the output in a meaningful directory 850397Sobrien# structure that is tar'd and gzip'd. 9132718Skan# 1050397Sobrien# $Id: dexplorer 3 2007-08-01 10:50:08Z brendan $ 11132718Skan# 1250397Sobrien# USAGE: dexplorer [-yDT] [-d outputdir] [-i interval] 1350397Sobrien# 1450397Sobrien# -q # quiet mode 1550397Sobrien# -y # "yes", don't prompt for confirmation 16132718Skan# -D # don't delete output dir 1750397Sobrien# -T # don't create output tar.gz 1850397Sobrien# -d outputdir # output directory 1950397Sobrien# -i interval # interval for each sample 2050397Sobrien# eg, 2150397Sobrien# dexplorer # default is 5 second samples 22132718Skan# dexplorer -y -i30 # no prompting, with 30 second samples 23169689Skan# 24169689Skan# SEE ALSO: DTraceToolkit 2550397Sobrien# 2650397Sobrien# THANKS: David Visser, et all. for the idea and encouragement. 2750397Sobrien# 28132718Skan# COPYRIGHT: Copyright (c) 2005 Brendan Gregg. 29132718Skan# 3050397Sobrien# CDDL HEADER START 3150397Sobrien# 3250397Sobrien# The contents of this file are subject to the terms of the 3350397Sobrien# Common Development and Distribution License, Version 1.0 only 3450397Sobrien# (the "License"). You may not use this file except in compliance 3550397Sobrien# with the License. 36169689Skan# 3750397Sobrien# You can obtain a copy of the license at Docs/cddl1.txt 3850397Sobrien# or http://www.opensolaris.org/os/licensing. 3950397Sobrien# See the License for the specific language governing permissions 4050397Sobrien# and limitations under the License. 4190075Sobrien# 4250397Sobrien# CDDL HEADER END 4390075Sobrien# 4450397Sobrien# CODE: 4550397Sobrien# 4690075Sobrien# This is currently a monolithic script, and while it contains only 4790075Sobrien# a few dozen straigftforward DTrace scripts I think it's desirable to 4890075Sobrien# keep it that way. The scripts themselves have designed to be very 4990075Sobrien# generic (eg, switching on all sdt:::), and are aggregations to keep a 5090075Sobrien# limit on the size of the output. 51132718Skan# 52169689Skan# Author: Brendan Gregg [Sydney, Australia] 53169689Skan# 5450397Sobrien# 23-Jun-2005 Brendan Gregg Created this. 55169689Skan# 28-Jun-2005 " " Last update. 56169689Skan 57169689Skan# 58169689Skan# Default variables 59169689Skan# 60169689Skaninterval=5 # time of each sample 61169689Skanverbose=1 # print screen output 62169689Skanprompt=1 # prompt before run 63169689Skantar=1 # create tar file 64169689Skandelete=1 # delete output dirs 65169689Skandtrace=/usr/sbin/dtrace # path to dtrace 66169689Skanroot=. # default output dir 67169689SkanPATH=/usr/bin:/usr/sbin # safe path 68169689Skandir=de_`uname -n`_`date +%Y%m%d%H%M` # OUTPUT FILENAME 69169689Skansamples=20 # max number of tests 70169689Skancurrent=0 # current sample 71169689Skan 72169689Skan# 73169689Skan# Process options 74169689Skan# 75169689Skanwhile getopts d:hi:qyDT name 76169689Skando 77169689Skan case $name in 78169689Skan d) root=$OPTARG ;; 7950397Sobrien i) interval=$OPTARG ;; 80169689Skan q) verbose=0 ;; 81169689Skan y) prompt=0 ;; 82169689Skan D) delete=0 ;; 83169689Skan T) tar=0 ;; 84169689Skan h|?) cat <<-END >&2 85169689Skan USAGE: dexplorer [-qyDT] [-d outputdir] [-i interval] 86169689Skan 87169689Skan -q # quiet mode 88169689Skan -y # "yes", don't prompt for confirmation 89169689Skan -D # don't delete output dir 90169689Skan -T # don't create output tar.gz 91169689Skan -d outputdir # output directory 92169689Skan -i interval # interval for each sample 93169689Skan eg, 94169689Skan dexplorer # default is 5 second samples 95169689Skan dexplorer -y -i30 # no prompting, with 30 second samples 96169689Skan END 97169689Skan exit 1 98169689Skan esac 99169689Skandone 100169689Skanshift $(( OPTIND - 1 )) 101169689Skan 102169689Skan# 10350397Sobrien# Confirm path 104169689Skan# 105169689Skanif [[ "$prompt" == "1" ]] ; then 106169689Skan if [[ "$root" == "." ]]; then 107169689Skan print "Output dir will be the current dir ($PWD)." 108169689Skan else 109169689Skan print "Output dir will be $root" 110169689Skan fi 111169689Skan print -n "Hit enter for yes, or type path: " 112169689Skan read ans junk 113169689Skan if [[ "$ans" == [yY] || "$ans" == [yY]es ]]; then 114169689Skan print "WARNING: I didn't ask for \"$ans\"!" 115169689Skan print "\tI was asking for the path or just enter." 116169689Skan print "\tignoring \"$ans\"..." 117169689Skan fi 118169689Skan if [[ "$ans" != "" ]]; then 119169689Skan root=$ans 120169689Skan print "Output is now $root." 121169689Skan fi 122169689Skanfi 123169689Skan 124169689Skan# 125169689Skan# Sanity checks 126169689Skan# 127169689Skanif [[ "$interval" == *[a-zA-Z]* ]]; then 128169689Skan print "ERROR2: Invalid interval $interval.\n" 129169689Skan print "Please use a number of seconds." 130169689Skan exit 2 131169689Skanfi 132169689Skanif (( ${#interval} < 1 )); then 133169689Skan print "ERROR3: Length of interval $interval too short.\n" 134169689Skan print "Minimum 1 second." 135169689Skan exit 3 136169689Skanfi 137169689Skanif [[ ! -d "$root" ]]; then 138169689Skan print "ERROR4: Output directory \"$root\" does not exist.\n" 139169689Skan print "Perhaps try a mkdir first?" 140169689Skan print "or use an existing dir, eg \"/tmp\"" 141169689Skan exit 4 142169689Skanfi 143169689Skanif [[ ! -w "$root" ]]; then 144169689Skan print "ERROR5: Can't write to output directory \"$root\".\n" 145169689Skan print "Are you logged in as root?" 146169689Skan print "Perhaps try another directory, eg \"/tmp\"" 147169689Skan exit 5 148169689Skanfi 149169689Skanif [[ `$dtrace -b1k -qn 'BEGIN { trace(pid); exit(0); }'` == "" ]]; then 150169689Skan print "ERROR6: Unable to run dtrace!\n" 151169689Skan print "Perhaps this is a permission problem? Try running as root." 152169689Skan exit 6 153169689Skanfi 154169689Skan 155169689Skan# calculate total time 156169689Skan(( total = interval * samples )) 157169689Skanif (( total > 180 )); then 158169689Skan (( total = total / 60 )) 159169689Skan total="$total minutes" 160169689Skanelse 161169689Skan total="$total seconds" 162169689Skanfi 163169689Skan 164169689Skan# 165169689Skan# Common Functions 166169689Skan# 167169689Skanfunction decho { 168169689Skan if (( verbose )); then print "$*"; fi 169169689Skan} 170169689Skanclean="sed /^\$/d" 171169689Skanheader='dtrace:::BEGIN { 172169689Skan printf("%Y, ", walltimestamp); 173169689Skan printf("%s %s %s %s %s, ", `utsname.sysname, `utsname.nodename, 174169689Skan `utsname.release, `utsname.version, `utsname.machine); 175169689Skan printf("%d secs\n",'$interval'); 176169689Skan } 177169689Skan profile:::tick-'$interval'sec { exit(0); } 178169689Skan ' 179169689Skanfunction dstatus { 180169689Skan if (( verbose )); then 181169689Skan (( percent = current * 100 / samples )) 182169689Skan printf "%3d%% $*\n" $percent 183169689Skan (( current = current + 1 )) 184169689Skan fi 185169689Skan} 186169689Skan 187169689Skan######################################## 188169689Skan# START # 189169689Skan######################################## 190169689Skan 191169689Skan# 192169689Skan# Make dirs 193169689Skan# 194169689Skanerr=0 195169689Skancd $root 196169689Skan(( err = err + $? )) 197169689Skanmkdir $dir 198169689Skan(( err = err + $? )) 199169689Skancd $dir 200169689Skan(( err = err + $? )) 201169689Skanbase1=${PWD##*/} 202169689Skanbase2=${dir##*/} 203169689Skanif [[ "$base1" != "$base2" || "$err" != "0" ]]; then 204169689Skan print "ERROR7: tried to mkdir $dir from $root, but something failed.\n" 205169689Skan print "Check directories before rerunning." 206169689Skan exit 7 207169689Skanfi 208169689Skanmkdir Cpu 209169689Skanmkdir Disk 210169689Skanmkdir Mem 211169689Skanmkdir Net 212169689Skanmkdir Proc 213169689Skanmkdir Info 214169689Skan 215169689Skan# 216169689Skan# Create Log 217169689Skan# 218169689Skandecho "Starting dexplorer ver 0.76." 219169689Skandecho "Sample interval is $interval seconds. Total run is > $total." 220169689Skan( print "dexplorer ver 0.76\n------------------" 221169689Skanprint -n "System: " 222169689Skanuname -a 223169689Skanprint -n "Start: " 224169689Skandate ) > log 225169689Skan 226169689Skan# 227169689Skan# Capture Standard Info 228169689Skan# 229169689Skanargs='pid,ppid,uid,gid,projid,zoneid,pset,pri,nice,' 230169689Skanargs=$args'class,vsz,rss,time,pcpu,pmem,args' 231169689Skanuname -a > Info/uname-a # System 232169689Skanpsrinfo -v > Info/psrinfo-v # CPU 233169689Skanprtconf > Info/prtconf # Memory (+ devices) 234169689Skandf -k > Info/df-k # Disk 235169689Skanifconfig -a > Info/ifconfig-a # Network 236169689Skanps -eo $args > Info/ps-o # Processes 23750397Sobrienuptime > Info/uptime # Load 23850397Sobrien 23950397Sobrien# 240169689Skan# Cpu Tests, DTrace 241169689Skan# 242169689Skan 24350397Sobriendstatus "Interrupts by CPU..." 24450397Sobrien$dtrace -qn "$header"' 245132718Skan sdt:::interrupt-start { @num[cpu] = count(); } 246132718Skan dtrace:::END 24750397Sobrien { 24896263Sobrien printf("%-16s %16s\n", "CPU", "INTERRUPTS"); 24996263Sobrien printa("%-16d %@16d\n", @num); 25090075Sobrien } 25190075Sobrien' | $clean > Cpu/interrupt_by_cpu 252169689Skan 253169689Skandstatus "Interrupt times..." 254169689Skan$dtrace -qn "$header"' 255169689Skan sdt:::interrupt-start { self->ts = vtimestamp; } 256169689Skan sdt:::interrupt-complete 257169689Skan /self->ts && arg0 != 0/ 25850397Sobrien { 25950397Sobrien this->devi = (struct dev_info *)arg0; 260169689Skan self->name = this->devi != 0 ? 26150397Sobrien stringof(`devnamesp[this->devi->devi_major].dn_name) : "?"; 26296263Sobrien this->inst = this->devi != 0 ? this->devi->devi_instance : 0; 26396263Sobrien @num[self->name, this->inst] = sum(vtimestamp - self->ts); 26496263Sobrien self->name = 0; 26596263Sobrien } 26650397Sobrien sdt:::interrupt-complete { self->ts = 0; } 26750397Sobrien dtrace:::END 26850397Sobrien { 26950397Sobrien printf("%11s %16s\n", "DEVICE", "TIME (ns)"); 27050397Sobrien printa("%10s%-3d %@16d\n", @num); 27150397Sobrien } 27250397Sobrien' | $clean > Cpu/interrupt_time 27350397Sobrien 27450397Sobriendstatus "Dispatcher queue length by CPU..." 27550397Sobrien$dtrace -qn "$header"' 27650397Sobrien profile:::profile-1000 27750397Sobrien { 27850397Sobrien this->num = curthread->t_cpu->cpu_disp->disp_nrunnable; 27950397Sobrien @length[cpu] = lquantize(this->num, 0, 100, 1); 28050397Sobrien } 28190075Sobrien dtrace:::END { printa(" CPU %d%@d\n", @length); } 28290075Sobrien' | $clean > Cpu/dispqlen_by_cpu 28390075Sobrien 28490075Sobriendstatus "Sdt counts..." 28590075Sobrien$dtrace -qn "$header"' 28690075Sobrien sdt:::{ @num[probefunc, probename] = count(); } 28790075Sobrien dtrace:::END 28890075Sobrien { 28990075Sobrien printf("%-32s %-32s %10s\n", "FUNC", "NAME", "COUNT"); 29090075Sobrien printa("%-32s %-32s %@10d\n", @num); 29190075Sobrien } 29290075Sobrien' | $clean > Cpu/sdt_count 29390075Sobrien 29490075Sobrien# 29590075Sobrien# Disk Tests, DTrace 29690075Sobrien# 29790075Sobrien 29890075Sobriendstatus "Pages paged in by process..." 299132718Skan$dtrace -qn "$header"' 300132718Skan vminfo:::pgpgin { @pg[pid, execname] = sum(arg0); } 301132718Skan dtrace:::END 302132718Skan { 303169689Skan printf("%6s %-16s %16s\n", "PID", "CMD", "PAGES"); 304169689Skan printa("%6d %-16s %@16d\n", @pg); 305169689Skan } 306169689Skan' | $clean > Disk/pgpgin_by_process 307169689Skan 308169689Skandstatus "Files opened successfully count..." 309169689Skan$dtrace -qn "$header"' 310169689Skan syscall::open*:entry { self->file = copyinstr(arg0); self->ok = 1; } 311169689Skan syscall::open*:return /self->ok && arg0 != -1/ 312132718Skan { 313132718Skan @num[self->file] = count(); 314169689Skan } 315169689Skan syscall::open*:return /self->ok/ { self->file = 0; self->ok = 0; } 31650397Sobrien dtrace:::END 317169689Skan { 318169689Skan printf("%-64s %8s\n", "FILE", "COUNT"); 319169689Skan printa("%-64s %@8d\n", @num); 320169689Skan } 321169689Skan' | $clean > Disk/fileopen_count 322132718Skan 32350397Sobriendstatus "Disk I/O size distribution by process..." 324169689Skan$dtrace -qn "$header"' 325169689Skan io:::start { @size[pid, execname] = quantize(args[0]->b_bcount); } 326169689Skan' | $clean > Disk/sizedist_by_process 327169689Skan 328132718Skan# 329132718Skan# Mem Tests, DTrace 330132718Skan# 331132718Skan 33250397Sobriendstatus "Minor faults by process..." 333132718Skan$dtrace -qn "$header"' 334132718Skan vminfo:::as_fault { @mem[pid, execname] = sum(arg0); } 33552284Sobrien dtrace:::END 336132718Skan { 337132718Skan printf("%6s %-16s %16s\n", "PID", "CMD", "MINFAULTS"); 338132718Skan printa("%6d %-16s %@16d\n", @mem); 339169689Skan } 340132718Skan' | $clean > Mem/minf_by_process 341132718Skan 342132718Skan 343169689Skandstatus "Vminfo data by process..." 344169689Skan$dtrace -qn "$header"' 345169689Skan vminfo::: { @data[pid, execname, probename] = sum(arg0); } 346169689Skan dtrace:::END 347169689Skan { 348169689Skan printf("%6s %-16s %-16s %16s\n", 34990075Sobrien "PID", "CMD", "STATISTIC", "VALUE"); 350169689Skan printa("%6d %-16s %-16s %@16d\n", @data); 35190075Sobrien } 35252284Sobrien' | $clean > Mem/vminfo_by_process 353132718Skan 354132718Skan# 355132718Skan# Net Tests, DTrace 356132718Skan# 35796263Sobrien 358132718Skandstatus "Mib data by mib statistic..." 359132718Skan$dtrace -qn "$header"' 360132718Skan mib::: { @data[probename] = sum(arg0); } 361132718Skan dtrace:::END 362132718Skan { 363117395Skan printf("%-32s %16s\n", "STATISTIC", "VALUE"); 364132718Skan printa("%-32s %@16d\n", @data); 365132718Skan } 366169689Skan' | $clean > Net/mib_data 367169689Skan 368169689Skandstatus "TCP write bytes by process..." 369169689Skan$dtrace -qn "$header"' 370169689Skan fbt:ip:tcp_output:entry 371169689Skan { 372132718Skan this->size = msgdsize(args[1]); 373132718Skan @size[pid, execname] = sum(this->size); 374146895Skan } 375146895Skan dtrace:::END 376132718Skan { 377132718Skan printf("%6s %-16s %12s\n", "PID", "CMD", "BYTES"); 378132718Skan printa("%6d %-16s %@12d\n", @size); 379132718Skan } 380132718Skan' | $clean > Net/tcpw_by_process 381132718Skan 382132718Skan# 383169689Skan# Proc Tests, DTrace 384169689Skan# 385169689Skan 386169689Skandstatus "Sample process @ 1000 Hz..." 387169689Skan$dtrace -qn "$header"' 388169689Skan profile:::profile-1000 389169689Skan { 390169689Skan @num[pid, curpsinfo->pr_psargs] = count(); 391169689Skan } 392169689Skan dtrace:::END 393169689Skan { 394169689Skan printf("%6s %12s %s\n", "PID", "SAMPLES", "ARGS"); 395169689Skan printa("%6d %@12d %S\n", @num); 396169689Skan } 397169689Skan' | $clean > Proc/sample_process 398169689Skan 399169689Skandstatus "Syscall count by process..." 400169689Skan$dtrace -qn "$header"' 401169689Skan syscall:::entry { @num[pid, execname, probefunc] = count(); } 40250397Sobrien dtrace:::END 40350397Sobrien { 40450397Sobrien printf("%6s %-24s %-24s %8s\n", 40550397Sobrien "PID", "CMD", "SYSCALL", "COUNT"); 40650397Sobrien printa("%6d %-24s %-24s %@8d\n", @num); 40750397Sobrien } 40890075Sobrien' | $clean > Proc/syscall_by_process 40950397Sobrien 41050397Sobriendstatus "Syscall count by syscall..." 41150397Sobrien$dtrace -qn "$header"' 41250397Sobrien syscall:::entry { @num[probefunc] = count(); } 41350397Sobrien dtrace:::END 41450397Sobrien { 41550397Sobrien printf("%-32s %16s\n", "SYSCALL", "COUNT"); 41650397Sobrien printa("%-32s %@16d\n", @num); 41750397Sobrien } 41850397Sobrien' | $clean > Proc/syscall_count 41950397Sobrien 42050397Sobriendstatus "Read bytes by process..." 421169689Skan$dtrace -qn "$header"' 422169689Skan sysinfo:::readch { @bytes[pid, execname] = sum(arg0); } 423169689Skan dtrace:::END 424169689Skan { 42590075Sobrien printf("%6s %-16s %16s\n", "PID", "CMD", "BYTES"); 42650397Sobrien printa("%6d %-16s %@16d\n", @bytes); 42790075Sobrien } 42890075Sobrien' | $clean > Proc/readb_by_process 42990075Sobrien 43090075Sobriendstatus "Write bytes by process..." 43190075Sobrien$dtrace -qn "$header"' 43290075Sobrien sysinfo:::writech { @bytes[pid, execname] = sum(arg0); } 43390075Sobrien dtrace:::END 43490075Sobrien { 43590075Sobrien printf("%6s %-16s %16s\n", "PID", "CMD", "BYTES"); 43690075Sobrien printa("%6d %-16s %@16d\n", @bytes); 43790075Sobrien } 43890075Sobrien' | $clean > Proc/writeb_by_process 43990075Sobrien 44090075Sobriendstatus "Sysinfo counts by process..." 44190075Sobrien$dtrace -qn "$header"' 44290075Sobrien sysinfo::: { @num[pid, execname, probename] = sum(arg0); } 44390075Sobrien dtrace:::END 44490075Sobrien { 44590075Sobrien printf("%6s %-16s %-16s %16s\n", 44690075Sobrien "PID", "CMD", "STATISTIC", "COUNT"); 44790075Sobrien printa("%6d %-16s %-16s %@16d\n", @num); 448169689Skan } 44990075Sobrien' | $clean > Proc/sysinfo_by_process 450169689Skan 45190075Sobriendstatus "New process counts with arguments..." 45290075Sobrien$dtrace -qn "$header"' 45390075Sobrien proc:::exec-success 45490075Sobrien { 45590075Sobrien @num[pid, ppid, curpsinfo->pr_psargs] = count(); 45690075Sobrien } 45790075Sobrien dtrace:::END 458117395Skan { 459117395Skan printf("%6s %6s %8s %s\n", "PID", "PPID", "COUNT", "ARGS"); 46090075Sobrien printa("%6d %6d %@8d %S\n", @num); 461132718Skan } 462132718Skan' | $clean > Proc/newprocess_count 463117395Skan 464132718Skandstatus "Signal counts..." 465132718Skan$dtrace -qn "$header"' 466169689Skan proc:::signal-send { 467169689Skan @num[execname,args[2],stringof(args[1]->pr_fname)] = count(); 468132718Skan } 469169689Skan dtrace:::END 470169689Skan { 471169689Skan printf("%-16s %-8s %-16s %8s\n", 472169689Skan "FROM", "SIG", "TO", "COUNT"); 473169689Skan printa("%-16s %-8d %-16s %@8d\n", @num); 474169689Skan } 475132718Skan' | $clean > Proc/signal_count 476132718Skan 477132718Skandstatus "Syscall error counts..." 478169689Skan$dtrace -qn "$header"' 479132718Skan syscall:::return /(int)arg0 == -1/ 480132718Skan { 481132718Skan @num[pid, execname, probefunc, errno] = count(); 482117395Skan } 483117395Skan dtrace:::END 484117395Skan { 485146895Skan printf("%6s %-16s %-32s %-6s %8s\n", 486117395Skan "PID", "CMD", "SYSCALL", "ERRNO", "COUNT"); 487132718Skan printa("%6d %-16s %-32s %-6d %@8d\n", @num); 488132718Skan } 489132718Skan' | $clean > Proc/syscall_errors 490132718Skan 491132718Skan 492169689Skan########### 493169689Skan# Done 494169689Skan# 495169689Skan( print -n "End: " 496169689Skandate ) >> log 497169689Skandecho "100% Done." 498169689Skanif (( tar )); then 499169689Skan cd .. 500169689Skan tar cf $dir.tar $dir 501169689Skan gzip $dir.tar 502169689Skan decho "File is $dir.tar.gz" 503169689Skanfi 504169689Skanif (( delete && tar )); then 505169689Skan cd $dir 506169689Skan # this could be all an "rm -r $dir", but since it will be run 507169689Skan # as root on production servers - lets be analy cautious, 508169689Skan rm Cpu/interrupt_by_cpu 509169689Skan rm Cpu/interrupt_time 510169689Skan rm Cpu/dispqlen_by_cpu 511169689Skan rm Cpu/sdt_count 512169689Skan rm Disk/pgpgin_by_process 513169689Skan rm Disk/fileopen_count 514169689Skan rm Disk/sizedist_by_process 515169689Skan rm Mem/minf_by_process 516169689Skan rm Mem/vminfo_by_process 517169689Skan rm Net/mib_data 518169689Skan rm Net/tcpw_by_process 519169689Skan rm Proc/sample_process 520169689Skan rm Proc/syscall_by_process 521169689Skan rm Proc/syscall_count 522169689Skan rm Proc/readb_by_process 523169689Skan rm Proc/writeb_by_process 524169689Skan rm Proc/sysinfo_by_process 525169689Skan rm Proc/newprocess_count 526169689Skan rm Proc/signal_count 527169689Skan rm Proc/syscall_errors 528169689Skan rmdir Cpu 529169689Skan rmdir Disk 530169689Skan rmdir Mem 531169689Skan rmdir Net 532169689Skan rmdir Proc 533169689Skan rm Info/uname-a 534169689Skan rm Info/psrinfo-v 535169689Skan rm Info/prtconf 536169689Skan rm Info/df-k 537169689Skan rm Info/ifconfig-a 538169689Skan rm Info/ps-o 539169689Skan rm Info/uptime 540169689Skan rmdir Info 541169689Skan rm log 542169689Skan cd .. 543169689Skan rmdir $dir 544169689Skanelse 545169689Skan decho "Directory is $dir" 546169689Skanfi 547169689Skan 548169689Skan