func showDiskStat(buffer *bytes.Buffer, prev_rec *ss.StatRecord, cur_rec *ss.StatRecord) error {
	dusage, err := ss.GetDiskUsage(
		prev_rec.Time, prev_rec.Disk,
		cur_rec.Time, cur_rec.Disk)
	if err != nil {
		return err
	}

	buffer.WriteString(`,"disk":`)
	dusage.WriteJsonTo(buffer)

	return nil
}
func main() {
	opt := parseArgs()

	var in *os.File

	f, err := os.Open(opt.PerfmongerFile)
	if err != nil {
		panic(err)
	}
	in = f
	defer f.Close()

	dec := gob.NewDecoder(bufio.NewReader(in))

	var cheader ss.CommonHeader
	var pheader ss.PlatformHeader
	var records = make([]ss.StatRecord, 2)
	curr := 0

	err = dec.Decode(&cheader)
	if err == io.EOF {
		return
	}
	if err != nil {
		panic(err)
	}
	err = dec.Decode(&pheader)
	if err == io.EOF {
		return
	}
	if err != nil {
		panic(err)
	}

	// read first record
	err = dec.Decode(&records[curr])
	if err == io.EOF {
		return
	} else if err != nil {
		panic(err)
	}
	t0 := records[curr].Time
	curr ^= 1

	meta_set := false
	meta := PlotMeta{}
	meta.StartTime = float64(records[0].Time.UnixNano()) / 1.0e9

	disk_dat_files := map[string]*DiskDatTmpFile{}
	cpu_dat_files := make([]*CpuDatTmpFile, records[0].Cpu.NumCore)
	meta.Cpu.NumCore = records[0].Cpu.NumCore

	f, err = os.Create(opt.CpuFile)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	cpu_writer := bufio.NewWriter(f)

	cpu_writer.WriteString("# All cpu usage\n")
	cpu_writer.WriteString("# elapsed_time	%usr	%nice	%sys	%iowait	%hardirq	%softirq	%steal	%guest	%idle\n")

	for {
		prev_rec := &records[curr^1]
		cur_rec := &records[curr]

		err := dec.Decode(cur_rec)
		if err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}

		// Disk usage
		dusage, err := ss.GetDiskUsage(prev_rec.Time, prev_rec.Disk, cur_rec.Time, cur_rec.Disk)
		if err != nil {
			panic(err)
		}
		didx := 0

		var dnames []string
		for dname, _ := range *dusage {
			if dname != "total" {
				dnames = append(dnames, dname)
			}
		}
		sort.Strings(dnames)
		dnames = append(dnames, "total")

		// for dname, dusage_entry := range *dusage {
		for _, dname := range dnames {
			dusage_entry, ok := (*dusage)[dname]
			if !ok {
				panic("device '" + dname + "' not found")
			}

			if !meta_set {
				meta.Disk.Devices =
					append(meta.Disk.Devices,
						DiskMetaEntry{Name: dname, Idx: didx})
			}

			disk_dat, ok := disk_dat_files[dname]
			if !ok {
				disk_dat = makeDiskDatTmpFile(dname, didx)
				disk_dat_files[dname] = disk_dat
				defer disk_dat.File.Close()

				disk_dat.Writer.WriteString("\n\n\n")
				disk_dat.Writer.WriteString("# device: " + disk_dat.Name + "\n")
				disk_dat.Writer.WriteString(fmt.Sprintln(
					"# elapsed_time	r_iops	w_iops	r_MB/s	w_MB/s	r_latency	w_latency	r_avgsz	w_avgsz	qdepth"))
			}

			elapsed_time := prev_rec.Time.Sub(t0).Seconds()
			disk_dat.Writer.WriteString(
				fmt.Sprintf("%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n",
					elapsed_time,
					dusage_entry.RdIops,
					dusage_entry.WrIops,
					dusage_entry.RdSecps*512.0/1024.0/1024.0,
					dusage_entry.WrSecps*512.0/1024.0/1024.0,
					dusage_entry.RdLatency,
					dusage_entry.WrLatency,
					dusage_entry.AvgRdSize,
					dusage_entry.AvgWrSize,
					dusage_entry.ReqQlen))

			didx += 1
		}

		// Cpu usage
		cusage, err := ss.GetCpuUsage(prev_rec.Cpu, cur_rec.Cpu)
		if err != nil {
			panic(err)
		}
		for coreid, coreusage := range cusage.CoreUsages {
			cpu_dat := cpu_dat_files[coreid]
			if cpu_dat == nil {
				cpu_dat = makeCpuDatTmpFile(coreid)
				cpu_dat_files[coreid] = cpu_dat
				defer cpu_dat.File.Close()

				cpu_dat.Writer.WriteString(fmt.Sprintf("\n\n\n# core: %d\n", coreid))
				cpu_dat.Writer.WriteString("# elapsed_time	%usr	%nice	%sys	%iowait	%hardirq	%softirq	%steal	%guest	%idle\n")
			}

			printCoreUsage(cpu_dat.Writer, prev_rec.Time.Sub(t0).Seconds(), coreusage)
		}
		printCoreUsage(cpu_writer, prev_rec.Time.Sub(t0).Seconds(), cusage.All)

		curr ^= 1
		meta_set = true
	}

	meta.EndTime = float64(records[curr^1].Time.UnixNano()) / 1.0e9

	for _, disk_dat := range disk_dat_files {
		disk_dat.Writer.Flush()
		disk_dat.File.Close()
	}

	f, err = os.Create(opt.DiskFile)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	df_writer := bufio.NewWriter(f)

	for _, dev := range meta.Disk.Devices {
		disk_dat, ok := disk_dat_files[dev.Name]
		if !ok {
			panic(dev.Name)
		}

		content, err := ioutil.ReadFile(disk_dat.Path)
		if err != nil {
			panic(err)
		}

		df_writer.Write(content)

		os.Remove(disk_dat.Path)
	}
	df_writer.Flush()

	for _, cpu_dat := range cpu_dat_files {
		cpu_dat.Writer.Flush()
		cpu_dat.File.Close()

		content, err := ioutil.ReadFile(cpu_dat.Path)
		if err != nil {
			panic(err)
		}

		cpu_writer.Write(content)
		os.Remove(cpu_dat.Path)
	}
	cpu_writer.Flush()

	json_enc := json.NewEncoder(os.Stdout)
	json_enc.Encode(meta)
}
func main() {
	var cheader ss.CommonHeader
	var pheader ss.PlatformHeader

	parseArgs()

	f, err := os.Open(option.logfile)
	if err != nil {
		panic(err)
	}
	dec := gob.NewDecoder(f)

	err = dec.Decode(&cheader)
	if err == io.EOF {
		return
	}
	if err != nil {
		panic(err)
	}
	err = dec.Decode(&pheader)
	if err == io.EOF {
		return
	}
	if err != nil {
		panic(err)
	}

	var fst_record ss.StatRecord
	// read first record
	err = dec.Decode(&fst_record)
	if err == io.EOF {
		return
	} else if err != nil {
		panic(err)
	}

	// loop until last line
	var lst_records [2]ss.StatRecord
	idx := 0
	for {
		err = dec.Decode(&lst_records[idx])
		if err == io.EOF {
			idx ^= 1
			break
		} else if err != nil {
			panic(err)
		}

		idx ^= 1
	}

	lst_record := lst_records[idx]

	var cpu_usage *ss.CpuUsage = nil
	var disk_usage *ss.DiskUsage = nil
	var net_usage *ss.NetUsage = nil

	if fst_record.Cpu != nil && lst_record.Cpu != nil {
		cpu_usage, err = ss.GetCpuUsage(fst_record.Cpu, lst_record.Cpu)
	}

	if fst_record.Disk != nil && lst_record.Disk != nil {
		disk_usage, err = ss.GetDiskUsage(
			fst_record.Time, fst_record.Disk,
			lst_record.Time, lst_record.Disk)
	}

	if fst_record.Disk != nil && lst_record.Disk != nil {
		net_usage, err = ss.GetNetUsage(
			fst_record.Time, fst_record.Net,
			lst_record.Time, lst_record.Net)
	}

	interval := lst_record.Time.Sub(fst_record.Time)

	if option.json {
		buf := bytes.NewBuffer([]byte{})

		buf.WriteString(fmt.Sprintf(`{"exectime":%.3f`, interval.Seconds()))
		if cpu_usage != nil {
			buf.WriteString(`,"cpu":`)
			cpu_usage.WriteJsonTo(buf)
		}

		if disk_usage != nil {
			buf.WriteString(`,"disk":`)
			disk_usage.WriteJsonTo(buf)
		}

		if disk_usage != nil {
			buf.WriteString(`,"net":`)
			net_usage.WriteJsonTo(buf)
		}

		buf.WriteByte('}')

		fmt.Println(buf.String())
	} else {
		if option.title == "" {
			fmt.Println("== performance summary ==")
		} else {
			fmt.Printf("== performance summary of '%s' ==\n", option.title)
		}
		fmt.Printf(`
Duration: %.3f sec

`,
			interval.Seconds())
		if cpu_usage != nil {
			fmt.Printf(`* Average CPU usage (MAX: %d %%)
  * Non-idle usage: %.2f %%
       %%usr: %.2f %%
       %%sys: %.2f %%
       %%irq: %.2f %%
      %%soft: %.2f %%
     %%other: %.2f %%
  * Idle usage: %.2f %%
    %%iowait: %.2f %%
      %%idle: %.2f %%

`,
				100*cpu_usage.NumCore,
				100.0*float64(cpu_usage.NumCore)-cpu_usage.All.Idle-cpu_usage.All.Iowait,
				cpu_usage.All.User+cpu_usage.All.Nice,
				cpu_usage.All.Sys,
				cpu_usage.All.Hardirq,
				cpu_usage.All.Softirq,
				cpu_usage.All.Steal,
				cpu_usage.All.Idle+cpu_usage.All.Iowait,
				cpu_usage.All.Iowait, cpu_usage.All.Idle)
		}

		if disk_usage != nil {
			devices := []string{}

			for device, _ := range *disk_usage {
				if device != "total" {
					devices = append(devices, device)
				}
			}
			sort.Strings(devices)
			if len(devices) > 1 {
				devices = append(devices, "total")
			}

			for _, device := range devices {
				e := (*disk_usage)[device]
				fmt.Printf(`* Average DEVICE usage: %s
        read IOPS: %.2f
       write IOPS: %.2f
  read throughput: %.2f MB/s
 write throughput: %.2f MB/s
     read latency: %.1f usec
    write latency: %.1f usec
      read amount: %.2f MB
     write amount: %.2f MB

`,
					device,
					e.RdIops, e.WrIops,
					e.RdSecps*512.0/1024.0/1024.0, e.WrSecps*512.0/1024.0/1024.0,
					e.RdLatency*1000.0, e.WrLatency*1000.0,
					float64(e.RdSectors*512)/1024.0/1024.0,
					float64(e.WrSectors*512)/1024.0/1024.0)
			}
		}
	}
}