| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	"sync/atomic" | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	"github.com/minio/madmin-go" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/disk" | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	ioutilx "github.com/minio/minio/internal/ioutil" | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | //go:generate stringer -type=osMetric -trimprefix=osMetric $GOFILE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type osMetric uint8 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	osMetricRemoveAll osMetric = iota | 
					
						
							|  |  |  | 	osMetricMkdirAll | 
					
						
							| 
									
										
										
										
											2022-07-24 15:43:11 +08:00
										 |  |  | 	osMetricMkdir | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	osMetricRename | 
					
						
							| 
									
										
										
										
											2022-08-02 04:22:43 +08:00
										 |  |  | 	osMetricOpenFileW | 
					
						
							|  |  |  | 	osMetricOpenFileR | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	osMetricOpen | 
					
						
							|  |  |  | 	osMetricOpenFileDirectIO | 
					
						
							|  |  |  | 	osMetricLstat | 
					
						
							|  |  |  | 	osMetricRemove | 
					
						
							|  |  |  | 	osMetricStat | 
					
						
							| 
									
										
										
										
											2021-03-29 23:07:23 +08:00
										 |  |  | 	osMetricAccess | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	osMetricCreate | 
					
						
							|  |  |  | 	osMetricReadDirent | 
					
						
							|  |  |  | 	osMetricFdatasync | 
					
						
							|  |  |  | 	osMetricSync | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	// .... add more
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	osMetricLast | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | var globalOSMetrics osMetrics | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							|  |  |  | 	// Inject metrics.
 | 
					
						
							|  |  |  | 	ioutilx.OsOpenFile = OpenFile | 
					
						
							|  |  |  | 	ioutilx.OpenFileDirectIO = OpenFileDirectIO | 
					
						
							|  |  |  | 	ioutilx.OsOpen = Open | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type osMetrics struct { | 
					
						
							|  |  |  | 	// All fields must be accessed atomically and aligned.
 | 
					
						
							|  |  |  | 	operations [osMetricLast]uint64 | 
					
						
							|  |  |  | 	latency    [osMetricLast]lockedLastMinuteLatency | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // time an os action.
 | 
					
						
							|  |  |  | func (o *osMetrics) time(s osMetric) func() { | 
					
						
							|  |  |  | 	startTime := time.Now() | 
					
						
							|  |  |  | 	return func() { | 
					
						
							|  |  |  | 		duration := time.Since(startTime) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		atomic.AddUint64(&o.operations[s], 1) | 
					
						
							|  |  |  | 		o.latency[s].add(duration) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // incTime will increment time on metric s with a specific duration.
 | 
					
						
							|  |  |  | func (o *osMetrics) incTime(s osMetric, d time.Duration) { | 
					
						
							|  |  |  | 	atomic.AddUint64(&o.operations[s], 1) | 
					
						
							|  |  |  | 	o.latency[s].add(d) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | func osTrace(s osMetric, startTime time.Time, duration time.Duration, path string) madmin.TraceInfo { | 
					
						
							|  |  |  | 	return madmin.TraceInfo{ | 
					
						
							|  |  |  | 		TraceType: madmin.TraceOS, | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 		Time:      startTime, | 
					
						
							|  |  |  | 		NodeName:  globalLocalNodeName, | 
					
						
							| 
									
										
										
										
											2021-03-28 01:07:07 +08:00
										 |  |  | 		FuncName:  "os." + s.String(), | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 		Duration:  duration, | 
					
						
							|  |  |  | 		Path:      path, | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | func updateOSMetrics(s osMetric, paths ...string) func() { | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	if globalTrace.NumSubscribers(madmin.TraceOS) == 0 { | 
					
						
							|  |  |  | 		return globalOSMetrics.time(s) | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	startTime := time.Now() | 
					
						
							|  |  |  | 	return func() { | 
					
						
							|  |  |  | 		duration := time.Since(startTime) | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 		globalOSMetrics.incTime(s, duration) | 
					
						
							| 
									
										
										
										
											2021-03-28 04:19:14 +08:00
										 |  |  | 		globalTrace.Publish(osTrace(s, startTime, duration, strings.Join(paths, " -> "))) | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RemoveAll captures time taken to call the underlying os.RemoveAll
 | 
					
						
							|  |  |  | func RemoveAll(dirPath string) error { | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	defer updateOSMetrics(osMetricRemoveAll, dirPath)() | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | 	return os.RemoveAll(dirPath) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-24 15:43:11 +08:00
										 |  |  | // Mkdir captures time taken to call os.Mkdir
 | 
					
						
							|  |  |  | func Mkdir(dirPath string, mode os.FileMode) error { | 
					
						
							|  |  |  | 	defer updateOSMetrics(osMetricMkdir, dirPath)() | 
					
						
							|  |  |  | 	return os.Mkdir(dirPath, mode) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | // MkdirAll captures time taken to call os.MkdirAll
 | 
					
						
							|  |  |  | func MkdirAll(dirPath string, mode os.FileMode) error { | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	defer updateOSMetrics(osMetricMkdirAll, dirPath)() | 
					
						
							| 
									
										
										
										
											2022-07-24 15:43:11 +08:00
										 |  |  | 	return osMkdirAll(dirPath, mode) | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Rename captures time taken to call os.Rename
 | 
					
						
							|  |  |  | func Rename(src, dst string) error { | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	defer updateOSMetrics(osMetricRename, src, dst)() | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | 	return os.Rename(src, dst) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // OpenFile captures time taken to call os.OpenFile
 | 
					
						
							|  |  |  | func OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) { | 
					
						
							| 
									
										
										
										
											2022-08-02 04:22:43 +08:00
										 |  |  | 	switch flag & writeMode { | 
					
						
							|  |  |  | 	case writeMode: | 
					
						
							|  |  |  | 		defer updateOSMetrics(osMetricOpenFileW, name)() | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		defer updateOSMetrics(osMetricOpenFileR, name)() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | 	return os.OpenFile(name, flag, perm) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-29 23:07:23 +08:00
										 |  |  | // Access captures time taken to call syscall.Access()
 | 
					
						
							|  |  |  | // on windows, plan9 and solaris syscall.Access uses
 | 
					
						
							|  |  |  | // os.Lstat()
 | 
					
						
							|  |  |  | func Access(name string) error { | 
					
						
							|  |  |  | 	defer updateOSMetrics(osMetricAccess, name)() | 
					
						
							|  |  |  | 	return access(name) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | // Open captures time taken to call os.Open
 | 
					
						
							|  |  |  | func Open(name string) (*os.File, error) { | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	defer updateOSMetrics(osMetricOpen, name)() | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | 	return os.Open(name) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // OpenFileDirectIO captures time taken to call disk.OpenFileDirectIO
 | 
					
						
							|  |  |  | func OpenFileDirectIO(name string, flag int, perm os.FileMode) (*os.File, error) { | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	defer updateOSMetrics(osMetricOpenFileDirectIO, name)() | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | 	return disk.OpenFileDirectIO(name, flag, perm) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Lstat captures time taken to call os.Lstat
 | 
					
						
							|  |  |  | func Lstat(name string) (os.FileInfo, error) { | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	defer updateOSMetrics(osMetricLstat, name)() | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | 	return os.Lstat(name) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Remove captures time taken to call os.Remove
 | 
					
						
							|  |  |  | func Remove(deletePath string) error { | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	defer updateOSMetrics(osMetricRemove, deletePath)() | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | 	return os.Remove(deletePath) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stat captures time taken to call os.Stat
 | 
					
						
							|  |  |  | func Stat(name string) (os.FileInfo, error) { | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	defer updateOSMetrics(osMetricStat, name)() | 
					
						
							| 
									
										
										
										
											2021-03-24 05:51:27 +08:00
										 |  |  | 	return os.Stat(name) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Create captures time taken to call os.Create
 | 
					
						
							|  |  |  | func Create(name string) (*os.File, error) { | 
					
						
							|  |  |  | 	defer updateOSMetrics(osMetricCreate, name)() | 
					
						
							|  |  |  | 	return os.Create(name) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Fdatasync captures time taken to call Fdatasync
 | 
					
						
							|  |  |  | func Fdatasync(f *os.File) error { | 
					
						
							|  |  |  | 	fn := "" | 
					
						
							|  |  |  | 	if f != nil { | 
					
						
							|  |  |  | 		fn = f.Name() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer updateOSMetrics(osMetricFdatasync, fn)() | 
					
						
							|  |  |  | 	return disk.Fdatasync(f) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // report returns all os metrics.
 | 
					
						
							|  |  |  | func (o *osMetrics) report() madmin.OSMetrics { | 
					
						
							|  |  |  | 	var m madmin.OSMetrics | 
					
						
							|  |  |  | 	m.CollectedAt = time.Now() | 
					
						
							|  |  |  | 	m.LifeTimeOps = make(map[string]uint64, osMetricLast) | 
					
						
							|  |  |  | 	for i := osMetric(0); i < osMetricLast; i++ { | 
					
						
							|  |  |  | 		if n := atomic.LoadUint64(&o.operations[i]); n > 0 { | 
					
						
							|  |  |  | 			m.LifeTimeOps[i.String()] = n | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(m.LifeTimeOps) == 0 { | 
					
						
							|  |  |  | 		m.LifeTimeOps = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	m.LastMinute.Operations = make(map[string]madmin.TimedAction, osMetricLast) | 
					
						
							|  |  |  | 	for i := osMetric(0); i < osMetricLast; i++ { | 
					
						
							|  |  |  | 		lm := o.latency[i].total() | 
					
						
							|  |  |  | 		if lm.N > 0 { | 
					
						
							|  |  |  | 			m.LastMinute.Operations[i.String()] = lm.asTimedAction() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(m.LastMinute.Operations) == 0 { | 
					
						
							|  |  |  | 		m.LastMinute.Operations = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return m | 
					
						
							|  |  |  | } |